[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 <kallentu@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Kallen Tu
2025-05-15 11:47:17 -07:00
committed by Commit Queue
parent 701c6113c2
commit f849516bd0
4 changed files with 129 additions and 4 deletions
+8 -4
View File
@@ -1455,10 +1455,14 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
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();
}
@@ -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'''
@@ -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'''
@@ -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 {