Catch macro execution exceptions and report MACRO_EXECUTION_ERROR.

Change-Id: I43dbbba6c975d274fcc90907d6461be5bc750a1b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211482
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 76b327e..dd94f01 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -267,6 +267,7 @@
   CompileTimeErrorCode.LATE_FINAL_FIELD_WITH_CONST_CONSTRUCTOR,
   CompileTimeErrorCode.LATE_FINAL_LOCAL_ALREADY_ASSIGNED,
   CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,
+  CompileTimeErrorCode.MACRO_EXECUTION_ERROR,
   CompileTimeErrorCode.MAIN_FIRST_POSITIONAL_PARAMETER_TYPE,
   CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS,
   CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS,
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 61f515c..433fcaa 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -80,7 +80,7 @@
 /// TODO(scheglov) Clean up the list of implicitly analyzed files.
 class AnalysisDriver implements AnalysisDriverGeneric {
   /// The version of data format, should be incremented on every format change.
-  static const int DATA_VERSION = 172;
+  static const int DATA_VERSION = 174;
 
   /// The number of exception contexts allowed to write. Once this field is
   /// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index ec1e0ed..57d2d82 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -43,6 +43,7 @@
 import 'package:analyzer/src/generated/utilities_collection.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
 import 'package:analyzer/src/generated/utilities_general.dart';
+import 'package:analyzer/src/macro/impl/error.dart' as macro;
 import 'package:analyzer/src/summary2/ast_binary_tokens.dart';
 import 'package:analyzer/src/summary2/bundle_reader.dart';
 import 'package:analyzer/src/summary2/reference.dart';
@@ -447,6 +448,10 @@
   /// TODO(scheglov) implement as modifier
   bool _isSimplyBounded = true;
 
+  /// The list of errors recorded during execution of macro builders
+  /// over this class.
+  List<macro.MacroExecutionError> macroExecutionErrors = [];
+
   ElementLinkedData? linkedData;
 
   /// Initialize a newly created class element to have the given [name] at the
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 6a29b7d..e2e2151 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -7653,6 +7653,15 @@
           hasPublishedDocs: true);
 
   /**
+   * Parameters:
+   * 0: the name of the macro
+   * 1: the message
+   */
+  static const CompileTimeErrorCode MACRO_EXECUTION_ERROR =
+      CompileTimeErrorCode(
+          'MACRO_EXECUTION_ERROR', "Exception thrown by macro {0}: {1}");
+
+  /**
    * No parameters.
    */
   // #### Description
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 7f5d9e6..f68eea7 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -45,6 +45,7 @@
 import 'package:analyzer/src/generated/java_engine.dart';
 import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
 import 'package:analyzer/src/generated/this_access_tracker.dart';
+import 'package:analyzer/src/macro/impl/error.dart' as macro;
 import 'package:collection/collection.dart';
 
 class EnclosingExecutableContext {
@@ -444,7 +445,8 @@
     var outerClass = _enclosingClass;
     try {
       _isInNativeClass = node.nativeClause != null;
-      _enclosingClass = node.declaredElement as ClassElementImpl;
+      var enclosingClass = node.declaredElement as ClassElementImpl;
+      _enclosingClass = enclosingClass;
 
       List<ClassMember> members = node.members;
       _duplicateDefinitionVerifier.checkClass(node);
@@ -468,6 +470,10 @@
       _checkForBadFunctionUse(node);
       _checkForWrongTypeParameterVarianceInSuperinterfaces();
       _checkForMainFunction(node.name);
+      _reportMacroExecutionErrors(
+        node.metadata,
+        enclosingClass.macroExecutionErrors,
+      );
       super.visitClassDeclaration(node);
     } finally {
       _isInNativeClass = false;
@@ -4986,6 +4992,23 @@
     return null;
   }
 
+  /// Report [macroExecutionErrors] at the corresponding [annotations].
+  void _reportMacroExecutionErrors(
+    List<Annotation> annotations,
+    List<macro.MacroExecutionError> macroExecutionErrors,
+  ) {
+    for (var macroExecutionError in macroExecutionErrors) {
+      errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.MACRO_EXECUTION_ERROR,
+        annotations[macroExecutionError.annotationIndex],
+        [
+          macroExecutionError.macroName,
+          macroExecutionError.message,
+        ],
+      );
+    }
+  }
+
   void _withEnclosingExecutable(
     ExecutableElement element,
     void Function() operation,
diff --git a/pkg/analyzer/lib/src/macro/impl/error.dart b/pkg/analyzer/lib/src/macro/impl/error.dart
new file mode 100644
index 0000000..8c2780c
--- /dev/null
+++ b/pkg/analyzer/lib/src/macro/impl/error.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// Error that happened during executing a macro builder.
+class MacroExecutionError {
+  final int annotationIndex;
+  final String macroName;
+  final String message;
+
+  MacroExecutionError({
+    required this.annotationIndex,
+    required this.macroName,
+    required this.message,
+  });
+}
diff --git a/pkg/analyzer/lib/src/summary2/bundle_reader.dart b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
index bdfc35d..f406b41 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
@@ -18,6 +18,7 @@
 import 'package:analyzer/src/dart/resolver/variance.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:analyzer/src/macro/impl/error.dart' as macro;
 import 'package:analyzer/src/summary2/ast_binary_reader.dart';
 import 'package:analyzer/src/summary2/ast_binary_tag.dart';
 import 'package:analyzer/src/summary2/data_reader.dart';
@@ -528,6 +529,10 @@
     element.setLinkedData(reference, linkedData);
     ClassElementFlags.read(_reader, element);
 
+    element.macroExecutionErrors = _reader.readTypedList(
+      _readMacroExecutionError,
+    );
+
     element.typeParameters = _readTypeParameters();
 
     if (!element.isMixinApplication) {
@@ -866,6 +871,14 @@
     }
   }
 
