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

Analyzer should detect if improperly typed handler passed to Future.catchError. #35825

Closed
stereotype441 opened this issue Jan 31, 2019 · 13 comments
Assignees
Labels
analyzer-linter Issues with the analyzer's support for the linter package area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug

Comments

@stereotype441
Copy link
Member

Due to the fact that Future.catchError supports two kinds of callbacks, its callback parameter is typed Function, which means it gets very limited type checking from the analyzer.

Since this is a commonly used core method, it would be nice if the analyzer had some special case logic to type check it. I'm not sure whether it should be a hint or a lint (@bwilkerson, @pq, WDYT?)

Related issues: #35812, #34144, #31939, #35545.

@stereotype441 stereotype441 added area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug labels Jan 31, 2019
@stereotype441 stereotype441 added the analyzer-linter Issues with the analyzer's support for the linter package label Feb 1, 2019
@pq
Copy link
Member

pq commented Feb 6, 2019

My inclination is that this should be a hint and agree it would be high value. 👍

@zanderso
Copy link
Member

zanderso commented Mar 5, 2019

Is there any update on this? Thanks!

@pq
Copy link
Member

pq commented Mar 5, 2019

Thanks for the nudge @zanderso.

@bwilkerson: any objection to treating this as a hint?

@srawlins
Copy link
Member

With null safety, the priority on this is raised. See #44386. I'd like to implement it as a Hint. I think there would be zero false positives.

  • The function must accept either one Object param, or one Object and one StackTrace param.
  • The function must return Future<T>.

@lrhn
Copy link
Member

lrhn commented Dec 10, 2020

If this can be extended to other Function-typed error handlers, then that would be nice. Not nearly as critical, though.

Most of the remaining error handlers are void-returning functions, so you'd only be checking that they accept (Object) or (Object, StackTrace), but that's still relevant for some situations. For example stream.handleError((FooException e) { ... }) where you know that the exception will be a FooException, but the function must still accept Object.
Or even stream.handleError((FooException e) => ..., test:(o) => o is FooException).

@bwilkerson
Copy link
Member

Sorry, I missed seeing this earlier. I agree that this should be a hint.

@srawlins
Copy link
Member

If this can be extended to other Function-typed error handlers, then that would be nice. Not nearly as critical, though.

+1

@srawlins
Copy link
Member

Reporting on invalid returns is not as cut and dry as I had thought 😛 . At runtime, the VM and dart2js do not use the static type of any return values; only runtime type. For example:

void main() async {
  var x = await Future<int>.error(7).catchError((_) {
    num y = 1;
    return y;
  });
  print(x);
}

In a null-safe program, with --sound-null-safety, dart prints 1. So the analyzer could report on return y (because num is not assignable to FutureOr<int>), but it would then be reporting on valid code.

Reporting return y might be preferred because it requires the return statement to behave as if it were any other function with a return type of FutureOr<int> (requiring the user to cast with as int or something). Or reporting return y might not be preferable, because the program is still valid.

I'm not sure how I would allow return y and still catch return "". Under null safety, where implicit casts are not allowed, I think we'd always have to report return y.

@lrhn
Copy link
Member

lrhn commented Dec 11, 2020

Correct. We don't check that the function argument is a FutureOr<T> Function(Object) or FutureOr<T> Function(Object, StackTrace), just that the parameters match. Then we check that the actual result value is a FutureOr<T>.
We do that because there is no inference to ensure the return type of a function literal, and it's far too easy to get something else.

If you check the actual return statement types, I think it would be fine to warn in the example above.
It seems like it should be non-breaking change to change the declaration to num y = 1.5;, but it is actually breaking.

@srawlins
Copy link
Member

WDYT, @bwilkerson , should this still be a Hint, or a Lint? I think we can perfectly detect when the type of the expression in a return statement is not kosher, but we cannot detect perfectly what will be a runtime error. But this is not unlike some other static analyses. For example missing_return was always this safety guard that didn't perfectly align with runtime behavior (it was always legal for a function to end without a return statement, in which case null was returned. And it is not always known whether casts will fail at runtime, which is why we've added guards like implicit-casts: false.

@bwilkerson
Copy link
Member

My understanding is that false positives will occur only in cases where the future on which catchError is being invoked returns a type T and there is a return of an expression whose static type is S where S is not assignable to T but where there is a subtype of both that is actually returned at runtime.

My intuition is that such cases, and hence false positives, will be rare, but we might want to gather some data to verify that.

@srawlins
Copy link
Member

srawlins commented Dec 16, 2020

#44480 is a semi-blocking issue. I see code in Flutter engine where a Future<void> has a catchError callback with return;, which is currently illegal for FutureOr<void>. ☹️

I could perhaps make an initial landing of some checks which do not check return statements (though I think that is by far more useful than checking arrow function literals or function-typed expressions), or some checks which do not fire for Future<void> futures...

srawlins added a commit to srawlins/usage that referenced this issue Dec 22, 2020
An upcoming feature to the Dart analyzer [0] will report Future.catchError [1]
`onError` handlers which return values of invalid type. Currently this is not
reported because the `onError` handler function type cannot be accurately
expressed. An `onError` handler for `Future<T>.catchError` must be either
`FutureOr<T> Function(dynamic)` or `FutureOr<T> Function(dynamic, StackTrace)`.
In either case, the return type of the function is `FutureOr<T>`.

This CL corrects `onError` handler(s) to return a value of a valid type (typically `null`).

