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;
   }
 }