[analyzer] Make constant assertion errors more helpful and specific.

If a condition is valid, but fails in an assert, make sure we report
that and a pseudo trace with context messages for the user to link
back to the problem.

This CL also starts building the stack trace when we call a super constructor for more detailed errors.

Bug: https://github.com/dart-lang/sdk/issues/36526
Change-Id: Ib9bc9841256644d2380d3f806e3bc3c9d84b37cf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/316625
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Kallen Tu <kallentu@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 273ae02..3210c43 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -384,6 +384,8 @@
   status: needsFix
   notes: |-
     Remove the `deferred` keyword from the import.
+CompileTimeErrorCode.CONST_EVAL_ASSERTION_FAILURE:
+  status: needsEvaluation
 CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD:
   status: needsEvaluation
 CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT:
diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
index 0a480ee..6963067 100644
--- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart
+++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart
@@ -25,9 +25,11 @@
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type_algebra.dart';
 import 'package:analyzer/src/dart/element/type_system.dart' show TypeSystemImpl;
+import 'package:analyzer/src/diagnostic/diagnostic.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/constant.dart';
 import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/java_core.dart';
 import 'package:analyzer/src/task/api/model.dart';
 import 'package:analyzer/src/utilities/extensions/collection.dart';
 import 'package:analyzer/src/utilities/extensions/object.dart';
@@ -2527,12 +2529,25 @@
         superName: evaluationResult.superName,
         superArguments: evaluationResult.superArguments);
     if (error != null) {
-      // TODO(kallentu): Report a better error here with context from the other
-      // error reported.
+      final formattedMessage =
+          formatList(error.errorCode.problemMessage, error.arguments);
+      final contextMessage = DiagnosticMessageImpl(
+        filePath: _library.source.fullName,
+        length: error.node.length,
+        message: "The exception is '$formattedMessage' and occurs here.",
+        offset: error.node.offset,
+        url: null,
+      );
+
+      // TODO(kallentu): When removing all on-site reporting, move this error
+      // to [_InstanceCreationEvaluator.evaluate] and provide context for all
+      // constructor related errors.
       _errorReporter.reportErrorForNode(
-          CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, _errorNode);
-      return InvalidConstant(
-          _errorNode, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION);
+          CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION,
+          _errorNode,
+          [],
+          [...error.contextMessages, contextMessage]);
+      return error;
     }
 
     return DartObjectImpl(
@@ -2755,12 +2770,12 @@
           case DartObjectImpl():
             if (!evaluationConstant.isBool ||
                 evaluationConstant.toBoolValue() == false) {
-              // TODO(kallentu): Report a better error here.
+              // TODO(kallentu): Don't report error here.
               _errorReporter.reportErrorForNode(
                   CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, _errorNode);
               return _InitializersEvaluationResult(
-                  InvalidConstant(_errorNode,
-                      CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION),
+                  InvalidConstant(initializer,
+                      CompileTimeErrorCode.CONST_EVAL_ASSERTION_FAILURE),
                   evaluationIsComplete: true);
             }
           case InvalidConstant():
@@ -2913,6 +2928,16 @@
           case DartObjectImpl():
             _fieldMap[GenericState.SUPERCLASS_FIELD] = evaluationResult;
           case InvalidConstant():
+            evaluationResult.contextMessages.add(DiagnosticMessageImpl(
+              filePath: _constructor.source.fullName,
+              length: _constructor.nameLength,
+              message:
+                  "The evaluated constructor '${superConstructor.displayName}' "
+                  "is called by '${_constructor.displayName}' and "
+                  "'${_constructor.displayName}' is defined here.",
+              offset: _constructor.nameOffset,
+              url: null,
+            ));
             return evaluationResult;
         }
       }
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index 2c1f843..2bdb58b 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -807,6 +807,12 @@
     hasPublishedDocs: true,
   );
 