[0]: dart-lang/sdk#35825
[1]: https://api.dart.dev/dev/2.12.0-149.0.dev/dart-async/Future/catchError.html
srawlins added a commit to srawlins/flutter_svg that referenced this issue Dec 22, 2020
An upcoming feature to the Dart analyzer [0] will report Future.catchError [1]
`onError` handlers which return values of invalid type. Currently this is not
reported because the `onError` handler function type cannot be accurately
expressed. An `onError` handler for `Future<T>.catchError` must be either
`FutureOr<T> Function(dynamic)` or `FutureOr<T> Function(dynamic, StackTrace)`.
In either case, the return type of the function is `FutureOr<T>`.

This CL corrects `onError` handler(s) to return a value of a valid type.

[0]: dart-lang/sdk#35825
[1]: https://api.dart.dev/dev/2.12.0-149.0.dev/dart-async/Future/catchError.html
dnfield pushed a commit to dnfield/flutter_svg that referenced this issue Dec 22, 2020
An upcoming feature to the Dart analyzer [0] will report Future.catchError [1]
`onError` handlers which return values of invalid type. Currently this is not
reported because the `onError` handler function type cannot be accurately
expressed. An `onError` handler for `Future<T>.catchError` must be either
`FutureOr<T> Function(dynamic)` or `FutureOr<T> Function(dynamic, StackTrace)`.
In either case, the return type of the function is `FutureOr<T>`.

This CL corrects `onError` handler(s) to return a value of a valid type.

[0]: dart-lang/sdk#35825
[1]: https://api.dart.dev/dev/2.12.0-149.0.dev/dart-async/Future/catchError.html
dart-bot pushed a commit that referenced this issue Dec 28, 2020
Bug: #35825
Change-Id: I92dda82b0745e47212ffe83b2b82e1d92809e7c6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177060
Reviewed-by: Devon Carew <devoncarew@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
dart-bot pushed a commit that referenced this issue Jan 22, 2021
The type of the `onError` parameter of Future<T>.catchError is just Function,
but the function can either have signature `FutureOr<T> Function(dynamic)` or
`FutureOr<T> Function(dynamic, StackTrace)`. This change adds checks for return
statements in a function literal passed to `onError`, and the return type of a
function-typed expression passed to `onError`.

We still need to check parameter types.

#35825

Change-Id: I3a1d1c444e298c5816fcd1d4bc537f7b87fa3da1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176221
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
dart-bot pushed a commit that referenced this issue Jan 25, 2021
This reverts commit d5ee021.

Reason for revert:
Breaks google3: b/178222419

Original change's description:
> Analyzer: Report on return types of Future.catchError function
>
> The type of the `onError` parameter of Future<T>.catchError is just Function,
> but the function can either have signature `FutureOr<T> Function(dynamic)` or
> `FutureOr<T> Function(dynamic, StackTrace)`. This change adds checks for return
> statements in a function literal passed to `onError`, and the return type of a
> function-typed expression passed to `onError`.
>
> We still need to check parameter types.
>
> #35825
>
> Change-Id: I3a1d1c444e298c5816fcd1d4bc537f7b87fa3da1
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176221
> Commit-Queue: Samuel Rawlins <srawlins@google.com>
> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>

TBR=scheglov@google.com,brianwilkerson@google.com,srawlins@google.com

# Not skipping CQ checks because original CL landed > 1 day ago.

Change-Id: I8ebbaac0a4b44293576809baa2dc2c3fdcf35379
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180822
Commit-Queue: David Morgan <davidmorgan@google.com>
Reviewed-by: David Morgan <davidmorgan@google.com>
dart-bot pushed a commit that referenced this issue Jan 25, 2021
The type of the `onError` parameter of Future<T>.catchError is just Function,
but the function can either have signature `FutureOr<T> Function(dynamic)` or
`FutureOr<T> Function(dynamic, StackTrace)`. This change adds checks for return
statements in a function literal passed to `onError`, and the return type of a
function-typed expression passed to `onError`.

We still need to check parameter types.

#35825

Change-Id: I806d9d30e595fb9d63ae669f3c30c428b60fdf4a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180880
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
@srawlins
Copy link
Member

srawlins commented Jan 28, 2021

I think the primary function-typed APIs from dart:async on which we could also report diagnostics include:

  • Future.then(onError)
  • Future.catchError(...)
  • Stream.handleError(onError)
  • Stream.listen(onError)
  • StreamSubscription.onError(handleError)

dart-bot pushed a commit that referenced this issue Feb 17, 2021
Bug: #35825
Change-Id: I43060e9b0b0a764f14be041c5cbdf1d884d92326
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/181740
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
stuartmorgan pushed a commit to flutter/packages that referenced this issue Oct 31, 2024
An upcoming feature to the Dart analyzer [0] will report Future.catchError [1]
`onError` handlers which return values of invalid type. Currently this is not
reported because the `onError` handler function type cannot be accurately
expressed. An `onError` handler for `Future<T>.catchError` must be either
`FutureOr<T> Function(dynamic)` or `FutureOr<T> Function(dynamic, StackTrace)`.
In either case, the return type of the function is `FutureOr<T>`.

This CL corrects `onError` handler(s) to return a value of a valid type.

[0]: dart-lang/sdk#35825
[1]: https://api.dart.dev/dev/2.12.0-149.0.dev/dart-async/Future/catchError.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
analyzer-linter Issues with the analyzer's support for the linter package area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. P2 A bug or feature request we're likely to work on type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

6 participants