Store resolution for method invocations that are not valid constants.
Change-Id: Iec71ee1f464c05288eba658e086519cceee9ce16
Reviewed-on: https://dart-review.googlesource.com/64802
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/test/src/context/mock_sdk.dart b/pkg/analyzer/test/src/context/mock_sdk.dart
index ab5a758..54d7673 100644
--- a/pkg/analyzer/test/src/context/mock_sdk.dart
+++ b/pkg/analyzer/test/src/context/mock_sdk.dart
@@ -157,6 +157,7 @@
bool get isEmpty => false;
bool get isNotEmpty => false;
int get length => 0;
+ int codeUnitAt(int index);
String substring(int len) => null;
String toLowerCase();
String toUpperCase();
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
index 912e315..8586d1c 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_resolution_test.dart
@@ -50,6 +50,8 @@
InterfaceType get mapType => typeProvider.mapType;
+ ClassElement get stringElement => typeProvider.stringType.element;
+
TypeProvider get typeProvider => result.unit.element.context.typeProvider;
void assertElement(Expression node, Element expected) {
@@ -3201,6 +3203,69 @@
assertType(aRef, 'int');
}
+ test_invalid_const_methodInvocation() async {
+ addTestFile(r'''
+const a = 'foo';
+const b = 0;
+const c = a.codeUnitAt(b);
+''');
+ await resolveTestFile();
+ expect(result.errors, isNotEmpty);
+
+ var invocation = findNode.methodInvocation('codeUnitAt');
+ assertType(invocation, 'int');
+ assertInvokeType(invocation, '(int) → int');
+ assertElement(invocation.methodName, stringElement.getMethod('codeUnitAt'));
+
+ var aRef = invocation.target;
+ assertElement(aRef, findElement.topGet('a'));
+ assertType(aRef, 'String');
+
+ var bRef = invocation.argumentList.arguments[0];
+ assertElement(bRef, findElement.topGet('b'));
+ assertType(bRef, 'int');
+ }
+
+ test_invalid_const_methodInvocation_static() async {
+ addTestFile(r'''
+const c = A.m();
+class A {
+ static int m() => 0;
+}
+''');
+ await resolveTestFile();
+ expect(result.errors, isNotEmpty);
+
+ var invocation = findNode.methodInvocation('m();');
+ assertType(invocation, 'int');
+ assertInvokeType(invocation, '() → int');
+ assertElement(invocation.methodName, findElement.method('m'));
+ }
+
+ test_invalid_const_methodInvocation_topLevelFunction() async {
+ addTestFile(r'''
+const id = identical;
+const a = 0;
+const b = 0;
+const c = id(a, b);
+''');
+ await resolveTestFile();
+ expect(result.errors, isNotEmpty);
+
+ var invocation = findNode.methodInvocation('id(');
+ assertType(invocation, 'bool');
+ assertInvokeType(invocation, '(Object, Object) → bool');
+ assertElement(invocation.methodName, findElement.topGet('id'));
+
+ var aRef = invocation.argumentList.arguments[0];
+ assertElement(aRef, findElement.topGet('a'));
+ assertType(aRef, 'int');
+
+ var bRef = invocation.argumentList.arguments[1];
+ assertElement(bRef, findElement.topGet('b'));
+ assertType(bRef, 'int');
+ }
+
test_invalid_const_throw_local() async {
addTestFile(r'''
main() {
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 57a5a7d..50489a7 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -2537,11 +2537,9 @@
int foo() => 42;
''', allowErrors: true);
if (isSharedFrontEnd) {
- // It is OK to keep non-constant initializers.
checkElementText(library, r'''
class C {
- static const int f = 1 +
- foo/*location: test.dart;foo*/();
+ static const int f = #invalidConst;
}
int foo() {}
''');
@@ -2605,10 +2603,8 @@
int foo() => 42;
''', allowErrors: true);
if (isSharedFrontEnd) {
- // It is OK to keep non-constant initializers.
checkElementText(library, r'''
-const int v = 1 +
- foo/*location: test.dart;foo*/();
+const int v = #invalidConst;
int foo() {}
''');
} else if (isStrongMode) {
@@ -4646,8 +4642,18 @@
}
int foo() => 42;
''', allowErrors: true);
- // It is OK to keep non-constant initializers.
- checkElementText(library, r'''
+ if (isSharedFrontEnd) {
+ checkElementText(library, r'''
+class C {
+ final dynamic x;
+ const C() :
+ x/*location: test.dart;C;x*/ = #invalidConst;
+}
+int foo() {}
+''');
+ } else {
+ // It is OK to keep non-constant initializers.
+ checkElementText(library, r'''
class C {
final dynamic x;
const C() :
@@ -4656,6 +4662,7 @@
}
int foo() {}
''');
+ }
}
test_constructor_initializers_field_withParameter() async {
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index d344f86..fb42b80 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -2738,7 +2738,9 @@
@override
Expression buildStaticInvocation(Member target, Arguments arguments,
- {Constness constness: Constness.implicit, int charOffset: -1}) {
+ {Constness constness: Constness.implicit,
+ int charOffset: -1,
+ Expression error}) {
// The argument checks for the initial target of redirecting factories
// invocations are skipped in Dart 1.
if (library.loader.target.strongMode || !isRedirectingFactory(target)) {
@@ -2790,7 +2792,7 @@
} else {
return new StaticInvocationJudgment(
target, forest.castArguments(arguments),
- isConst: isConst)
+ desugaredError: error, isConst: isConst)
..fileOffset = charOffset;
}
}
@@ -4209,14 +4211,18 @@
@override
Expression buildMethodInvocation(
Expression receiver, Name name, Arguments arguments, int offset,
- {bool isConstantExpression: false,
+ {Expression error,
+ bool isConstantExpression: false,
bool isNullAware: false,
bool isImplicitCall: false,
bool isSuper: false,
Member interfaceTarget}) {
if (constantContext != ConstantContext.none && !isConstantExpression) {
- return deprecated_buildCompileTimeError(
- "Not a constant expression.", offset);
+ error = buildCompileTimeError(
+ fasta.templateNotConstantExpression
+ .withArguments('Method invocation'),
+ offset,
+ name.name.length);
}
if (isSuper) {
// We can ignore [isNullAware] on super sends.
@@ -4235,15 +4241,17 @@
name.name.length);
}
return new SuperMethodInvocationJudgment(
- name, forest.castArguments(arguments), target)
+ name, forest.castArguments(arguments),
+ interfaceTarget: target, desugaredError: error)
..fileOffset = offset;
}
- receiver = new SuperPropertyGetJudgment(name, target)
+ receiver = new SuperPropertyGetJudgment(name,
+ interfaceTarget: target, desugaredError: error)
..fileOffset = offset;
return new MethodInvocationJudgment(
receiver, callName, forest.castArguments(arguments),
- isImplicitCall: true)
+ isImplicitCall: true, desugaredError: error)
..fileOffset = forest.readOffset(arguments);
}
@@ -4259,12 +4267,15 @@
new MethodInvocation(new VariableGet(variable), name,
forest.castArguments(arguments), interfaceTarget)
..fileOffset = offset)
- ..fileOffset = offset)
+ ..fileOffset = offset,
+ desugaredError: error)
..fileOffset = offset;
} else {
return new MethodInvocationJudgment(
receiver, name, forest.castArguments(arguments),
- isImplicitCall: isImplicitCall, interfaceTarget: interfaceTarget)
+ isImplicitCall: isImplicitCall,
+ interfaceTarget: interfaceTarget,
+ desugaredError: error)
..fileOffset = offset;
}
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
index 2a554c6..a7b7d47 100644
--- a/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/expression_generator_helper.dart
@@ -94,7 +94,7 @@
[int charOffset = -1]);
Expression buildStaticInvocation(Procedure target, Arguments arguments,
- {Constness constness, int charOffset});
+ {Constness constness, int charOffset, Expression error});
Expression buildProblemExpression(
ProblemBuilder builder, int offset, int length);
@@ -125,7 +125,8 @@
Expression buildMethodInvocation(
Expression receiver, Name name, Arguments arguments, int offset,
- {bool isConstantExpression,
+ {Expression error,
+ bool isConstantExpression,
bool isNullAware,
bool isImplicitCall,
bool isSuper,
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart
index 142237c..484340e 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_expression_generator.dart
@@ -13,7 +13,8 @@
show
LocatedMessage,
messageLoadLibraryTakesNoArguments,
- messageSuperAsExpression;
+ messageSuperAsExpression,
+ templateNotConstantExpression;
import '../messages.dart' show Message, noLength;
@@ -618,7 +619,7 @@
helper.warnUnresolvedGet(name, offsetForToken(token), isSuper: true);
}
// TODO(ahe): Use [DirectPropertyGet] when possible.
- var read = new SuperPropertyGetJudgment(name, getter)
+ var read = new SuperPropertyGetJudgment(name, interfaceTarget: getter)
..fileOffset = offsetForToken(token);
complexAssignment?.read = read;
return read;
@@ -981,10 +982,9 @@
isSuper: true);
}
// TODO(ahe): Use [DirectMethodInvocation] when possible.
- return new SuperMethodInvocationJudgment(
- indexGetName,
+ return new SuperMethodInvocationJudgment(indexGetName,
forest.castArguments(forest.arguments(<Expression>[index], token)),
- getter)
+ interfaceTarget: getter)
..fileOffset = offsetForToken(token);
}
@@ -1113,21 +1113,25 @@
@override
Expression doInvocation(int offset, Arguments arguments) {
+ Expression error;
if (helper.constantContext != ConstantContext.none &&
!helper.isIdentical(readTarget)) {
- helper.deprecated_addCompileTimeError(
- offset, "Not a constant expression.");
+ error = helper.buildCompileTimeError(
+ templateNotConstantExpression.withArguments('Method invocation'),
+ offset,
+ readTarget?.name?.name?.length ?? 0);
}
if (readTarget == null || isFieldOrGetter(readTarget)) {
return helper.buildMethodInvocation(buildSimpleRead(), callName,
arguments, offset + (readTarget?.name?.name?.length ?? 0),
+ error: error,
// This isn't a constant expression, but we have checked if a
// constant expression error should be emitted already.
isConstantExpression: true,
isImplicitCall: true);
} else {
return helper.buildStaticInvocation(readTarget, arguments,
- charOffset: offset);
+ charOffset: offset, error: error);
}
}
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
index de4c780..75b3b44 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart
@@ -2084,6 +2084,7 @@
/// Shadow object for [MethodInvocation].
class MethodInvocationJudgment extends MethodInvocation
implements ExpressionJudgment {
+ final kernel.Expression desugaredError;
DartType inferredType;
/// Indicates whether this method invocation is a call to a `call` method
@@ -2092,7 +2093,7 @@
MethodInvocationJudgment(
Expression receiver, Name name, ArgumentsJudgment arguments,
- {bool isImplicitCall: false, Member interfaceTarget})
+ {this.desugaredError, bool isImplicitCall: false, Member interfaceTarget})
: _isImplicitCall = isImplicitCall,
super(receiver, name, arguments, interfaceTarget);
@@ -2107,6 +2108,10 @@
factory, this, receiver, fileOffset, _isImplicitCall, typeContext,
desugaredInvocation: this);
inferredType = inferenceResult.type;
+ if (desugaredError != null) {
+ parent.replaceChild(this, desugaredError);
+ parent = null;
+ }
return null;
}
}
@@ -2183,10 +2188,12 @@
/// let v = a in v == null ? null : v.b(...)
class NullAwareMethodInvocationJudgment extends Let
implements ExpressionJudgment {
+ final kernel.Expression desugaredError;
DartType inferredType;
NullAwareMethodInvocationJudgment(
- VariableDeclaration variable, Expression body)
+ VariableDeclaration variable, Expression body,
+ {this.desugaredError})
: super(variable, body);
@override
@@ -2567,10 +2574,11 @@
/// Shadow object for [StaticInvocation].
class StaticInvocationJudgment extends StaticInvocation
implements ExpressionJudgment {
+ final kernel.Expression desugaredError;
DartType inferredType;
StaticInvocationJudgment(Procedure target, ArgumentsJudgment arguments,
- {bool isConst: false})
+ {this.desugaredError, bool isConst: false})
: super(target, arguments, isConst: isConst);
ArgumentsJudgment get argumentJudgments => arguments;
@@ -2593,6 +2601,10 @@
inferrer.lastCalleeType,
inferrer.lastInferredSubstitution,
inferredType);
+ if (desugaredError != null) {
+ parent.replaceChild(this, desugaredError);
+ parent = null;
+ }
return null;
}
}
@@ -2681,10 +2693,11 @@
/// Shadow object for [SuperMethodInvocation].
class SuperMethodInvocationJudgment extends SuperMethodInvocation
implements ExpressionJudgment {
+ final kernel.Expression desugaredError;
DartType inferredType;
SuperMethodInvocationJudgment(Name name, ArgumentsJudgment arguments,
- [Procedure interfaceTarget])
+ {this.desugaredError, Procedure interfaceTarget})
: super(name, arguments, interfaceTarget);
ArgumentsJudgment get argumentJudgments => arguments;
@@ -2704,6 +2717,10 @@
methodName: name,
arguments: arguments);
inferredType = inferenceResult.type;
+ if (desugaredError != null) {
+ parent.replaceChild(this, desugaredError);
+ parent = null;
+ }
return null;
}
}
@@ -2711,9 +2728,11 @@
/// Shadow object for [SuperPropertyGet].
class SuperPropertyGetJudgment extends SuperPropertyGet
implements ExpressionJudgment {
+ final kernel.Expression desugaredError;
DartType inferredType;
- SuperPropertyGetJudgment(Name name, [Member interfaceTarget])
+ SuperPropertyGetJudgment(Name name,
+ {this.desugaredError, Member interfaceTarget})
: super(name, interfaceTarget);
@override
@@ -2727,6 +2746,10 @@
}
inferrer.inferPropertyGet(factory, this, null, fileOffset, typeContext,
interfaceMember: interfaceTarget, propertyName: name);
+ if (desugaredError != null) {
+ parent.replaceChild(this, desugaredError);
+ parent = null;
+ }
return null;
}
}