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