From f849516bd005bcf652e077bb770d9bf34b5cdc2a Mon Sep 17 00:00:00 2001 From: Kallen Tu Date: Thu, 15 May 2025 11:47:17 -0700 Subject: [PATCH] [analyzer] Dot shorthands: Avoid crashing with prefix and postfix operators. When we evaluate the `target` for this `IndexExpression`, we don't cache the dot shorthand context which causes the analyzer to crash. Added a context push of the `target` (whose context is `UnknownType`) which allows the analyzer to error out without crashing. Passing co19 tests and unit tests. Bug: https://github.com/dart-lang/sdk/issues/59835 Change-Id: I6a19b3e92ed803a51d0cfd320df886a26a36b33d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/426289 Commit-Queue: Kallen Tu Reviewed-by: Chloe Stefantsova --- pkg/analyzer/lib/src/generated/resolver.dart | 12 ++++-- ...shorthand_constructor_invocation_test.dart | 35 +++++++++++++++ .../dot_shorthand_invocation_test.dart | 43 +++++++++++++++++++ .../dot_shorthand_property_access_test.dart | 43 +++++++++++++++++++ 4 files changed, 129 insertions(+), 4 deletions(-) diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index 89bd309dfae..33e0c43166b 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart @@ -1455,10 +1455,14 @@ class ResolverVisitor extends ThrowingAstVisitor if (node is IndexExpressionImpl) { var target = node.target; if (target != null) { - analyzeExpression( - target, - SharedTypeSchemaView(UnknownInferredType.instance), - ); + if (isDotShorthand(node)) { + // Recovery. + // It's a compile-time error to use postfix or prefix operators with + // dot shorthands. We provide an unknown type since this shouldn't be + // valid code, but we want to prevent any crashes. + pushDotShorthandContext(target, operations.unknownType); + } + analyzeExpression(target, operations.unknownType); popRewrite(); } diff --git a/pkg/analyzer/test/src/dart/resolution/dot_shorthand_constructor_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/dot_shorthand_constructor_invocation_test.dart index 9f021b15900..e74b77864b5 100644 --- a/pkg/analyzer/test/src/dart/resolution/dot_shorthand_constructor_invocation_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/dot_shorthand_constructor_invocation_test.dart @@ -2,6 +2,7 @@ // for details. 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/src/dart/error/syntactic_errors.dart'; import 'package:analyzer/src/error/codes.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; @@ -566,6 +567,40 @@ DotShorthandConstructorInvocation '''); } + test_postfixOperator() async { + await assertErrorsInCode( + r''' +class C {} + +void main() { + C c = .new()++; + print(c); +} +''', + [ + error(CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_INVOCATION, 35, 3), + error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 40, 2), + ], + ); + } + + test_prefixOperator() async { + await assertErrorsInCode( + r''' +class C {} + +void main() { + C c = ++.new(); + print(c); +} +''', + [ + error(CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_INVOCATION, 37, 3), + error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 41, 1), + ], + ); + } + test_requiredParameters_missing() async { await assertErrorsInCode( r''' diff --git a/pkg/analyzer/test/src/dart/resolution/dot_shorthand_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/dot_shorthand_invocation_test.dart index 533a4c76d18..d4f53ef8556 100644 --- a/pkg/analyzer/test/src/dart/resolution/dot_shorthand_invocation_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/dot_shorthand_invocation_test.dart @@ -2,6 +2,7 @@ // for details. 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/src/dart/error/syntactic_errors.dart'; import 'package:analyzer/src/error/codes.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; @@ -619,6 +620,48 @@ DotShorthandInvocation '''); } + test_postfixOperator() async { + await assertErrorsInCode( + r''' +class C { + static C member() => C(1); + int x; + C(this.x); +} + +void main() { + C c = .member()++; + print(c); +} +''', + [ + error(CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_INVOCATION, 87, 6), + error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 95, 2), + ], + ); + } + + test_prefixOperator() async { + await assertErrorsInCode( + r''' +class C { + static C member() => C(1); + int x; + C(this.x); +} + +void main() { + C c = ++.member(); + print(c); +} +''', + [ + error(CompileTimeErrorCode.DOT_SHORTHAND_UNDEFINED_INVOCATION, 89, 6), + error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 96, 1), + ], + ); + } + test_requiredParameters_missing() async { await assertErrorsInCode( r''' diff --git a/pkg/analyzer/test/src/dart/resolution/dot_shorthand_property_access_test.dart b/pkg/analyzer/test/src/dart/resolution/dot_shorthand_property_access_test.dart index ab2ffef63fe..b086744f6d9 100644 --- a/pkg/analyzer/test/src/dart/resolution/dot_shorthand_property_access_test.dart +++ b/pkg/analyzer/test/src/dart/resolution/dot_shorthand_property_access_test.dart @@ -2,6 +2,7 @@ // for details. 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/src/dart/error/syntactic_errors.dart'; import 'package:analyzer/src/error/codes.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; @@ -563,6 +564,48 @@ DotShorthandPropertyAccess '''); } + test_postfixOperator() async { + await assertErrorsInCode( + r''' +class C { + static C get member => C(1); + int x; + C(this.x); +} + +void main() { + C c = .member++; + print(c); +} +''', + [ + error(CompileTimeErrorCode.DOT_SHORTHAND_MISSING_CONTEXT, 88, 7), + error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 95, 2), + ], + ); + } + + test_prefixOperator() async { + await assertErrorsInCode( + r''' +class C { + static C get member => C(1); + int x; + C(this.x); +} + +void main() { + C c = ++.member; + print(c); +} +''', + [ + error(CompileTimeErrorCode.DOT_SHORTHAND_MISSING_CONTEXT, 90, 7), + error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 91, 6), + ], + ); + } + test_tearOff_constructor() async { await assertNoErrorsInCode(r''' class C1 {