Track if location has access to this.
We will need this also to implement '16.35 Lexical Lookup' from the spec.
Change-Id: I3b77c4a016fd54771b07957d522074aeb8dd1938
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/158802
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 1576546..512d356 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -39,6 +39,7 @@
import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
import 'package:analyzer/src/generated/sdk.dart' show DartSdk, SdkLibrary;
+import 'package:analyzer/src/generated/this_access_tracker.dart';
import 'package:analyzer/src/task/strong/checker.dart';
import 'package:meta/meta.dart';
@@ -194,6 +195,9 @@
/// in the scope of an extension.
ExtensionElement _enclosingExtension;
+ /// The helper for tracking if the current location has access to `this`.
+ final ThisAccessTracker _thisAccessTracker = ThisAccessTracker.unit();
+
/// The context of the method or function that we are currently visiting, or
/// `null` if we are not inside a method or function.
EnclosingExecutableContext _enclosingExecutable =
@@ -366,6 +370,16 @@
}
@override
+ void visitBlockFunctionBody(BlockFunctionBody node) {
+ _thisAccessTracker.enterFunctionBody(node);
+ try {
+ super.visitBlockFunctionBody(node);
+ } finally {
+ _thisAccessTracker.exitFunctionBody(node);
+ }
+ }
+
+ @override
void visitBreakStatement(BreakStatement node) {
SimpleIdentifier labelNode = node.label;
if (labelNode != null) {
@@ -546,8 +560,13 @@
@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {
- _returnTypeVerifier.verifyExpressionFunctionBody(node);
- super.visitExpressionFunctionBody(node);
+ _thisAccessTracker.enterFunctionBody(node);
+ try {
+ _returnTypeVerifier.verifyExpressionFunctionBody(node);
+ super.visitExpressionFunctionBody(node);
+ } finally {
+ _thisAccessTracker.exitFunctionBody(node);
+ }
}
@override
@@ -574,6 +593,7 @@
@override
void visitFieldDeclaration(FieldDeclaration node) {
var fields = node.fields;
+ _thisAccessTracker.enterFieldDeclaration(node);
_isInStaticVariableDeclaration = node.isStatic;
_isInInstanceNotLateVariableDeclaration =
!node.isStatic && !node.fields.isLate;
@@ -591,6 +611,7 @@
} finally {
_isInStaticVariableDeclaration = false;
_isInInstanceNotLateVariableDeclaration = false;
+ _thisAccessTracker.exitFieldDeclaration(node);
}
}
@@ -2976,7 +2997,7 @@
///
/// See [CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS].
void _checkForInvalidReferenceToThis(ThisExpression expression) {
- if (!_isThisInValidContext(expression)) {
+ if (!_thisAccessTracker.hasAccess) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, expression);
}
@@ -5161,29 +5182,6 @@
return false;
}
- /// Return `true` if the given 'this' [expression] is in a valid context.
- bool _isThisInValidContext(ThisExpression expression) {
- for (AstNode node = expression.parent; node != null; node = node.parent) {
- if (node is CompilationUnit) {
- return false;
- } else if (node is ConstructorDeclaration) {
- return node.factoryKeyword == null;
- } else if (node is ConstructorInitializer) {
- return false;
- } else if (node is MethodDeclaration) {
- return !node.isStatic;
- } else if (node is FieldDeclaration) {
- if (node.fields.isLate &&
- (node.parent is ClassDeclaration ||
- node.parent is MixinDeclaration)) {
- return true;
- }
- // Continue; a non-late variable may still occur in a valid context.
- }
- }
- return false;
- }
-
/// Return `true` if the given [identifier] is in a location where it is
/// allowed to resolve to a static member of a supertype.
bool _isUnqualifiedReferenceToNonLocalStaticMemberAllowed(
diff --git a/pkg/analyzer/lib/src/generated/this_access_tracker.dart b/pkg/analyzer/lib/src/generated/this_access_tracker.dart
new file mode 100644
index 0000000..7fdb8d3
--- /dev/null
+++ b/pkg/analyzer/lib/src/generated/this_access_tracker.dart
@@ -0,0 +1,45 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// 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/dart/ast/ast.dart';
+
+/// Tracks if the current location has access to `this`.
+///
+/// The current instance (and hence its members) can only be accessed at
+/// specific locations in a class: We say that a location `l` has access to
+/// `this` iff `l` is inside the body of a declaration of an instance member,
+/// or a generative constructor, or in the initializing expression of a `late`
+/// instance variable declaration.
+class ThisAccessTracker {
+ final List<bool> _stack = [];
+
+ ThisAccessTracker.unit() {
+ _stack.add(false);
+ }
+
+ bool get hasAccess => _stack.last;
+
+ void enterFieldDeclaration(FieldDeclaration node) {
+ _stack.add(!node.isStatic && node.fields.isLate);
+ }
+
+ void enterFunctionBody(FunctionBody node) {
+ var parent = node.parent;
+ if (parent is ConstructorDeclaration) {
+ _stack.add(parent.factoryKeyword == null);
+ } else if (parent is MethodDeclaration) {
+ _stack.add(!parent.isStatic);
+ } else {
+ _stack.add(_stack.last);
+ }
+ }
+
+ void exitFieldDeclaration(FieldDeclaration node) {
+ _stack.removeLast();
+ }
+
+ void exitFunctionBody(FunctionBody node) {
+ _stack.removeLast();
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
index 48725a2..d565790 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_reference_to_this_test.dart
@@ -16,7 +16,7 @@
@reflectiveTest
class InvalidReferenceToThisTest extends PubPackageResolutionTest {
- test_constructor_valid() async {
+ test_class_constructor() async {
await assertErrorsInCode(r'''
class A {
A() {
@@ -28,7 +28,7 @@
]);
}
- test_factoryConstructor() async {
+ test_class_factoryConstructor() async {
await assertErrorsInCode(r'''
class A {
factory A() { return this; }
@@ -38,19 +38,17 @@
]);
}
- test_instanceMethod_valid() async {
- await assertErrorsInCode(r'''
+ test_class_instanceMethod() async {
+ await assertNoErrorsInCode(r'''
class A {
- m() {
- var v = this;
+ void foo() {
+ this;
}
}
-''', [
- error(HintCode.UNUSED_LOCAL_VARIABLE, 26, 1),
- ]);
+''');
}
- test_instanceVariableInitializer_inConstructor() async {
+ test_class_instanceVariableInitializer_inConstructor() async {
await assertErrorsInCode(r'''
class A {
var f;
@@ -61,7 +59,7 @@
]);
}
- test_instanceVariableInitializer_inDeclaration() async {
+ test_class_instanceVariableInitializer_inDeclaration() async {
await assertErrorsInCode(r'''
class A {
var f = this;
@@ -71,7 +69,7 @@
]);
}
- test_staticMethod() async {
+ test_class_staticMethod() async {
await assertErrorsInCode(r'''
class A {
static m() { return this; }
@@ -81,7 +79,7 @@
]);
}
- test_staticVariableInitializer() async {
+ test_class_staticVariableInitializer() async {
await assertErrorsInCode(r'''
class A {
static A f = this;
@@ -91,7 +89,7 @@
]);
}
- test_superInitializer() async {
+ test_class_superInitializer() async {
await assertErrorsInCode(r'''
class A {
A(var x) {}
@@ -112,25 +110,13 @@
]);
}
- test_variableInitializer() async {
+ test_topLevelVariable() async {
await assertErrorsInCode('''
int x = this;
''', [
error(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS, 8, 4),
]);
}
-
- test_variableInitializer_inMethod_notLate() async {
- await assertErrorsInCode(r'''
-class A {
- f() {
- var r = this;
- }
-}
-''', [
- error(HintCode.UNUSED_LOCAL_VARIABLE, 26, 1),
- ]);
- }
}
@reflectiveTest