[analyzer] Dot shorthands: Issue 61219 - Recovery for selector chains in LHS of assignment. This CL adds caching of a context type in the particular case that we have a dot shorthand in a property access in the LHS of an assignment. This scenario would previously crash upon seeing that we have no cached context type. It's still not valid code, but at least we're not crashing when we analyze it. Bug: https://github.com/dart-lang/sdk/issues/61219 Change-Id: I0159b90de7a53a8a1120e0ba2c7be5d7758cc17d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/442803 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Kallen Tu <kallentu@google.com>
diff --git a/pkg/analysis_server/test/services/completion/dart/location/dot_shorthand_property_access_test.dart b/pkg/analysis_server/test/services/completion/dart/location/dot_shorthand_property_access_test.dart index 54d72d9..6458081 100644 --- a/pkg/analysis_server/test/services/completion/dart/location/dot_shorthand_property_access_test.dart +++ b/pkg/analysis_server/test/services/completion/dart/location/dot_shorthand_property_access_test.dart
@@ -35,6 +35,55 @@ '''); } + Future<void> test_class_assignment() async { + allowedIdentifiers = {'setter', 'self'}; + await computeSuggestions(''' +class C { + set setter(int value) {} + late C self = this; +} + +void f(C foo) {var foo.^ = C()} +'''); + assertResponse(r''' +suggestions +'''); + } + + Future<void> test_class_assignment_prefix() async { + allowedIdentifiers = {'setter', 'self'}; + await computeSuggestions(''' +class C { + set setter(int value) {} + late C self = this; +} + +void f(C foo) {var foo.s^ = C()} +'''); + assertResponse(r''' +replacement + left: 1 +suggestions +'''); + } + + Future<void> test_class_assignment_prefix_chain() async { + allowedIdentifiers = {'setter', 'self'}; + await computeSuggestions(''' +class C { + set setter(int value) {} + late C self = this; +} + +void f(C foo) {var foo.s^.self = C()} +'''); + assertResponse(r''' +replacement + left: 1 +suggestions +'''); + } + Future<void> test_class_chain() async { allowedIdentifiers = {'getter', 'anotherGetter', 'notStatic'}; await computeSuggestions('''
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index 92340f1..f6431ad 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -1515,6 +1515,13 @@ ); } else if (node is PropertyAccessImpl) { if (node.target case var target?) { + if (isDotShorthand(node)) { + // Recovery. + // It's a compile-time error to use a dot shorthand as the target of a + // write, but to prevent any crashing we provide an unknown context + // type since this shouldn't be valid code. + pushDotShorthandContext(target, operations.unknownType); + } analyzeExpression(target, operations.unknownType); popRewrite(); }
diff --git a/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart b/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart index fb9d8b4..0f28c4d 100644 --- a/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart +++ b/pkg/analyzer/test/src/dart/analysis/resolve_for_completion_test.dart
@@ -152,6 +152,45 @@ result.assertResolvedNodes(['B(super.);']); } + test_dotShorthand_assignment() async { + var result = await _resolveTestCode(r''' +extension on int { + set setter(int value) {} +} + +void f(int foo) {var foo.^ = 0} +'''); + + result.assertResolvedNodes(['void f(int foo) {var foo; . = 0;}']); + } + + test_dotShorthand_assignment_chain_method() async { + var result = await _resolveTestCode(r''' +class C { + set setter(int value) {} + C method() => C(); + late C self = this; +} + +void f(C foo) {var foo.s^.method() = C()} +'''); + + result.assertResolvedNodes(['void f(C foo) {var foo; .s.method() = C();}']); + } + + test_dotShorthand_assignment_chain_propertyAccess() async { + var result = await _resolveTestCode(r''' +class C { + set setter(int value) {} + late C self = this; +} + +void f(C foo) {var foo.s^.self = C()} +'''); + + result.assertResolvedNodes(['void f(C foo) {var foo; .s.self = C();}']); + } + test_extension_methodDeclaration_body() async { var result = await _resolveTestCode(r''' extension E on int {