forked from flutter/flutter
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[flutter_tools] add custom tool analysis to analyze.dart, lint Future…
….catchError (flutter#140122) Ensure tool code does not use Future.catchError or Future.onError, because it is not statically safe: dart-lang/sdk#51248. This was proposed upstream in dart-lang/linter in dart-lang/linter#4071 and dart-lang/linter#4068, but not accepted.
- Loading branch information
1 parent
2ea5ca0
commit 2dd06d1
Showing
6 changed files
with
139 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Copyright 2014 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
import 'package:analyzer/dart/analysis/results.dart'; | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
import 'package:analyzer/dart/element/type.dart'; | ||
|
||
import '../utils.dart'; | ||
import 'analyze.dart'; | ||
|
||
/// Don't use Future.catchError or Future.onError. | ||
/// | ||
/// See https://github.com/flutter/flutter/pull/130662 for more context. | ||
/// | ||
/// **BAD:** | ||
/// | ||
/// ```dart | ||
/// Future<Object?> doSomething() { | ||
/// return doSomethingAsync().catchError((_) => null); | ||
/// } | ||
/// | ||
/// Future<Object?> doSomethingAsync() { | ||
/// return Future<Object>.error(Exception('error')); | ||
/// } | ||
/// ``` | ||
/// | ||
/// **GOOD:** | ||
/// | ||
/// ```dart | ||
/// Future<Object?> doSomething() { | ||
/// return doSomethingAsync().then( | ||
/// (Object? obj) => obj, | ||
/// onError: (_) => null, | ||
/// ); | ||
/// } | ||
/// | ||
/// Future<Object?> doSomethingAsync() { | ||
/// return Future<Object>.error(Exception('error')); | ||
/// } | ||
/// ``` | ||
class AvoidFutureCatchError extends AnalyzeRule { | ||
final Map<ResolvedUnitResult, List<AstNode>> _errors = <ResolvedUnitResult, List<AstNode>>{}; | ||
|
||
@override | ||
void applyTo(ResolvedUnitResult unit) { | ||
final _Visitor visitor = _Visitor(); | ||
unit.unit.visitChildren(visitor); | ||
if (visitor._offendingNodes.isNotEmpty) { | ||
_errors.putIfAbsent(unit, () => <AstNode>[]).addAll(visitor._offendingNodes); | ||
} | ||
} | ||
|
||
@override | ||
void reportViolations(String workingDirectory) { | ||
if (_errors.isEmpty) { | ||
return; | ||
} | ||
|
||
foundError(<String>[ | ||
for (final MapEntry<ResolvedUnitResult, List<AstNode>> entry in _errors.entries) | ||
for (final AstNode node in entry.value) | ||
'${locationInFile(entry.key, node, workingDirectory)}: ${node.parent}', | ||
'\n${bold}Future.catchError and Future.onError are not type safe--instead use Future.then: https://github.com/dart-lang/sdk/issues/51248$reset', | ||
]); | ||
} | ||
|
||
@override | ||
String toString() => 'Avoid "Future.catchError" and "Future.onError"'; | ||
} | ||
|
||
class _Visitor extends RecursiveAstVisitor<void> { | ||
_Visitor(); | ||
|
||
final List<AstNode> _offendingNodes = <AstNode>[]; | ||
|
||
@override | ||
void visitMethodInvocation(MethodInvocation node) { | ||
if (node.methodName.name != 'onError' && node.methodName.name != 'catchError') { | ||
return; | ||
} | ||
final DartType? targetType = node.realTarget?.staticType; | ||
if (targetType == null || !targetType.isDartAsyncFuture) { | ||
return; | ||
} | ||
_offendingNodes.add(node); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters