Skip to content

Commit 4c3e916

Browse files
nateboschCommit Queue
authored andcommitted
Allow (e as dynamic).foo() in avoid_dynamic_calls
The `(e as dynamic)` pattern is the intended workaround for intentional dynamic calls. The lint currently allows this for property reads like `(e as dynamic).foo` but disallows this for method invocations like `(e as dynamic).foo()` because the latter falls into a case checking if the `node.function` is the dynamic expression (to handle cases like `(e as dynamic).call()`) but there it is no longer checking the right expression for whether it is a cast. Pull out an `_isExplicitCast` method to check for casts wrapped in parenthesis. Use the explicit cast as a signal to bail earlier when checking method invocations. Change-Id: I77b1c28b38474cea018b61a3d0ebe38ead82378d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/390640 Commit-Queue: Nate Bosch <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]> Auto-Submit: Nate Bosch <[email protected]>
1 parent b39bd0b commit 4c3e916

File tree

3 files changed

+24
-2
lines changed

3 files changed

+24
-2
lines changed

pkg/linter/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
- new _(experimental)_ lint: `omit_obvious_local_variable_types`
99
- new _(experimental)_ lint: `specify_nonobvious_local_variable_types`
1010
- new _(experimental)_ lint: `avoid_futureor_void`
11+
- update `avoid_dynamic_calls` to allow method invocations on a cast to
12+
dynamic.
1113

1214
# 3.5.0
1315

pkg/linter/lib/src/rules/avoid_dynamic_calls.dart

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class _Visitor extends SimpleAstVisitor<void> {
115115
return;
116116
}
117117
}
118+
if (_isExplicitCast(node.realTarget)) return;
118119
var receiverWasDynamic = _reportIfDynamic(node.realTarget);
119120
if (!receiverWasDynamic) {
120121
var target = node.realTarget;
@@ -167,7 +168,7 @@ class _Visitor extends SimpleAstVisitor<void> {
167168

168169
bool _reportIfDynamic(Expression? node) {
169170
if (node == null || node.staticType is! DynamicType) return false;
170-
if (node.unParenthesized is AsExpression) return false;
171+
if (_isExplicitCast(node)) return false;
171172

172173
rule.reportLint(node);
173174
return true;
@@ -176,7 +177,7 @@ class _Visitor extends SimpleAstVisitor<void> {
176177
void _reportIfDynamicOrFunction(Expression node, {DartType? staticType}) {
177178
staticType ??= node.staticType;
178179
if (staticType == null) return;
179-
if (node.unParenthesized is AsExpression) return;
180+
if (_isExplicitCast(node)) return;
180181
if (staticType is DynamicType || staticType.isDartCoreFunction) {
181182
rule.reportLint(node);
182183
}
@@ -195,4 +196,15 @@ class _Visitor extends SimpleAstVisitor<void> {
195196
}
196197
}
197198
}
199+
200+
/// Whether an expression matches a pattern to allow a dynamic call.
201+
///
202+
/// This should only be used to check expressions which have a static type of
203+
/// `dynamic` or `Function`.
204+
///
205+
/// Expressions with a static type of `dynamic` can be used only if they are
206+
/// expliticly a parenthesized cast (which must be to `dynamic` or `Function`
207+
/// to be relevant).
208+
static bool _isExplicitCast(Expression? node) =>
209+
node?.unParenthesized is AsExpression;
198210
}

pkg/linter/test/rules/avoid_dynamic_calls_test.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,14 @@ void f(dynamic a) {
539539
]);
540540
}
541541

542+
test_prefixedIdentifier_dynamicMethodCall_asDynamic() async {
543+
await assertNoDiagnostics(r'''
544+
void f(Object? a) {
545+
(a as dynamic).foo();
546+
}
547+
''');
548+
}
549+
542550
test_prefixedIdentifier_noSuchMethod() async {
543551
await assertNoDiagnostics(r'''
544552
void f(dynamic a, Invocation i) {

0 commit comments

Comments
 (0)