Skip to content

Commit 4a206e4

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Analyzer: Report invalid instance access from closure within factory
Fixes #43610 Change-Id: Ie9728d375d01ff8c02831befd5fe04b9f5596e35 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/174020 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 72e8eab commit 4a206e4

File tree

2 files changed

+116
-75
lines changed

2 files changed

+116
-75
lines changed

pkg/analyzer/lib/src/generated/error_verifier.dart

Lines changed: 83 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ class EnclosingExecutableContext {
4848
final ExecutableElement element;
4949
final bool isAsynchronous;
5050
final bool isConstConstructor;
51-
final bool isFactoryConstructor;
5251
final bool isGenerativeConstructor;
5352
final bool isGenerator;
53+
final bool inFactoryConstructor;
5454
final bool inStaticMethod;
5555

5656
/// The return statements that have a value.
@@ -66,11 +66,10 @@ class EnclosingExecutableContext {
6666
EnclosingExecutableContext(this.element)
6767
: isAsynchronous = element != null && element.isAsynchronous,
6868
isConstConstructor = element is ConstructorElement && element.isConst,
69-
isFactoryConstructor =
70-
element is ConstructorElement && element.isFactory,
7169
isGenerativeConstructor =
7270
element is ConstructorElement && !element.isFactory,
7371
isGenerator = element != null && element.isGenerator,
72+
inFactoryConstructor = _inFactoryConstructor(element),
7473
inStaticMethod = _inStaticMethod(element);
7574

7675
EnclosingExecutableContext.empty() : this(null);
@@ -84,7 +83,7 @@ class EnclosingExecutableContext {
8483
? className
8584
: '$className.$constructorName';
8685
} else {
87-
return element.displayName;
86+
return element?.displayName;
8887
}
8988
}
9089

@@ -107,6 +106,17 @@ class EnclosingExecutableContext {
107106

108107
DartType get returnType => element.returnType;
109108

109+
static bool _inFactoryConstructor(ExecutableElement element) {
110+
if (element is ConstructorElement) {
111+
return element.isFactory;
112+
}
113+
var enclosing = element?.enclosingElement;
114+
if (enclosing is ExecutableElement) {
115+
return _inFactoryConstructor(enclosing);
116+
}
117+
return false;
118+
}
119+
110120
static bool _inStaticMethod(ExecutableElement element) {
111121
var enclosing = element?.enclosingElement;
112122
if (enclosing is ClassElement || enclosing is ExtensionElement) {
@@ -1129,7 +1139,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
11291139
void visitSimpleIdentifier(SimpleIdentifier node) {
11301140
_checkForAmbiguousImport(node);
11311141
_checkForReferenceBeforeDeclaration(node);
1132-
_checkForImplicitThisReferenceInInitializer(node);
1142+
_checkForInvalidInstanceMemberAccess(node);
11331143
_checkForTypeParameterReferencedByStatic(node);
11341144
if (!_isUnqualifiedReferenceToNonLocalStaticMemberAllowed(node)) {
11351145
_checkForUnqualifiedReferenceToNonLocalStaticMember(node);
@@ -2773,76 +2783,6 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
27732783
}
27742784
}
27752785

2776-
/// Verify that if the given [identifier] is part of a constructor
2777-
/// initializer, then it does not implicitly reference 'this' expression.
2778-
///
2779-
/// See [CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER], and
2780-
/// [CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC].
2781-
/// TODO(scheglov) rename thid method
2782-
void _checkForImplicitThisReferenceInInitializer(
2783-
SimpleIdentifier identifier) {
2784-
if (_isInComment) {
2785-
return;
2786-
}
2787-
if (!_isInConstructorInitializer &&
2788-
!_enclosingExecutable.inStaticMethod &&
2789-
!_enclosingExecutable.isFactoryConstructor &&
2790-
!_isInInstanceNotLateVariableDeclaration &&
2791-
!_isInStaticVariableDeclaration) {
2792-
return;
2793-
}
2794-
// prepare element
2795-
Element element = identifier.writeOrReadElement;
2796-
if (!(element is MethodElement || element is PropertyAccessorElement)) {
2797-
return;
2798-
}
2799-
// static element
2800-
ExecutableElement executableElement = element as ExecutableElement;
2801-
if (executableElement.isStatic) {
2802-
return;
2803-
}
2804-
// not a class member
2805-
Element enclosingElement = element.enclosingElement;
2806-
if (enclosingElement is! ClassElement &&
2807-
enclosingElement is! ExtensionElement) {
2808-
return;
2809-
}
2810-
// qualified method invocation
2811-
AstNode parent = identifier.parent;
2812-
if (parent is MethodInvocation) {
2813-
if (identical(parent.methodName, identifier) &&
2814-
parent.realTarget != null) {
2815-
return;
2816-
}
2817-
}
2818-
// qualified property access
2819-
if (parent is PropertyAccess) {
2820-
if (identical(parent.propertyName, identifier) &&
2821-
parent.realTarget != null) {
2822-
return;
2823-
}
2824-
}
2825-
if (parent is PrefixedIdentifier) {
2826-
if (identical(parent.identifier, identifier)) {
2827-
return;
2828-
}
2829-
}
2830-
2831-
if (_enclosingExecutable.inStaticMethod) {
2832-
// TODO
2833-
_errorReporter.reportErrorForNode(
2834-
CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, identifier);
2835-
} else if (_enclosingExecutable.isFactoryConstructor) {
2836-
_errorReporter.reportErrorForNode(
2837-
CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, identifier);
2838-
} else {
2839-
_errorReporter.reportErrorForNode(
2840-
CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER,
2841-
identifier,
2842-
[identifier.name]);
2843-
}
2844-
}
2845-
28462786
/// Check that if the visiting library is not system, then any given library
28472787
/// should not be SDK internal library. The [importElement] is the
28482788
/// [ImportElement] retrieved from the node, if the element in the node was
@@ -3011,6 +2951,74 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
30112951
}
30122952
}
30132953

2954+
/// Verify that if the given [identifier] is part of a constructor
2955+
/// initializer, then it does not implicitly reference 'this' expression.
2956+
///
2957+
/// See [CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER],
2958+
/// [CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY], and
2959+
/// [CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC].
2960+
void _checkForInvalidInstanceMemberAccess(SimpleIdentifier identifier) {
2961+
if (_isInComment) {
2962+
return;
2963+
}
2964+
if (!_isInConstructorInitializer &&
2965+
!_enclosingExecutable.inStaticMethod &&
2966+
!_enclosingExecutable.inFactoryConstructor &&
2967+
!_isInInstanceNotLateVariableDeclaration &&
2968+
!_isInStaticVariableDeclaration) {
2969+
return;
2970+
}
2971+
// prepare element
2972+
Element element = identifier.writeOrReadElement;
2973+
if (!(element is MethodElement || element is PropertyAccessorElement)) {
2974+
return;
2975+
}
2976+
// static element
2977+
ExecutableElement executableElement = element as ExecutableElement;
2978+
if (executableElement.isStatic) {
2979+
return;
2980+
}
2981+
// not a class member
2982+
Element enclosingElement = element.enclosingElement;
2983+
if (enclosingElement is! ClassElement &&
2984+
enclosingElement is! ExtensionElement) {
2985+
return;
2986+
}
2987+
// qualified method invocation
2988+
AstNode parent = identifier.parent;
2989+
if (parent is MethodInvocation) {
2990+
if (identical(parent.methodName, identifier) &&
2991+
parent.realTarget != null) {
2992+
return;
2993+
}
2994+
}
2995+
// qualified property access
2996+
if (parent is PropertyAccess) {
2997+
if (identical(parent.propertyName, identifier) &&
2998+
parent.realTarget != null) {
2999+
return;
3000+
}
3001+
}
3002+
if (parent is PrefixedIdentifier) {
3003+
if (identical(parent.identifier, identifier)) {
3004+
return;
3005+
}
3006+
}
3007+
3008+
if (_enclosingExecutable.inStaticMethod) {
3009+
_errorReporter.reportErrorForNode(
3010+
CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC, identifier);
3011+
} else if (_enclosingExecutable.inFactoryConstructor) {
3012+
_errorReporter.reportErrorForNode(
3013+
CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, identifier);
3014+
} else {
3015+
_errorReporter.reportErrorForNode(
3016+
CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER,
3017+
identifier,
3018+
[identifier.name]);
3019+
}
3020+
}
3021+
30143022
/// Check to see whether the given function [body] has a modifier associated
30153023
/// with it, and report it as an error if it does.
30163024
void _checkForInvalidModifierOnBody(

pkg/analyzer/test/src/diagnostics/instance_member_access_from_factory_test.dart

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,39 @@ class A {
3030
]);
3131
}
3232

33+
test_property() async {
34+
await assertErrorsInCode(r'''
35+
class A {
36+
int m;
37+
A();
38+
factory A.make() {
39+
m;
40+
return new A();
41+
}
42+
}
43+
''', [
44+
error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 51, 1),
45+
]);
46+
}
47+
48+
test_property_fromClosure() async {
49+
await assertErrorsInCode(r'''
50+
class A {
51+
int m;
52+
A();
53+
factory A.make() {
54+
void f() {
55+
m;
56+
}
57+
f();
58+
return new A();
59+
}
60+
}
61+
''', [
62+
error(CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_FACTORY, 68, 1),
63+
]);
64+
}
65+
3366
test_unnamed() async {
3467
await assertErrorsInCode(r'''
3568
class A {

0 commit comments

Comments
 (0)