+  macro.MacroExecutionError _readMacroExecutionError() {
+    return macro.MacroExecutionError(
+      annotationIndex: _reader.readUInt30(),
+      macroName: _reader.readStringReference(),
+      message: _reader.readStringReference(),
+    );
+  }
+
   List<MethodElementImpl> _readMethods(
     CompilationUnitElementImpl unitElement,
     ElementImpl enclosingElement,
diff --git a/pkg/analyzer/lib/src/summary2/bundle_writer.dart b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
index 6c96d5e..44dcc92 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
@@ -14,6 +14,7 @@
 import 'package:analyzer/src/dart/element/member.dart';
 import 'package:analyzer/src/dart/element/type_algebra.dart';
 import 'package:analyzer/src/dart/resolver/variance.dart';
+import 'package:analyzer/src/macro/impl/error.dart' as macro;
 import 'package:analyzer/src/summary2/ast_binary_tag.dart';
 import 'package:analyzer/src/summary2/ast_binary_writer.dart';
 import 'package:analyzer/src/summary2/data_writer.dart';
@@ -129,6 +130,11 @@
     _sink._writeStringReference(element.name);
     ClassElementFlags.write(_sink, element);
 
+    _writeList(
+      element.macroExecutionErrors,
+      _sink._writeMacroExecutionError,
+    );
+
     _resolutionSink._writeAnnotationList(element.metadata);
 
     _writeTypeParameters(element.typeParameters, () {
@@ -974,6 +980,12 @@
     }
   }
 
+  void _writeMacroExecutionError(macro.MacroExecutionError error) {
+    writeUInt30(error.annotationIndex);
+    _writeStringReference(error.macroName);
+    _writeStringReference(error.message);
+  }
+
   void _writeOptionalStringReference(String? value) {
     if (value != null) {
       writeBool(true);
diff --git a/pkg/analyzer/lib/src/summary2/library_builder.dart b/pkg/analyzer/lib/src/summary2/library_builder.dart
index 8cadf5b..4b894a9 100644
--- a/pkg/analyzer/lib/src/summary2/library_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/library_builder.dart
@@ -8,8 +8,10 @@
 import 'package:analyzer/src/dart/ast/mixin_super_invoked_names.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
+import 'package:analyzer/src/macro/api/macro.dart' as macro;
 import 'package:analyzer/src/macro/builders/data_class.dart' as macro;
 import 'package:analyzer/src/macro/builders/observable.dart' as macro;
+import 'package:analyzer/src/macro/impl/error.dart' as macro;
 import 'package:analyzer/src/macro/impl/macro.dart' as macro;
 import 'package:analyzer/src/summary2/combinator.dart';
 import 'package:analyzer/src/summary2/constructor_initializer_resolver.dart';
@@ -200,19 +202,25 @@
 
   /// Run built-in declaration macros.
   void runDeclarationMacros() {
-    bool hasMacroAnnotation(ast.AnnotatedNode node, String name) {
-      for (var annotation in node.metadata) {
+    /// If [node] has a macro annotation with the required [name],
+    /// return the index of this annotation node.
+    int? hasMacroAnnotation(ast.AnnotatedNode node, String name) {
+      var metadata = node.metadata;
+      for (var i = 0; i < metadata.length; i++) {
+        var annotation = metadata[i];
         var nameNode = annotation.name;
         if (nameNode is ast.SimpleIdentifier &&
             annotation.arguments == null &&
             annotation.constructorName == null &&
             nameNode.name == name) {
           var nameElement = element.scope.lookup(name).getter;
-          return nameElement != null &&
-              nameElement.library?.name == 'analyzer.macro.annotations';
+          if (nameElement != null &&
+              nameElement.library?.name == 'analyzer.macro.annotations') {
+            return i;
+          }
         }
       }
-      return false;
+      return null;
     }
 
     /// Build types for type annotations in new [nodes].
@@ -236,39 +244,53 @@
       for (var declaration in linkingUnit.node.declarations) {
         if (declaration is ast.ClassDeclarationImpl) {
           classDeclarationIndex++;
+          var macroExecutionErrors = <macro.MacroExecutionError>[];
+
           var members = declaration.members.toList();
           var classBuilder = macro.ClassDeclarationBuilderImpl(
             linkingUnit,
             classDeclarationIndex,
             declaration,
           );
-          if (hasMacroAnnotation(declaration, 'autoConstructor')) {
-            macro.AutoConstructorMacro().visitClassDeclaration(
-              declaration,
-              classBuilder,
-            );
+
+          void runClassMacro(
+            String name,
+            macro.ClassDeclarationMacro Function() newInstance,
+          ) {
+            var annotationIndex = hasMacroAnnotation(declaration, name);
+            if (annotationIndex != null) {
+              try {
+                newInstance().visitClassDeclaration(
+                  declaration,
+                  classBuilder,
+                );
+              } catch (e) {
+                macroExecutionErrors.add(
+                  macro.MacroExecutionError(
+                    annotationIndex: annotationIndex,
+                    macroName: name,
+                    message: e.toString(),
+                  ),
+                );
+              }
+            }
           }
-          if (hasMacroAnnotation(declaration, 'dataClass')) {
-            macro.DataClassMacro().visitClassDeclaration(
-              declaration,
-              classBuilder,
-            );
-          }
-          if (hasMacroAnnotation(declaration, 'hashCode')) {
-            macro.HashCodeMacro().visitClassDeclaration(
-              declaration,
-              classBuilder,
-            );
-          }
-          if (hasMacroAnnotation(declaration, 'toString')) {
-            macro.ToStringMacro().visitClassDeclaration(
-              declaration,
-              classBuilder,
-            );
-          }
+
+          runClassMacro('autoConstructor', () => macro.AutoConstructorMacro());
+          runClassMacro('dataClass', () => macro.DataClassMacro());
+          runClassMacro('hashCode', () => macro.HashCodeMacro());
+          runClassMacro('toString', () => macro.ToStringMacro());
+
+          var classElement = declaration.declaredElement as ClassElementImpl;
+          classElement.macroExecutionErrors = macroExecutionErrors;
+
           for (var member in members) {
             if (member is ast.FieldDeclarationImpl) {
-              if (hasMacroAnnotation(member, 'observable')) {
+              var annotationIndex = hasMacroAnnotation(
+                member,
+                'observable',
+              );
+              if (annotationIndex != null) {
                 macro.ObservableMacro().visitFieldDeclaration(
                   member,
                   classBuilder,
diff --git a/pkg/analyzer/test/src/dart/resolution/macro_test.dart b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
index b309301..a2e3aa4 100644
--- a/pkg/analyzer/test/src/dart/resolution/macro_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
@@ -101,6 +101,20 @@
     ]);
   }
 
+  test_executionError_autoConstructor() async {
+    await assertErrorsInCode(r'''
+import 'macro_annotations.dart';
+
+@autoConstructor
+class A {
+  final int a;
+  A(this.a);
+}
+''', [
+      error(CompileTimeErrorCode.MACRO_EXECUTION_ERROR, 34, 16),
+    ]);
+  }
+
   test_observable() async {
     var code = r'''
 import 'macro_annotations.dart';