Fix analyzer-CFE integration of illegal references to instance members from static contexts.
In particular, we want to make sure the element is resolved, in case the user wants to navigate to it to make it static. Change-Id: I0707145281858fbf084c6b2af32bb48bfed457f9 Reviewed-on: https://dart-review.googlesource.com/66820 Commit-Queue: Paul Berry <paulberry@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
d5b5736a47
commit
1a5f94a0a4
@@ -30,6 +30,12 @@ class AnalysisDriverResolutionTest_Kernel extends AnalysisDriverResolutionTest {
|
||||
return super.test_closure_generic();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
test_illegal_call_to_static_member() {
|
||||
return super.test_illegal_call_to_static_member();
|
||||
}
|
||||
|
||||
@override
|
||||
@failingTest
|
||||
test_invalid_annotation_on_variable_declaration_for() async {
|
||||
|
||||
@@ -964,6 +964,23 @@ main() {
|
||||
expect(xReference.staticType.toString(), useCFE ? 'dynamic' : 'int');
|
||||
}
|
||||
|
||||
test_assignment_to_illegal_static_access() async {
|
||||
addTestFile('''
|
||||
class C {
|
||||
int x;
|
||||
static f(int y) {
|
||||
x = y;
|
||||
}
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
expect(result.errors, isNotEmpty);
|
||||
var xElement = findElement.field('x');
|
||||
assertElement(findNode.simple('x ='), xElement.setter);
|
||||
var yElement = findElement.parameter('y');
|
||||
assertElement(findNode.simple('y;'), yElement);
|
||||
}
|
||||
|
||||
test_assignment_to_non_generator() async {
|
||||
addTestFile('''
|
||||
f() {}
|
||||
@@ -2081,6 +2098,23 @@ var v = (() => 42)();
|
||||
expect(closureElement.enclosingElement, same(variableInitializer));
|
||||
}
|
||||
|
||||
test_compound_assignment_to_illegal_static_access() async {
|
||||
addTestFile('''
|
||||
class C {
|
||||
int x;
|
||||
static f(int y) {
|
||||
x += y;
|
||||
}
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
expect(result.errors, isNotEmpty);
|
||||
var xElement = findElement.field('x');
|
||||
assertElement(findNode.simple('x +='), xElement.setter);
|
||||
var yElement = findElement.parameter('y');
|
||||
assertElement(findNode.simple('y;'), yElement);
|
||||
}
|
||||
|
||||
test_compound_assignment_to_postfix_increment() async {
|
||||
addTestFile('''
|
||||
f(int x, int y) {
|
||||
@@ -2860,6 +2894,23 @@ main() {
|
||||
assertElement(tRef, functionType.typeFormals[0]);
|
||||
}
|
||||
|
||||
test_illegal_call_to_static_member() async {
|
||||
addTestFile('''
|
||||
class C {
|
||||
f() {}
|
||||
static g() {
|
||||
f();
|
||||
}
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
expect(result.errors, isNotEmpty);
|
||||
|
||||
var fElement = findElement.method('f');
|
||||
var fReference = findNode.simple('f();');
|
||||
assertElement(fReference, fElement);
|
||||
}
|
||||
|
||||
test_indexExpression() async {
|
||||
String content = r'''
|
||||
main() {
|
||||
@@ -6519,6 +6570,23 @@ void f<T, U>(T a, U b) {}
|
||||
}
|
||||
}
|
||||
|
||||
test_null_aware_assignment_to_illegal_static_access() async {
|
||||
addTestFile('''
|
||||
class C {
|
||||
int x;
|
||||
static f(int y) {
|
||||
x ??= y;
|
||||
}
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
expect(result.errors, isNotEmpty);
|
||||
var xElement = findElement.field('x');
|
||||
assertElement(findNode.simple('x ??='), xElement.setter);
|
||||
var yElement = findElement.parameter('y');
|
||||
assertElement(findNode.simple('y;'), yElement);
|
||||
}
|
||||
|
||||
test_null_aware_assignment_to_postfix_increment() async {
|
||||
addTestFile('''
|
||||
f(int x, int y) {
|
||||
@@ -6546,6 +6614,21 @@ f(int y) {
|
||||
assertElement(findNode.simple('y;'), yElement);
|
||||
}
|
||||
|
||||
test_postfix_increment_of_illegal_static_access() async {
|
||||
addTestFile('''
|
||||
class C {
|
||||
int x;
|
||||
static f() {
|
||||
x++;
|
||||
}
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
expect(result.errors, isNotEmpty);
|
||||
var xElement = findElement.field('x');
|
||||
assertElement(findNode.simple('x++'), xElement.setter);
|
||||
}
|
||||
|
||||
test_postfix_increment_of_non_generator() async {
|
||||
addTestFile('''
|
||||
f() {}
|
||||
@@ -6643,6 +6726,21 @@ class C {
|
||||
}
|
||||
}
|
||||
|
||||
test_prefix_increment_of_illegal_static_access() async {
|
||||
addTestFile('''
|
||||
class C {
|
||||
int x;
|
||||
static f() {
|
||||
++x; // usage
|
||||
}
|
||||
}
|
||||
''');
|
||||
await resolveTestFile();
|
||||
expect(result.errors, isNotEmpty);
|
||||
var xElement = findElement.field('x');
|
||||
assertElement(findNode.simple('x; // usage'), xElement.setter);
|
||||
}
|
||||
|
||||
test_prefix_increment_of_non_generator() async {
|
||||
addTestFile('''
|
||||
f() {}
|
||||
@@ -9755,13 +9853,24 @@ class FindElement {
|
||||
|
||||
ParameterElement parameter(String name) {
|
||||
ParameterElement parameterElement;
|
||||
void considerParameter(ParameterElement parameter) {
|
||||
if (parameter.name == name) {
|
||||
if (parameterElement != null) {
|
||||
throw new StateError('Parameter name $name is not unique.');
|
||||
}
|
||||
parameterElement = parameter;
|
||||
}
|
||||
}
|
||||
|
||||
for (var function in unitElement.functions) {
|
||||
for (var parameter in function.parameters) {
|
||||
if (parameter.name == name) {
|
||||
if (parameterElement != null) {
|
||||
throw new StateError('Parameter name $name is not unique.');
|
||||
}
|
||||
parameterElement = parameter;
|
||||
considerParameter(parameter);
|
||||
}
|
||||
}
|
||||
for (var class_ in unitElement.types) {
|
||||
for (var method in class_.methods) {
|
||||
for (var parameter in method.parameters) {
|
||||
considerParameter(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ import 'expression_generator.dart'
|
||||
DelayedAssignment,
|
||||
DelayedPostfixIncrement,
|
||||
Generator,
|
||||
IllegalThisPropertyAccessGenerator,
|
||||
IncompleteErrorGenerator,
|
||||
IncompletePropertyAccessGenerator,
|
||||
IncompleteSendGenerator,
|
||||
@@ -1518,11 +1519,9 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
|
||||
return new IncompleteErrorGenerator(this, token, declaration.target,
|
||||
fasta.templateThisAccessInFieldInitializer.withArguments(name));
|
||||
}
|
||||
if (declaration == null ||
|
||||
(!isInstanceContext && declaration.isInstanceMember)) {
|
||||
if (declaration == null) {
|
||||
Name n = new Name(name, library.library);
|
||||
if (!isQualified && isInstanceContext) {
|
||||
assert(declaration == null);
|
||||
if (constantContext != ConstantContext.none || member.isField) {
|
||||
return new UnresolvedNameGenerator(this, token, n);
|
||||
}
|
||||
@@ -1588,7 +1587,12 @@ abstract class BodyBuilder extends ScopeListener<JumpTarget>
|
||||
getter = declaration.target;
|
||||
setter = lookupInstanceMember(n, isSetter: true);
|
||||
}
|
||||
return new ThisPropertyAccessGenerator(this, token, n, getter, setter);
|
||||
if (isInstanceContext) {
|
||||
return new ThisPropertyAccessGenerator(this, token, n, getter, setter);
|
||||
} else {
|
||||
return new IllegalThisPropertyAccessGenerator(
|
||||
this, token, n, getter, setter);
|
||||
}
|
||||
} else if (declaration.isRegularMethod) {
|
||||
assert(declaration.isStatic || declaration.isTopLevel);
|
||||
return new StaticAccessGenerator(this, token, declaration.target, null);
|
||||
|
||||
@@ -908,6 +908,56 @@ abstract class ErroneousExpressionGenerator implements Generator {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class IllegalThisPropertyAccessGenerator
|
||||
implements ErroneousExpressionGenerator {
|
||||
factory IllegalThisPropertyAccessGenerator(ExpressionGeneratorHelper helper,
|
||||
Token token, Name name, Member getter, Member setter) {
|
||||
return helper.forest.illegalThisPropertyAccessGenerator(
|
||||
helper, token, name, getter, setter);
|
||||
}
|
||||
|
||||
@override
|
||||
Generator asLvalue() {
|
||||
return new NonLvalueGenerator(
|
||||
helper,
|
||||
token,
|
||||
buildError(new Arguments([]),
|
||||
isSetter: true, offset: offsetForToken(token)),
|
||||
buildSimpleWrite());
|
||||
}
|
||||
|
||||
/// Builds an [Expression] representing a write to the generator.
|
||||
Expression buildSimpleWrite();
|
||||
|
||||
@override
|
||||
String get debugName => "IllegalThisPropertyAccessGenerator";
|
||||
|
||||
@override
|
||||
Expression doInvocation(int charOffset, Arguments arguments) {
|
||||
return new UnresolvedTargetInvocationJudgment(
|
||||
buildError(arguments, offset: charOffset), arguments)
|
||||
..fileOffset = arguments.fileOffset;
|
||||
}
|
||||
|
||||
@override
|
||||
Expression buildError(Arguments arguments,
|
||||
{bool isGetter: false, bool isSetter: false, int offset}) {
|
||||
offset ??= offsetForToken(this.token);
|
||||
return helper.throwNoSuchMethodError(
|
||||
forest.literalNull(null)..fileOffset = offset,
|
||||
plainNameForRead ?? '',
|
||||
arguments,
|
||||
offset,
|
||||
isGetter: isGetter,
|
||||
isSetter: isSetter);
|
||||
}
|
||||
|
||||
@override
|
||||
/* Expression | Generator */ Object prefixedLookup(Token name) {
|
||||
throw new UnimplementedError('TODO(paulberry)');
|
||||
}
|
||||
}
|
||||
|
||||
abstract class UnresolvedNameGenerator implements ErroneousExpressionGenerator {
|
||||
factory UnresolvedNameGenerator(
|
||||
ExpressionGeneratorHelper helper, Token token, Name name) {
|
||||
|
||||
@@ -48,6 +48,7 @@ import 'kernel_expression_generator.dart'
|
||||
KernelDeferredAccessGenerator,
|
||||
KernelDelayedAssignment,
|
||||
KernelDelayedPostfixIncrement,
|
||||
KernelIllegalThisPropertyAccessGenerator,
|
||||
KernelIndexedAccessGenerator,
|
||||
KernelLargeIntAccessGenerator,
|
||||
KernelLoadLibraryGenerator,
|
||||
@@ -683,6 +684,16 @@ class Fangorn extends Forest {
|
||||
helper, token, name, getter, setter);
|
||||
}
|
||||
|
||||
KernelIllegalThisPropertyAccessGenerator illegalThisPropertyAccessGenerator(
|
||||
ExpressionGeneratorHelper helper,
|
||||
Token token,
|
||||
Name name,
|
||||
Member getter,
|
||||
Member setter) {
|
||||
return new KernelIllegalThisPropertyAccessGenerator(
|
||||
helper, token, name, getter, setter);
|
||||
}
|
||||
|
||||
@override
|
||||
KernelNullAwarePropertyAccessGenerator nullAwarePropertyAccessGenerator(
|
||||
ExpressionGeneratorHelper helper,
|
||||
|
||||
@@ -376,6 +376,9 @@ abstract class Forest {
|
||||
Generator thisPropertyAccessGenerator(ExpressionGeneratorHelper helper,
|
||||
Token location, Name name, Member getter, Member setter);
|
||||
|
||||
Generator illegalThisPropertyAccessGenerator(ExpressionGeneratorHelper helper,
|
||||
Token location, Name name, Member getter, Member setter);
|
||||
|
||||
Generator nullAwarePropertyAccessGenerator(
|
||||
ExpressionGeneratorHelper helper,
|
||||
Token location,
|
||||
|
||||
@@ -90,6 +90,7 @@ export 'kernel_shadow_ast.dart'
|
||||
IfNullJudgment,
|
||||
IfJudgment,
|
||||
IllegalAssignmentJudgment,
|
||||
IllegalPropertySetJudgment,
|
||||
IndexAssignmentJudgment,
|
||||
InvalidConstructorInvocationJudgment,
|
||||
InvalidPropertyGetJudgment,
|
||||
|
||||
@@ -11,15 +11,15 @@ import '../constant_context.dart' show ConstantContext;
|
||||
|
||||
import '../fasta_codes.dart'
|
||||
show
|
||||
Message,
|
||||
LocatedMessage,
|
||||
Message,
|
||||
messageCannotAssignToParenthesizedExpression,
|
||||
messageInvalidUseOfNullAwareAccess,
|
||||
messageLoadLibraryTakesNoArguments,
|
||||
messageNotAConstantExpression,
|
||||
messageCannotAssignToParenthesizedExpression,
|
||||
templateNotConstantExpression,
|
||||
messageSuperAsExpression,
|
||||
templateThisOrSuperAccessInFieldInitializer,
|
||||
messageInvalidUseOfNullAwareAccess;
|
||||
templateNotConstantExpression,
|
||||
templateThisOrSuperAccessInFieldInitializer;
|
||||
|
||||
import '../messages.dart' show Message, noLength;
|
||||
|
||||
@@ -44,6 +44,7 @@ import 'expression_generator.dart'
|
||||
ErroneousExpressionGenerator,
|
||||
ExpressionGenerator,
|
||||
Generator,
|
||||
IllegalThisPropertyAccessGenerator,
|
||||
IndexedAccessGenerator,
|
||||
LargeIntAccessGenerator,
|
||||
LoadLibraryGenerator,
|
||||
@@ -78,6 +79,7 @@ import 'kernel_ast_api.dart'
|
||||
Constructor,
|
||||
DartType,
|
||||
Field,
|
||||
IllegalPropertySetJudgment,
|
||||
IndexAssignmentJudgment,
|
||||
Initializer,
|
||||
InvalidPropertyGetJudgment,
|
||||
@@ -1441,6 +1443,49 @@ class KernelLargeIntAccessGenerator extends KernelGenerator
|
||||
}
|
||||
}
|
||||
|
||||
class KernelIllegalThisPropertyAccessGenerator extends KernelGenerator
|
||||
with ErroneousExpressionGenerator, IllegalThisPropertyAccessGenerator {
|
||||
@override
|
||||
final Name name;
|
||||
|
||||
final Member getter;
|
||||
|
||||
final Member setter;
|
||||
|
||||
KernelIllegalThisPropertyAccessGenerator(ExpressionGeneratorHelper helper,
|
||||
Token token, this.name, this.getter, this.setter)
|
||||
: super(helper, token);
|
||||
|
||||
@override
|
||||
Expression buildSimpleRead() {
|
||||
Expression error = buildError(forest.argumentsEmpty(token), isGetter: true);
|
||||
return new InvalidPropertyGetJudgment(error, getter)
|
||||
..fileOffset = token.charOffset;
|
||||
}
|
||||
|
||||
Expression buildSimpleWrite() {
|
||||
Expression error = buildError(forest.argumentsEmpty(token), isSetter: true);
|
||||
return new IllegalPropertySetJudgment(error, token.isSynthetic, setter)
|
||||
..fileOffset = token.charOffset;
|
||||
}
|
||||
|
||||
DartType buildTypeWithBuiltArguments(List<DartType> arguments,
|
||||
{bool nonInstanceAccessIsError: false, TypeInferrer typeInferrer}) {
|
||||
var type = super.buildTypeWithBuiltArguments(arguments,
|
||||
nonInstanceAccessIsError: nonInstanceAccessIsError,
|
||||
typeInferrer: typeInferrer);
|
||||
typeInferrer.storeTypeReference(
|
||||
token.offset, token.isSynthetic, null, null, type);
|
||||
return type;
|
||||
}
|
||||
|
||||
@override
|
||||
void printOn(StringSink sink) {
|
||||
sink.write(", name: ");
|
||||
sink.write(name.name);
|
||||
}
|
||||
}
|
||||
|
||||
class KernelUnresolvedNameGenerator extends KernelGenerator
|
||||
with ErroneousExpressionGenerator, UnresolvedNameGenerator {
|
||||
@override
|
||||
|
||||
@@ -1716,6 +1716,28 @@ class IndexAssignmentJudgment extends ComplexAssignmentJudgmentWithReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
class IllegalPropertySetJudgment extends SyntheticExpressionJudgment {
|
||||
final bool forSyntheticToken;
|
||||
|
||||
final Member setter;
|
||||
|
||||
IllegalPropertySetJudgment(
|
||||
Expression desugared, this.forSyntheticToken, this.setter)
|
||||
: super(desugared);
|
||||
|
||||
@override
|
||||
Expression infer<Expression, Statement, Initializer, Type>(
|
||||
ShadowTypeInferrer inferrer,
|
||||
Factory<Expression, Statement, Initializer, Type> factory,
|
||||
DartType typeContext) {
|
||||
_replaceWithDesugared();
|
||||
inferredType = const DynamicType();
|
||||
inferrer.listener.propertyAssign(
|
||||
this, fileOffset, setter, setter.setterType, null, inferredType);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Common base class for shadow objects representing initializers in kernel
|
||||
/// form.
|
||||
abstract class InitializerJudgment implements Initializer {
|
||||
|
||||
Reference in New Issue
Block a user