Macro. Report MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE.
Bug: https://github.com/dart-lang/sdk/issues/54915
Change-Id: I0eb2d56b9fc4ad95c41de5681e22d55923845632
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/354382
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Phil Quitslund <pquitslund@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 05521a2..f02c0ec 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
@@ -1034,6 +1034,8 @@
status: noFix
CompileTimeErrorCode.MACRO_DECLARATIONS_PHASE_INTROSPECTION_CYCLE:
status: needsEvaluation
+CompileTimeErrorCode.MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE:
+ status: noFix
CompileTimeErrorCode.MACRO_APPLICATION_ARGUMENT_ERROR:
status: noFix
CompileTimeErrorCode.MACRO_ERROR:
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 7370037..ee94ca9 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -85,6 +85,7 @@
: _testingData = testingData,
_typeSystemOperations = typeSystemOperations {
_libraryVerificationContext = LibraryVerificationContext(
+ libraryKind: _library,
constructorFieldsVerifier: ConstructorFieldsVerifier(
typeSystem: _typeSystem,
),
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
index d9232b3..e80e760 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
@@ -26,6 +26,9 @@
/// The libraries that belong to this cycle.
final List<LibraryFileKind> libraries;
+ /// The URIs of [libraries].
+ final Set<Uri> libraryUris;
+
/// The library cycles that this cycle references directly.
final Set<LibraryCycle> directDependencies;
@@ -89,6 +92,7 @@
LibraryCycle({
required this.libraries,
+ required this.libraryUris,
required this.directDependencies,
required this.apiSignature,
required this.implSignature,
@@ -212,9 +216,11 @@
// Fill the cycle with libraries.
var libraries = <LibraryFileKind>[];
+ var libraryUris = <Uri>{};
for (var node in scc) {
final file = node.kind.file;
libraries.add(node.kind);
+ libraryUris.add(file.uri);
apiSignature.addLanguageVersion(file.packageLanguageVersion);
apiSignature.addString(file.uriStr);
@@ -241,6 +247,7 @@
// Create the LibraryCycle instance for the cycle.
var cycle = LibraryCycle(
libraries: libraries.toFixedList(),
+ libraryUris: libraryUris,
directDependencies: directDependencies,
apiSignature: apiSignature.toHex(),
implSignature: implSignature.toHex(),
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index a8d3543..ecd5caf 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -3055,6 +3055,18 @@
"Try removing one or more macro applications to break the cycle.",
);
+ /// Parameters:
+ /// 0: the name of the macro class
+ static const CompileTimeErrorCode
+ MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE = CompileTimeErrorCode(
+ 'MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE',
+ "The macro '{0}' can't be applied in the same library cycle where it is "
+ "defined.",
+ correctionMessage:
+ "Try moving it to a different library that does not import the one "
+ "where it is applied.",
+ );
+
/// Reported when the macro uses `Builder.report()` with `Severity.error`.
/// Parameters:
/// 0: the message
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 6cec47c..8b6b540 100644
--- a/pkg/analyzer/lib/src/error/error_code_values.g.dart
+++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart
@@ -325,6 +325,7 @@
CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,
CompileTimeErrorCode.MACRO_APPLICATION_ARGUMENT_ERROR,
CompileTimeErrorCode.MACRO_DECLARATIONS_PHASE_INTROSPECTION_CYCLE,
+ CompileTimeErrorCode.MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE,
CompileTimeErrorCode.MACRO_ERROR,
CompileTimeErrorCode.MACRO_INTERNAL_EXCEPTION,
CompileTimeErrorCode.MAIN_FIRST_POSITIONAL_PARAMETER_TYPE,
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index d014d6b..dfa2a41 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -54,6 +54,7 @@
import 'package:analyzer/src/generated/this_access_tracker.dart';
import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/summary2/macro_type_location.dart';
+import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:analyzer/src/utilities/extensions/object.dart';
import 'package:analyzer/src/utilities/extensions/string.dart';
import 'package:collection/collection.dart';
@@ -6113,10 +6114,12 @@
/// Information to pass from from the defining unit to augmentations.
class LibraryVerificationContext {
final duplicationDefinitionContext = DuplicationDefinitionContext();
+ final LibraryFileKind libraryKind;
final ConstructorFieldsVerifier constructorFieldsVerifier;
final Map<FileState, UnitAnalysis> units;
LibraryVerificationContext({
+ required this.libraryKind,
required this.constructorFieldsVerifier,
required this.units,
});
@@ -6148,6 +6151,10 @@
entity: node,
);
}
+
+ bool libraryCycleContains(Uri uri) {
+ return libraryKind.libraryCycle.libraryUris.contains(uri);
+ }
}
class _MacroDiagnosticsReporter {
@@ -6162,6 +6169,8 @@
});
void report() {
+ _reportApplicationFromSameLibraryCycle();
+
for (final diagnostic in element.macroDiagnostics) {
switch (diagnostic) {
case ArgumentMacroDiagnostic():
@@ -6182,7 +6191,7 @@
final target = object.target;
switch (target) {
case ApplicationMacroDiagnosticTarget():
- final node = _annotationNode(element, target.annotationIndex);
+ final node = element.annotationAst(target.annotationIndex);
return DiagnosticMessageImpl(
filePath: element.source!.fullName,
length: node.length,
@@ -6208,8 +6217,30 @@
}
}
+ void _reportApplicationFromSameLibraryCycle() {
+ for (var annotation in element.metadata) {
+ var element = annotation.element;
+ if (element is! ConstructorElementImpl) continue;
+
+ var macroElement = element.enclosingElement;
+ if (macroElement is! ClassElementImpl) continue;
+ if (!macroElement.isMacro) continue;
+
+ var macroUri = macroElement.library.source.uri;
+ if (!libraryContext.libraryCycleContains(macroUri)) continue;
+
+ errorReporter.atNode(
+ _annotationNameIdentifier(annotation),
+ CompileTimeErrorCode.MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE,
+ arguments: [
+ macroElement.name,
+ ],
+ );
+ }
+ }
+
void _reportArgument(ArgumentMacroDiagnostic diagnostic) {
- var annotation = _annotationNode(element, diagnostic.annotationIndex);
+ var annotation = element.annotationAst(diagnostic.annotationIndex);
var arguments = annotation.arguments!.arguments;
errorReporter.atNode(
arguments[diagnostic.argumentIndex],
@@ -6231,7 +6262,7 @@
final target = diagnostic.message.target;
switch (target) {
case ApplicationMacroDiagnosticTarget():
- var node = _annotationNode(element, target.annotationIndex);
+ var node = element.annotationAst(target.annotationIndex);
errorReporter.reportError(
AnalysisError.forValues(
source: element.source!,
@@ -6262,10 +6293,7 @@
if (location == null) {
return;
}
- var node = _annotationNode(
- target.element,
- target.annotationIndex,
- );
+ var node = target.element.annotationAst(target.annotationIndex);
location.unitAnalysis.errorReporter.reportError(
AnalysisError.forValues(
source: target.element.source!,
@@ -6301,7 +6329,7 @@
void _reportException(ExceptionMacroDiagnostic diagnostic) {
errorReporter.atNode(
- _annotationNode(element, diagnostic.annotationIndex),
+ element.annotationAst(diagnostic.annotationIndex),
CompileTimeErrorCode.MACRO_INTERNAL_EXCEPTION,
arguments: [
diagnostic.message,
@@ -6343,7 +6371,7 @@
void _reportInvalidTarget(InvalidMacroTargetDiagnostic diagnostic) {
errorReporter.atNode(
- _annotationNode(element, diagnostic.annotationIndex),
+ element.annotationAst(diagnostic.annotationIndex),
CompileTimeErrorCode.INVALID_MACRO_APPLICATION_TARGET,
arguments: [
diagnostic.supportedKinds.commaSeparatedWithOr,
@@ -6351,16 +6379,22 @@
);
}
- static AnnotationImpl _annotationNode(ElementImpl element, int index) {
- var annotation = element.metadata[index];
- return annotation.annotationAst;
+ static SimpleIdentifier _annotationNameIdentifier(
+ ElementAnnotationImpl annotation,
+ ) {
+ var fullName = annotation.annotationAst.name;
+ if (fullName is PrefixedIdentifierImpl) {
+ return fullName.identifier;
+ } else {
+ return fullName as SimpleIdentifierImpl;
+ }
}
static SimpleIdentifier _macroAnnotationNameIdentifier({
required ElementImpl element,
required int annotationIndex,
}) {
- var annotationNode = _annotationNode(element, annotationIndex);
+ var annotationNode = element.annotationAst(annotationIndex);
var fullName = annotationNode.name;
if (fullName is PrefixedIdentifierImpl) {
return fullName.identifier;
diff --git a/pkg/analyzer/lib/src/utilities/extensions/element.dart b/pkg/analyzer/lib/src/utilities/extensions/element.dart
index 21e2dff..228ceb6 100644
--- a/pkg/analyzer/lib/src/utilities/extensions/element.dart
+++ b/pkg/analyzer/lib/src/utilities/extensions/element.dart
@@ -5,6 +5,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
extension ElementExtension on Element {
@@ -27,6 +28,12 @@
}
}
+extension ElementImplExtension on ElementImpl {
+ AnnotationImpl annotationAst(int index) {
+ return metadata[index].annotationAst;
+ }
+}
+
extension ListOfTypeParameterElementExtension on List<TypeParameterElement> {
List<TypeParameterType> instantiateNone() {
return map((e) {
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 8d7638a..cad16a4 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -9550,6 +9550,12 @@
comment: |-
Parameters:
0: the name of the introspected declaration
+ MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE:
+ problemMessage: "The macro '{0}' can't be applied in the same library cycle where it is defined."
+ correctionMessage: Try moving it to a different library that does not import the one where it is applied.
+ comment: |-
+ Parameters:
+ 0: the name of the macro class
MAIN_FIRST_POSITIONAL_PARAMETER_TYPE:
problemMessage: "The type of the first positional parameter of the 'main' function must be a supertype of 'List<String>'."
correctionMessage: Try changing the type of the parameter.
diff --git a/pkg/analyzer/test/src/dart/resolution/macro_test.dart b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
index 7148e93..d1e9ac4 100644
--- a/pkg/analyzer/test/src/dart/resolution/macro_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
@@ -248,6 +248,53 @@
]);
}
+ test_diagnostic_definitionApplication_sameLibrary() async {
+ await assertErrorsInCode('''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassDefinitionMacro {
+ const MyMacro();
+
+ @override
+ buildDefinitionForClass(declaration, builder) async {}
+}
+
+@MyMacro()
+class A {}
+''', [
+ error(
+ CompileTimeErrorCode.MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE,
+ 206,
+ 7),
+ ]);
+ }
+
+ test_diagnostic_definitionApplication_sameLibraryCycle() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+import 'test.dart';
+
+macro class MyMacro implements ClassDefinitionMacro {
+ const MyMacro();
+
+ @override
+ buildDefinitionForClass(declaration, builder) async {}
+}
+''');
+
+ await assertErrorsInCode('''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', [
+ error(
+ CompileTimeErrorCode.MACRO_DEFINITION_APPLICATION_SAME_LIBRARY_CYCLE,
+ 19,
+ 7),
+ ]);
+ }
+
test_diagnostic_invalidTarget_wantsClassOrMixin_hasFunction() async {
await assertErrorsInCode('''
import 'diagnostic.dart';