+  static const CompileTimeErrorCode CONST_EVAL_ASSERTION_FAILURE =
+      CompileTimeErrorCode(
+    'CONST_EVAL_ASSERTION_FAILURE',
+    "The assertion in this constant expression failed.",
+  );
+
   static const CompileTimeErrorCode CONST_EVAL_EXTENSION_METHOD =
       CompileTimeErrorCode(
     'CONST_EVAL_EXTENSION_METHOD',
diff --git a/pkg/analyzer/lib/src/error/error_code_values.g.dart b/pkg/analyzer/lib/src/error/error_code_values.g.dart
index 75a5a61..9da86b3 100644
--- a/pkg/analyzer/lib/src/error/error_code_values.g.dart
+++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart
@@ -110,6 +110,7 @@
   CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_CONST_SUPER,
   CompileTimeErrorCode.CONST_CONSTRUCTOR_WITH_NON_FINAL_FIELD,
   CompileTimeErrorCode.CONST_DEFERRED_CLASS,
+  CompileTimeErrorCode.CONST_EVAL_ASSERTION_FAILURE,
   CompileTimeErrorCode.CONST_EVAL_EXTENSION_METHOD,
   CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT,
   CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION,
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 38bd475..5c35cf8 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -2575,6 +2575,8 @@
 
       const json2 = convert.JsonCodec();
       ```
+  CONST_EVAL_ASSERTION_FAILURE:
+    problemMessage: "The assertion in this constant expression failed."
   CONST_EVAL_EXTENSION_METHOD:
     problemMessage: "Extension methods can't be used in constant expressions."
   CONST_EVAL_FOR_ELEMENT:
diff --git a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
index f4a67f3..001170c 100644
--- a/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
+++ b/pkg/analyzer/test/src/dart/constant/evaluation_test.dart
@@ -3447,6 +3447,35 @@
 
 @reflectiveTest
 mixin InstanceCreationEvaluatorTestCases on ConstantVisitorTestSupport {
+  test_assertInitializer_indirect() async {
+    await assertErrorsInCode(r'''
+class A {
+  const A(int i)
+  : assert(i == 1); // (2)
+}
+class B extends A {
+  const B(int i) : super(i);
+}
+main() {
+  print(const B(2)); // (1)
+}
+''', [
+      error(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION,
+        124,
+        10,
+        contextMessages: [
+          ExpectedContextMessage('/home/test/lib/test.dart', 84, 1,
+              text:
+                  "The evaluated constructor 'A' is called by 'B' and 'B' is defined here."),
+          ExpectedContextMessage('/home/test/lib/test.dart', 31, 14,
+              text:
+                  "The exception is 'The assertion in this constant expression failed.' and occurs here."),
+        ],
+      ),
+    ]);
+  }
+
   test_assertInitializer_intInDoubleContext_false() async {
     await assertErrorsInCode('''
 class A {
@@ -3479,7 +3508,19 @@
 }
 const b = const B();
 ''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 101, 9),
+      error(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION,
+        101,
+        9,
+        contextMessages: [
+          ExpectedContextMessage('/home/test/lib/test.dart', 74, 1,
+              text:
+                  "The evaluated constructor 'A' is called by 'B' and 'B' is defined here."),
+          ExpectedContextMessage('/home/test/lib/test.dart', 23, 19,
+              text:
+                  "The exception is 'The assertion in this constant expression failed.' and occurs here."),
+        ],
+      ),
     ]);
   }
 
diff --git a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
index dc849a3..9d323df 100644
--- a/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/const_eval_throws_exception_test.dart
@@ -6,6 +6,7 @@
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import '../../generated/test_support.dart';
 import '../dart/resolution/context_collection_resolution.dart';
 
 main() {
@@ -65,6 +66,33 @@
       error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 11, 3),
     ]);
   }
+
+  test_superConstructor_paramTypeMismatch() async {
+    await assertErrorsInCode(r'''
+class C {
+  final double d;
+  const C(this.d);
+}
+class D extends C {
+  const D(d) : super(d);
+}
+const f = const D('0.0');
+''', [
+      error(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION,
+        106,
+        14,
+        contextMessages: [
+          ExpectedContextMessage('/home/test/lib/test.dart', 90, 1,
+              text:
+                  "The exception is 'A value of type 'String' can't be assigned to a parameter of type 'double' in a const constructor.' and occurs here."),
+          ExpectedContextMessage('/home/test/lib/test.dart', 77, 1,
+              text:
+                  "The evaluated constructor 'C' is called by 'D' and 'D' is defined here."),
+        ],
+      ),
+    ]);
+  }
 }
 
 @reflectiveTest
@@ -80,6 +108,35 @@
     ]);
   }
 
+  test_assertion_indirect() async {
+    await assertErrorsInCode(r'''
+class A {
+  const A(int i)
+  : assert(i == 1); // (2)
+}
+class B extends A {
+  const B(int i) : super(i);
+}
+main() {
+  print(const B(2)); // (1)
+}
+''', [
+      error(
+        CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION,
+        124,
+        10,
+        contextMessages: [
+          ExpectedContextMessage('/home/test/lib/test.dart', 84, 1,
+              text:
+                  "The evaluated constructor 'A' is called by 'B' and 'B' is defined here."),
+          ExpectedContextMessage('/home/test/lib/test.dart', 31, 14,
+              text:
+                  "The exception is 'The assertion in this constant expression failed.' and occurs here."),
+        ],
+      ),
+    ]);
+  }
+
   test_CastError_intToDouble_constructor_importAnalyzedAfter() async {
     // See dartbug.com/35993
     newFile('$testPackageLibPath/other.dart', '''
@@ -289,21 +346,6 @@
     ]);
   }
 
-  test_superConstructor_paramTypeMismatch() async {
-    await assertErrorsInCode(r'''
-class C {
-  final double d;
-  const C(this.d);
-}
-class D extends C {
-  const D(d) : super(d);
-}
-const f = const D('0.0');
-''', [
-      error(CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION, 106, 14),
-    ]);
-  }
-
   test_symbolConstructor_nonStringArgument() async {
     await assertErrorsInCode(r'''
 var s2 = const Symbol(3);