Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unnecessary_lambdas constructor tear-off false postitives #4914

Closed
MohiuddinM opened this issue Mar 17, 2024 · 4 comments
Closed

unnecessary_lambdas constructor tear-off false postitives #4914

MohiuddinM opened this issue Mar 17, 2024 · 4 comments
Assignees
Labels
false-positive P3 A lower priority bug or feature request set-none Does not affect any rules in a defined rule set (e.g., core, recommended, flutter) type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@MohiuddinM
Copy link

class A {
  A(int i);
}

void main() {
  // these are ok (but dart lint encourages tear-off)
  [].map((e) => A(e));
  [].map((e) => A.new(e));
  
  // this is an error: The argument type 'A Function(int)' can't be assigned to the parameter type 'dynamic Function(dynamic)'.
  [].map(A.new);
}
@lrhn
Copy link
Member

lrhn commented Mar 17, 2024

This is correct behavior, so the issue is with recommending using the tear-offs.

The error is correct. The torn off constructor cannot take any value as argument, so it cannot be used as a function with dynamic as parameter type.

Wrapping in (e) => …(e) seems like it should do nothing, but due to type inference, the type inferred for e is actually dynamic, what is then implicitly downcast where is used.
If you had written it as (dynamic e) => A(e as int), it would be clearer that it isn't just forwarding the argument to the constructor, but it's a different function, with different typing, than the tear-off.

So the analyzer may want to check that the parameter type and argument expression types are actually compatible with the tear-off before recommending it.

@pq
Copy link
Member

pq commented Mar 18, 2024

Good catch!

It looks like unnecessary_lambdas is wrongly linting these cases:

  [].map((e) => A(e));
  [].map((e) => A.new(e));

and that we should fix these false positives in the linter.

Thanks for the report @MohiuddinM!

@pq pq transferred this issue from dart-lang/sdk Mar 18, 2024
@pq pq changed the title Analyzer error when doing constructor tear-off unnecessary_lambdas constructor tear-off false postitives Mar 18, 2024
@pq pq added false-positive P3 A lower priority bug or feature request set-none Does not affect any rules in a defined rule set (e.g., core, recommended, flutter) labels Mar 18, 2024
@eernstg
Copy link
Member

eernstg commented Mar 19, 2024

we should fix these false positives in the linter.

Interestingly, we have several design forces pushing each other around here.

unnecessary_lambdas might detect that the eta expansion has an effect because (as @lrhn mentioned) the bare A.new and (x) => A.new(x) have different signatures. So it's not unnecessary, and shouldn't be reported.

However, some other lint or option (perhaps avoid_dynamic_calls) should then be able to report that the invocation of A.new(x) in the body of the function literal may incur a run-time type error.

That should help nudging developers in the direction of the more concise and statically typed variant A.new.

Still, [] would be inferred to mean <dynamic>[] because it's a receiver (and hence there's no context type). So A.new will still give rise to a compile-time error. This may not be very helpful, but it will at least give the developer a heads-up that something about the typing needs to be refined.

For the future, if and when we get dart-lang/language#3527, we will also be able to infer [] as <int>[].

@srawlins srawlins added the type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) label Mar 29, 2024
@srawlins srawlins self-assigned this Apr 28, 2024
copybara-service bot pushed a commit to dart-lang/sdk that referenced this issue Apr 29, 2024
Basically, in `_visitInstanceCreation`, we make sure that, given an
expression like `(e) => C(e))`, that the function type of the outer
function expression is the same as the function type of `C(e)`.

And little cleanups:

* _extractElementsOfSimpleIdentifiers's returned Iterable is only used
  to check whether it contains something, so change it to a Set.
* In `isFinalNode`, use `unParenthesized` to unwrap parens, rather
  calling recursively.
* In `_Visitor`, we stored a `LinterContext`, but we only need the
  TypeSystem object, so just store that for more terse code later.
* In `_visitInstanceCreation`, remove the local function, `matches`,
  by checking whether all arguments are SimpleIdentifiers.
* Convert `isFinalElement` to an extension getter on `Expression`.
* Remove helper `isTearoffAssignable`. It is more concise and I think
  easier to read to just use TypeSystem.isSubtypeOf on its own.
* When looking at a VariableDeclaration, get the type from the
  declared element, which will include inference, rather than the
  literal type annotation on the declaration.

Fixes dart-lang/linter#4914
Fixes dart-lang/linter#3516

Change-Id: I3e89ee4dc011473511d375b0ad324988232c8c96
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/364628
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
@srawlins
Copy link
Member

Fixed with dart-lang/sdk@293469e

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
false-positive P3 A lower priority bug or feature request set-none Does not affect any rules in a defined rule set (e.g., core, recommended, flutter) type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

5 participants