Macro. Initial support for TypeAnnotationDiagnosticTarget.

Change-Id: I008203e570e64207f010e5c818b7a2e30c2e1001
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/349415
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
This commit is contained in:
Konstantin Shcheglov 2024-02-01 01:48:44 +00:00 committed by Commit Queue
parent 12841a6e29
commit 996a51c9bb
14 changed files with 1325 additions and 230 deletions

View file

@ -74,6 +74,7 @@ class LibraryAnalyzer {
final Map<FileState, LineInfo> _fileToLineInfo = {};
final Map<FileState, CompilationUnitImpl> _libraryUnits = {};
final Map<FileState, IgnoreInfo> _fileToIgnoreInfo = {};
final Map<FileState, RecordingErrorListener> _errorListeners = {};
final Map<FileState, ErrorReporter> _errorReporters = {};
@ -92,6 +93,7 @@ class LibraryAnalyzer {
constructorFieldsVerifier: ConstructorFieldsVerifier(
typeSystem: _typeSystem,
),
units: _libraryUnits,
);
}
@ -101,12 +103,12 @@ class LibraryAnalyzer {
/// Compute analysis results for all units of the library.
List<UnitAnalysisResult> analyze() {
var units = _parseAndResolve();
_computeDiagnostics(units);
_parseAndResolve();
_computeDiagnostics();
// Return full results.
var results = <UnitAnalysisResult>[];
units.forEach((file, unit) {
_libraryUnits.forEach((file, unit) {
var errors = _getErrorListener(file).errors;
errors = _filterIgnoredErrors(file, errors);
results.add(UnitAnalysisResult(file, unit, errors));
@ -186,8 +188,8 @@ class LibraryAnalyzer {
}
}
var units = _parseAndResolve();
var unit = units[file]!;
_parseAndResolve();
var unit = _libraryUnits[file]!;
return AnalysisForCompletionResult(
parsedUnit: unit,
resolvedNodes: [unit],
@ -268,10 +270,10 @@ class LibraryAnalyzer {
);
}
/// Compute diagnostics in [units], including errors and warnings,
/// Compute diagnostics in [_libraryUnits], including errors and warnings,
/// lints, and a few other cases.
void _computeDiagnostics(Map<FileState, CompilationUnitImpl> units) {
units.forEach((file, unit) {
void _computeDiagnostics() {
_libraryUnits.forEach((file, unit) {
_computeVerifyErrors(file, unit);
});
@ -280,7 +282,7 @@ class LibraryAnalyzer {
if (_analysisOptions.warning) {
var usedImportedElements = <UsedImportedElements>[];
var usedLocalElements = <UsedLocalElements>[];
for (var unit in units.values) {
for (var unit in _libraryUnits.values) {
{
var visitor = GatherUsedLocalElementsVisitor(_libraryElement);
unit.accept(visitor);
@ -293,7 +295,7 @@ class LibraryAnalyzer {
}
}
var usedElements = UsedLocalElements.merge(usedLocalElements);
units.forEach((file, unit) {
_libraryUnits.forEach((file, unit) {
_computeWarnings(
file,
unit,
@ -306,7 +308,7 @@ class LibraryAnalyzer {
if (_analysisOptions.lint) {
final allUnits = _library.files
.map((file) {
final unit = units[file];
final unit = _libraryUnits[file];
if (unit != null) {
return LinterContextUnit2(file, unit);
} else {
@ -321,7 +323,7 @@ class LibraryAnalyzer {
}
}
_checkForInconsistentLanguageVersionOverride(units);
_checkForInconsistentLanguageVersionOverride(_libraryUnits);
// This must happen after all other diagnostics have been computed but
// before the list of diagnostics has been filtered.
@ -576,21 +578,17 @@ class LibraryAnalyzer {
}
/// Parse and resolve all files in [_library].
Map<FileState, CompilationUnitImpl> _parseAndResolve() {
final units = <FileState, CompilationUnitImpl>{};
void _parseAndResolve() {
_resolveDirectives(
containerKind: _library,
containerElement: _libraryElement,
units: units,
);
units.forEach((file, unit) {
_libraryUnits.forEach((file, unit) {
_resolveFile(file, unit);
});
_computeConstants(units.values);
return units;
_computeConstants(_libraryUnits.values);
}
void _resolveAugmentationImportDirective({
@ -599,7 +597,6 @@ class LibraryAnalyzer {
required AugmentationImportState state,
required ErrorReporter errorReporter,
required Set<AugmentationFileKind> seenAugmentations,
required Map<FileState, CompilationUnitImpl> units,
}) {
directive?.element = element;
@ -655,7 +652,7 @@ class LibraryAnalyzer {
final augmentationFile = importedAugmentationKind.file;
final augmentationUnit = _parse(augmentationFile);
units[augmentationFile] = augmentationUnit;
_libraryUnits[augmentationFile] = augmentationUnit;
final importedAugmentation = element.importedAugmentation!;
augmentationUnit.declaredElement =
@ -670,7 +667,6 @@ class LibraryAnalyzer {
_resolveDirectives(
containerKind: importedAugmentationKind,
containerElement: importedAugmentation,
units: units,
);
}
@ -679,12 +675,11 @@ class LibraryAnalyzer {
void _resolveDirectives({
required LibraryOrAugmentationFileKind containerKind,
required LibraryOrAugmentationElementImpl containerElement,
required Map<FileState, CompilationUnitImpl> units,
}) {
final containerFile = containerKind.file;
final containerUnit = _parse(containerFile);
containerUnit.declaredElement = containerElement.definingCompilationUnit;
units[containerFile] = containerUnit;
_libraryUnits[containerFile] = containerUnit;
final containerErrorReporter = _getErrorReporter(containerFile);
@ -705,7 +700,6 @@ class LibraryAnalyzer {
state: containerKind.augmentationImports[index],
errorReporter: containerErrorReporter,
seenAugmentations: seenAugmentations,
units: units,
);
} else if (directive is ExportDirectiveImpl) {
final index = libraryExportIndex++;
@ -743,7 +737,6 @@ class LibraryAnalyzer {
partElement: containerElement.parts[index],
errorReporter: containerErrorReporter,
libraryNameNode: libraryNameNode,
units: units,
seenPartSources: seenPartSources,
);
}
@ -762,7 +755,6 @@ class LibraryAnalyzer {
state: macroImport,
errorReporter: containerErrorReporter,
seenAugmentations: seenAugmentations,
units: units,
);
}
}
@ -984,7 +976,6 @@ class LibraryAnalyzer {
required PartElement partElement,
required ErrorReporter errorReporter,
required LibraryIdentifier? libraryNameNode,
required Map<FileState, CompilationUnitImpl> units,
required Set<Source> seenPartSources,
}) {
StringLiteral partUri = directive.uri;
@ -1065,7 +1056,7 @@ class LibraryAnalyzer {
}
final partUnit = _parse(includedFile);
units[includedFile] = partUnit;
_libraryUnits[includedFile] = partUnit;
final partElementUri = partElement.uri;
if (partElementUri is DirectiveUriWithUnitImpl) {

View file

@ -25,6 +25,122 @@ abstract class AnalysisResultImpl implements AnalysisResult {
});
}
/// A visitor which locates the [AstNode] which declares [element].
class DeclarationByElementLocator extends UnifyingAstVisitor<void> {
// TODO(srawlins): This visitor could be further optimized by special casing each static
// type of [element]. For example, for library-level elements (classes etc),
// we can iterate over the compilation unit's declarations.
final Element element;
final int _nameOffset;
AstNode? result;
DeclarationByElementLocator(this.element) : _nameOffset = element.nameOffset;
@override
void visitNode(AstNode node) {
if (result != null) return;
if (node.endToken.end < _nameOffset || node.offset > _nameOffset) {
return;
}
if (element is InterfaceElement) {
if (node is ClassDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is ClassTypeAlias) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is EnumDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is MixinDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is ExtensionTypeDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
}
} else if (element is ConstructorElement) {
if (node is ConstructorDeclaration) {
if (node.name != null) {
if (_hasOffset2(node.name)) {
result = node;
}
} else {
if (_hasOffset(node.returnType)) {
result = node;
}
}
}
} else if (element is ExtensionElement) {
if (node is ExtensionDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
}
} else if (element is FieldElement) {
if (node is EnumConstantDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is VariableDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
}
} else if (element is FunctionElement) {
if (node is FunctionDeclaration && _hasOffset2(node.name)) {
result = node;
}
} else if (element is LocalVariableElement) {
if (node is VariableDeclaration && _hasOffset2(node.name)) {
result = node;
}
} else if (element is MethodElement) {
if (node is MethodDeclaration && _hasOffset2(node.name)) {
result = node;
}
} else if (element is ParameterElement) {
if (node is FormalParameter && _hasOffset2(node.name)) {
result = node;
}
} else if (element is PropertyAccessorElement) {
if (node is FunctionDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is MethodDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
}
} else if (element is TopLevelVariableElement) {
if (node is VariableDeclaration && _hasOffset2(node.name)) {
result = node;
}
}
if (result == null) {
node.visitChildren(this);
}
}
bool _hasOffset(AstNode? node) {
return node?.offset == _nameOffset;
}
bool _hasOffset2(Token? token) {
return token?.offset == _nameOffset;
}
}
class ElementDeclarationResultImpl implements ElementDeclarationResult {
@override
final Element element;
@ -175,7 +291,7 @@ class ParsedLibraryResultImpl extends AnalysisResultImpl
},
);
var locator = _DeclarationByElementLocator(element);
var locator = DeclarationByElementLocator(element);
unitResult.unit.accept(locator);
var declaration = locator.result;
@ -298,7 +414,7 @@ class ResolvedLibraryResultImpl extends AnalysisResultImpl
},
);
var locator = _DeclarationByElementLocator(element);
var locator = DeclarationByElementLocator(element);
unitResult.unit.accept(locator);
var declaration = locator.result;
@ -361,119 +477,3 @@ class UnitElementResultImpl extends FileResultImpl
required this.element,
});
}
/// A visitor which locates the [AstNode] which declares [element].
class _DeclarationByElementLocator extends UnifyingAstVisitor<void> {
// TODO(srawlins): This visitor could be further optimized by special casing each static
// type of [element]. For example, for library-level elements (classes etc),
// we can iterate over the compilation unit's declarations.
final Element element;
final int _nameOffset;
AstNode? result;
_DeclarationByElementLocator(this.element) : _nameOffset = element.nameOffset;
@override
void visitNode(AstNode node) {
if (result != null) return;
if (node.endToken.end < _nameOffset || node.offset > _nameOffset) {
return;
}
if (element is InterfaceElement) {
if (node is ClassDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is ClassTypeAlias) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is EnumDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is MixinDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is ExtensionTypeDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
}
} else if (element is ConstructorElement) {
if (node is ConstructorDeclaration) {
if (node.name != null) {
if (_hasOffset2(node.name)) {
result = node;
}
} else {
if (_hasOffset(node.returnType)) {
result = node;
}
}
}
} else if (element is ExtensionElement) {
if (node is ExtensionDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
}
} else if (element is FieldElement) {
if (node is EnumConstantDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is VariableDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
}
} else if (element is FunctionElement) {
if (node is FunctionDeclaration && _hasOffset2(node.name)) {
result = node;
}
} else if (element is LocalVariableElement) {
if (node is VariableDeclaration && _hasOffset2(node.name)) {
result = node;
}
} else if (element is MethodElement) {
if (node is MethodDeclaration && _hasOffset2(node.name)) {
result = node;
}
} else if (element is ParameterElement) {
if (node is FormalParameter && _hasOffset2(node.name)) {
result = node;
}
} else if (element is PropertyAccessorElement) {
if (node is FunctionDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
} else if (node is MethodDeclaration) {
if (_hasOffset2(node.name)) {
result = node;
}
}
} else if (element is TopLevelVariableElement) {
if (node is VariableDeclaration && _hasOffset2(node.name)) {
result = node;
}
}
if (result == null) {
node.visitChildren(this);
}
}
bool _hasOffset(AstNode? node) {
return node?.offset == _nameOffset;
}
bool _hasOffset2(Token? token) {
return token?.offset == _nameOffset;
}
}

View file

@ -17,6 +17,8 @@ import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/analysis/file_state.dart';
import 'package:analyzer/src/dart/analysis/results.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/class_hierarchy.dart';
@ -50,8 +52,10 @@ import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
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/object.dart';
import 'package:analyzer/src/utilities/extensions/string.dart';
import 'package:collection/collection.dart';
class EnclosingExecutableContext {
final ExecutableElement? element;
@ -848,13 +852,13 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
ExecutableElement functionElement = node.declaredElement!;
if (functionElement.enclosingElement is! CompilationUnitElement) {
_hiddenElements!.declare(functionElement);
void visitFunctionDeclaration(covariant FunctionDeclarationImpl node) {
var element = node.declaredElement!;
if (element.enclosingElement is! CompilationUnitElement) {
_hiddenElements!.declare(element);
}
_withEnclosingExecutable(functionElement, () {
_withEnclosingExecutable(element, () {
TypeAnnotation? returnType = node.returnType;
if (node.isSetter) {
FunctionExpression functionExpression = node.functionExpression;
@ -866,6 +870,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
_returnTypeVerifier.verifyReturnType(returnType);
_checkForMainFunction1(node.name, node.declaredElement!);
_checkForMainFunction2(node);
_reportMacroDiagnostics(element, node.metadata);
super.visitFunctionDeclaration(node);
});
}
@ -5928,6 +5933,73 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
MacroTargetElement element,
List<Annotation> metadata,
) {
AstNode? locationNode(TypeAnnotationLocation location) {
switch (location) {
case ElementTypeLocation():
var element = location.element;
return libraryVerificationContext.declarationByElement(element);
case FormalParameterTypeLocation():
var node = locationNode(location.parent);
switch (node) {
case FunctionDeclaration():
var parameterList = node.functionExpression.parameters;
return parameterList!.parameters[location.index];
case MethodDeclaration():
var parameterList = node.parameters;
return parameterList!.parameters[location.index];
default:
throw UnimplementedError('${node.runtimeType}');
}
case ListIndexTypeLocation():
var node = locationNode(location.parent);
switch (node) {
case NamedType():
return node.typeArguments?.arguments[location.index];
default:
throw UnimplementedError('${node.runtimeType}');
}
case ReturnTypeLocation():
var node = locationNode(location.parent);
switch (node) {
case FunctionDeclaration():
return node.returnType;
case GenericFunctionType():
return node.returnType;
case MethodDeclaration():
return node.returnType;
default:
throw UnimplementedError('${node.runtimeType}');
}
case VariableTypeLocation():
var node = locationNode(location.parent);
if (node is DefaultFormalParameter) {
node = node.parameter;
}
var parent = node?.parent;
switch (node) {
case SimpleFormalParameter():
return node.type;
case VariableDeclaration():
if (parent is VariableDeclarationList) {
return parent.type;
}
}
throw UnimplementedError(
'${node.runtimeType} ${parent.runtimeType}',
);
case ExtendsClauseTypeLocation():
var node = locationNode(location.parent);
switch (node) {
case ClassDeclaration():
return node.extendsClause!.superclass;
default:
throw UnimplementedError('${node.runtimeType}');
}
default:
throw UnimplementedError('${location.runtimeType}');
}
}
DiagnosticMessage convertMessage(MacroDiagnosticMessage object) {
final target = object.target;
switch (target) {
@ -5949,6 +6021,9 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
offset: element.nameOffset,
url: null,
);
case TypeAnnotationMacroDiagnosticTarget():
// TODO(scheglov): Handle this case.
throw UnimplementedError();
}
}
@ -6009,6 +6084,14 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
[diagnostic.message.message],
diagnostic.contextMessages.map(convertMessage).toList(),
);
case TypeAnnotationMacroDiagnosticTarget():
var errorNode = locationNode(target.location)!;
errorReporter.reportErrorForNode(
errorCode,
errorNode,
[diagnostic.message.message],
diagnostic.contextMessages.map(convertMessage).toList(),
);
}
}
}
@ -6155,10 +6238,30 @@ class HiddenElements {
class LibraryVerificationContext {
final duplicationDefinitionContext = DuplicationDefinitionContext();
final ConstructorFieldsVerifier constructorFieldsVerifier;
final Map<FileState, CompilationUnitImpl> units;
LibraryVerificationContext({
required this.constructorFieldsVerifier,
required this.units,
});
AstNode? declarationByElement(Element element) {
var uri = element.library?.source.uri;
if (uri == null) {
return null;
}
var unit = units.entries.firstWhereOrNull((entry) {
return entry.key.uri == uri;
})?.value;
if (unit == null) {
return null;
}
var locator = DeclarationByElementLocator(element);
unit.accept(locator);
return locator.result;
}
}
/// Recursively visits a type annotation, looking uninstantiated bounds.

View file

@ -135,6 +135,15 @@ class Tag {
static const int VoidType = 13;
}
enum TypeAnnotationLocationKind {
element,
extendsClause,
formalParameter,
listIndex,
returnType,
variableType,
}
enum TypeParameterVarianceTag {
legacy,
unrelated,

View file

@ -30,6 +30,7 @@ import 'package:analyzer/src/summary2/export.dart';
import 'package:analyzer/src/summary2/informative_data.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/summary2/macro_type_location.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/task/inference_error.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
@ -471,6 +472,7 @@ class FunctionElementLinkedData extends ElementLinkedData<FunctionElementImpl> {
element.metadata = reader._readAnnotationList(
unitElement: unitElement,
);
element.macroDiagnostics = reader.readMacroDiagnostics();
_readTypeParameters(reader, element.typeParameters);
element.returnType = reader.readRequiredType();
_readFormalParameters(reader, element.parameters);
@ -2367,6 +2369,38 @@ class ResolutionReader {
MacroDiagnosticMessage _readMacroDiagnosticMessage() {
final message = _reader.readStringUtf8();
TypeAnnotationLocation readTypeAnnotationLocation() {
var tag = readByte();
var kind = TypeAnnotationLocationKind.values[tag];
switch (kind) {
case TypeAnnotationLocationKind.element:
var element = readElement()!;
return ElementTypeLocation(element);
case TypeAnnotationLocationKind.extendsClause:
var parent = readTypeAnnotationLocation();
return ExtendsClauseTypeLocation(parent);
case TypeAnnotationLocationKind.formalParameter:
return FormalParameterTypeLocation(
readTypeAnnotationLocation(),
readUInt30(),
);
case TypeAnnotationLocationKind.listIndex:
return ListIndexTypeLocation(
readTypeAnnotationLocation(),
readUInt30(),
);
case TypeAnnotationLocationKind.returnType:
var parent = readTypeAnnotationLocation();
return ReturnTypeLocation(parent);
case TypeAnnotationLocationKind.variableType:
var parent = readTypeAnnotationLocation();
return VariableTypeLocation(parent);
default:
throw UnimplementedError('kind: $kind');
}
}
MacroDiagnosticTarget target;
switch (readByte()) {
case 0x00:
@ -2378,6 +2412,9 @@ class ResolutionReader {
target = ElementMacroDiagnosticTarget(
element: element as ElementImpl,
);
case 0x02:
var location = readTypeAnnotationLocation();
target = TypeAnnotationMacroDiagnosticTarget(location: location);
case final int tag:
throw UnimplementedError('tag: $tag');
}

View file

@ -22,6 +22,7 @@ import 'package:analyzer/src/summary2/data_writer.dart';
import 'package:analyzer/src/summary2/element_flags.dart';
import 'package:analyzer/src/summary2/export.dart';
import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/summary2/macro_type_location.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/task/inference_error.dart';
@ -416,6 +417,7 @@ class BundleWriter {
FunctionElementFlags.write(_sink, element);
_resolutionSink._writeAnnotationList(element.metadata);
_resolutionSink.writeMacroDiagnostics(element.macroDiagnostics);
_writeTypeParameters(element.typeParameters, () {
_resolutionSink.writeType(element.returnType);
@ -978,6 +980,35 @@ class ResolutionSink extends _SummaryDataWriter {
void _writeMacroDiagnosticMessage(MacroDiagnosticMessage object) {
writeStringUtf8(object.message);
void writeTypeAnnotationLocation(TypeAnnotationLocation location) {
switch (location) {
case ElementTypeLocation():
writeByte(TypeAnnotationLocationKind.element.index);
writeElement(location.element);
case ExtendsClauseTypeLocation():
writeByte(TypeAnnotationLocationKind.extendsClause.index);
writeTypeAnnotationLocation(location.parent);
case FormalParameterTypeLocation():
writeByte(TypeAnnotationLocationKind.formalParameter.index);
writeTypeAnnotationLocation(location.parent);
writeUInt30(location.index);
case ListIndexTypeLocation():
writeByte(TypeAnnotationLocationKind.listIndex.index);
writeTypeAnnotationLocation(location.parent);
writeUInt30(location.index);
case ReturnTypeLocation():
writeByte(TypeAnnotationLocationKind.returnType.index);
writeTypeAnnotationLocation(location.parent);
case VariableTypeLocation():
writeByte(TypeAnnotationLocationKind.variableType.index);
writeTypeAnnotationLocation(location.parent);
default:
// TODO(scheglov): Handle this case.
throw UnimplementedError('${location.runtimeType}');
}
}
final target = object.target;
switch (target) {
case ApplicationMacroDiagnosticTarget():
@ -986,6 +1017,9 @@ class ResolutionSink extends _SummaryDataWriter {
case ElementMacroDiagnosticTarget():
writeByte(0x01);
writeElement(target.element);
case TypeAnnotationMacroDiagnosticTarget():
writeByte(0x02);
writeTypeAnnotationLocation(target.location);
}
}

View file

@ -527,7 +527,9 @@ class LibraryMacroApplier {
case macro.DeclarationDiagnosticTarget macroTarget:
final element = (macroTarget.declaration as HasElement).element;
target = ElementMacroDiagnosticTarget(element: element);
default:
case macro.TypeAnnotationDiagnosticTarget macroTarget:
target = _typeAnnotationTarget(application, macroTarget);
case null:
target = ApplicationMacroDiagnosticTarget(
annotationIndex: application.annotationIndex,
);
@ -713,6 +715,23 @@ class LibraryMacroApplier {
return null;
}
MacroDiagnosticTarget _typeAnnotationTarget(
_MacroApplication application,
macro.TypeAnnotationDiagnosticTarget macroTarget,
) {
switch (macroTarget.typeAnnotation) {
case NamedTypeAnnotation typeAnnotation:
return TypeAnnotationMacroDiagnosticTarget(
location: typeAnnotation.location,
);
}
// We don't know anything better.
return ApplicationMacroDiagnosticTarget(
annotationIndex: application.annotationIndex,
);
}
static macro.Arguments _buildArguments({
required int annotationIndex,
required ConstructorElement constructor,

View file

@ -4,6 +4,7 @@
import 'package:_fe_analyzer_shared/src/macros/api.dart' as macro;
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/summary2/macro_type_location.dart';
/// Base for all macro related diagnostics.
sealed class AnalyzerMacroDiagnostic {}
@ -100,3 +101,11 @@ final class MacroDiagnosticMessage {
}
sealed class MacroDiagnosticTarget {}
final class TypeAnnotationMacroDiagnosticTarget extends MacroDiagnosticTarget {
final TypeAnnotationLocation location;
TypeAnnotationMacroDiagnosticTarget({
required this.location,
});
}

View file

@ -18,6 +18,7 @@ import 'package:analyzer/src/dart/ast/ast.dart' as ast;
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/summary2/macro_type_location.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:collection/collection.dart';
@ -1040,13 +1041,18 @@ class DeclarationBuilderFromNode {
current = nextNode;
}
var classTypeLocation = ElementTypeLocation(element);
return ClassDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(element),
typeParameters: _typeParameters(node.typeParameters),
interfaces: _namedTypes(interfaceNodes),
interfaces: _namedTypes(
interfaceNodes,
ImplementsClauseTypeLocation(classTypeLocation),
),
hasAbstract: node.abstractKeyword != null,
hasBase: node.baseKeyword != null,
hasExternal: false,
@ -1054,8 +1060,16 @@ class DeclarationBuilderFromNode {
hasInterface: node.interfaceKeyword != null,
hasMixin: node.mixinKeyword != null,
hasSealed: node.sealedKeyword != null,
mixins: _namedTypes(mixinNodes),
superclass: node.extendsClause?.superclass.mapOrNull(_namedType),
mixins: _namedTypes(
mixinNodes,
WithClauseTypeLocation(classTypeLocation),
),
superclass: node.extendsClause?.superclass.mapOrNull((type) {
return _namedType(
type,
ExtendsClauseTypeLocation(classTypeLocation),
);
}),
element: element,
);
}
@ -1080,13 +1094,18 @@ class DeclarationBuilderFromNode {
current = nextNode;
}
var classTypeLocation = ElementTypeLocation(element);
return ClassDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(element),
typeParameters: _typeParameters(node.typeParameters),
interfaces: _namedTypes(interfaceNodes),
interfaces: _namedTypes(
interfaceNodes,
ImplementsClauseTypeLocation(classTypeLocation),
),
hasAbstract: node.abstractKeyword != null,
hasBase: node.baseKeyword != null,
hasExternal: false,
@ -1094,8 +1113,16 @@ class DeclarationBuilderFromNode {
hasInterface: node.interfaceKeyword != null,
hasMixin: node.mixinKeyword != null,
hasSealed: node.sealedKeyword != null,
mixins: _namedTypes(mixinNodes),
superclass: node.superclass.mapOrNull(_namedType),
mixins: _namedTypes(
mixinNodes,
WithClauseTypeLocation(classTypeLocation),
),
superclass: node.superclass.mapOrNull((type) {
return _namedType(
type,
ExtendsClauseTypeLocation(classTypeLocation),
);
}),
element: element,
);
}
@ -1106,6 +1133,9 @@ class DeclarationBuilderFromNode {
final definingType = _definingType(node);
final element = node.declaredElement!;
var (namedParameters, positionalParameters) =
_executableFormalParameters(element, node.parameters);
return ConstructorDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
definingType: definingType,
@ -1116,8 +1146,8 @@ class DeclarationBuilderFromNode {
hasBody: node.body is! ast.EmptyFunctionBody,
hasExternal: node.externalKeyword != null,
isFactory: node.factoryKeyword != null,
namedParameters: _namedFormalParameters(node.parameters),
positionalParameters: _positionalFormalParameters(node.parameters),
namedParameters: namedParameters,
positionalParameters: positionalParameters,
returnType: macro.NamedTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: definingType,
@ -1177,14 +1207,22 @@ class DeclarationBuilderFromNode {
current = nextNode;
}
var enumTypeLocation = ElementTypeLocation(element);
return EnumDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(element),
typeParameters: _typeParameters(node.typeParameters),
interfaces: _namedTypes(interfaceNodes),
mixins: _namedTypes(mixinNodes),
interfaces: _namedTypes(
interfaceNodes,
ImplementsClauseTypeLocation(enumTypeLocation),
),
mixins: _namedTypes(
mixinNodes,
WithClauseTypeLocation(enumTypeLocation),
),
element: element,
);
}
@ -1200,7 +1238,10 @@ class DeclarationBuilderFromNode {
library: library(element),
metadata: _buildMetadata(element),
typeParameters: _typeParameters(node.typeParameters),
onType: _typeAnnotation(node.extendedType),
onType: _typeAnnotation(
node.extendedType,
ExtensionElementOnTypeLocation(element),
),
element: element,
);
}
@ -1216,7 +1257,10 @@ class DeclarationBuilderFromNode {
library: library(element),
metadata: _buildMetadata(element),
typeParameters: _typeParameters(node.typeParameters),
representationType: _typeAnnotation(node.representation.fieldType),
representationType: _typeAnnotation(
node.representation.fieldType,
ExtensionTypeElementRepresentationTypeLocation(element),
),
element: element,
);
}
@ -1227,6 +1271,9 @@ class DeclarationBuilderFromNode {
final element = node.declaredElement!;
final function = node.functionExpression;
var (namedParameters, positionalParameters) =
_executableFormalParameters(element, function.parameters);
return FunctionDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
element: element,
@ -1238,8 +1285,8 @@ class DeclarationBuilderFromNode {
isGetter: node.isGetter,
isOperator: false,
isSetter: node.isSetter,
namedParameters: _namedFormalParameters(function.parameters),
positionalParameters: _positionalFormalParameters(function.parameters),
namedParameters: namedParameters,
positionalParameters: positionalParameters,
returnType: _typeAnnotationFunctionReturnType(node),
typeParameters: _typeParameters(function.typeParameters),
);
@ -1303,6 +1350,8 @@ class DeclarationBuilderFromNode {
current = nextNode;
}
var mixinTypeLocation = ElementTypeLocation(element);
return MixinDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: _declaredIdentifier(node.name, element),
@ -1310,8 +1359,14 @@ class DeclarationBuilderFromNode {
metadata: _buildMetadata(element),
typeParameters: _typeParameters(node.typeParameters),
hasBase: node.baseKeyword != null,
interfaces: _namedTypes(interfaceNodes),
superclassConstraints: _namedTypes(onNodes),
interfaces: _namedTypes(
interfaceNodes,
ImplementsClauseTypeLocation(mixinTypeLocation),
),
superclassConstraints: _namedTypes(
onNodes,
OnClauseTypeLocation(mixinTypeLocation),
),
element: element,
);
}
@ -1329,7 +1384,11 @@ class DeclarationBuilderFromNode {
hasExternal: false,
hasFinal: element.isFinal,
hasLate: element.isLate,
type: _typeAnnotationVariable(node.fieldType, element),
type: _typeAnnotationVariable(
node.fieldType,
element,
ElementTypeLocation(element),
),
definingType: _definingType(node),
isStatic: element.isStatic,
element: element,
@ -1381,7 +1440,11 @@ class DeclarationBuilderFromNode {
hasExternal: variablesDeclaration.externalKeyword != null,
hasFinal: element.isFinal,
hasLate: element.isLate,
type: _typeAnnotationVariable(variableList.type, element),
type: _typeAnnotationVariable(
variableList.type,
element,
ElementTypeLocation(element),
),
definingType: _definingType(variablesDeclaration),
isStatic: element.isStatic,
element: element,
@ -1396,7 +1459,11 @@ class DeclarationBuilderFromNode {
hasExternal: element.isExternal,
hasFinal: element.isFinal,
hasLate: element.isLate,
type: _typeAnnotationVariable(variableList.type, element),
type: _typeAnnotationVariable(
variableList.type,
element,
ElementTypeLocation(element),
),
element: element,
);
default:
@ -1471,7 +1538,34 @@ class DeclarationBuilderFromNode {
);
}
macro.ParameterDeclarationImpl _formalParameter(ast.FormalParameter node) {
(List<macro.ParameterDeclarationImpl>, List<macro.ParameterDeclarationImpl>)
_executableFormalParameters(
ExecutableElement element,
ast.FormalParameterList? node,
) {
var named = <macro.ParameterDeclarationImpl>[];
var positional = <macro.ParameterDeclarationImpl>[];
if (node != null) {
var elementLocation = ElementTypeLocation(element);
for (var (index, node) in node.parameters.indexed) {
var formalParameter = _formalParameter(
node,
FormalParameterTypeLocation(elementLocation, index),
);
if (node.isNamed) {
named.add(formalParameter);
} else {
positional.add(formalParameter);
}
}
}
return (named, positional);
}
macro.ParameterDeclarationImpl _formalParameter(
ast.FormalParameter node,
TypeAnnotationLocation location,
) {
if (node is ast.DefaultFormalParameter) {
node = node.parameter;
}
@ -1481,9 +1575,9 @@ class DeclarationBuilderFromNode {
final macro.TypeAnnotationImpl typeAnnotation;
switch (node) {
case ast.FieldFormalParameter():
typeAnnotation = _typeAnnotationVariable(node.type, element);
typeAnnotation = _typeAnnotationVariable(node.type, element, location);
case ast.SimpleFormalParameter():
typeAnnotation = _typeAnnotationVariable(node.type, element);
typeAnnotation = _typeAnnotationVariable(node.type, element, location);
default:
throw UnimplementedError('(${node.runtimeType}) $node');
}
@ -1501,6 +1595,7 @@ class DeclarationBuilderFromNode {
macro.FunctionTypeParameterImpl _functionTypeFormalParameter(
ast.FormalParameter node,
TypeAnnotationLocation location,
) {
if (node is ast.DefaultFormalParameter) {
node = node.parameter;
@ -1510,7 +1605,7 @@ class DeclarationBuilderFromNode {
final macro.TypeAnnotationImpl typeAnnotation;
if (node is ast.SimpleFormalParameter) {
typeAnnotation = _typeAnnotationOrDynamic(node.type);
typeAnnotation = _typeAnnotationOrDynamic(node.type, location);
} else {
throw UnimplementedError('(${node.runtimeType}) $node');
}
@ -1531,6 +1626,9 @@ class DeclarationBuilderFromNode {
final definingType = _definingType(node);
final element = node.declaredElement!;
var (namedParameters, positionalParameters) =
_executableFormalParameters(element, node.parameters);
return MethodDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
definingType: definingType,
@ -1544,32 +1642,26 @@ class DeclarationBuilderFromNode {
isOperator: node.isOperator,
isSetter: node.isSetter,
isStatic: node.isStatic,
namedParameters: _namedFormalParameters(node.parameters),
positionalParameters: _positionalFormalParameters(node.parameters),
namedParameters: namedParameters,
positionalParameters: positionalParameters,
returnType: _typeAnnotationMethodReturnType(node),
typeParameters: _typeParameters(node.typeParameters),
);
}
List<macro.ParameterDeclarationImpl> _namedFormalParameters(
ast.FormalParameterList? node,
macro.NamedTypeAnnotationImpl _namedType(
ast.NamedType node,
TypeAnnotationLocation location,
) {
if (node != null) {
return node.parameters
.where((e) => e.isNamed)
.map(_formalParameter)
.toList();
} else {
return const [];
}
}
macro.NamedTypeAnnotationImpl _namedType(ast.NamedType node) {
return macro.NamedTypeAnnotationImpl(
return NamedTypeAnnotation(
id: macro.RemoteInstance.uniqueId,
identifier: _namedTypeIdentifier(node),
isNullable: node.question != null,
typeArguments: _typeAnnotations(node.typeArguments?.arguments),
typeArguments: _typeAnnotations(
node.typeArguments?.arguments,
location,
),
location: location,
);
}
@ -1587,47 +1679,55 @@ class DeclarationBuilderFromNode {
List<macro.NamedTypeAnnotationImpl> _namedTypes(
List<ast.NamedType>? elements,
TypeAnnotationLocation location,
) {
if (elements != null) {
return elements.map(_namedType).toList();
return elements.indexed.map((pair) {
return _namedType(
pair.$2,
ListIndexTypeLocation(location, pair.$1),
);
}).toList();
} else {
return const [];
}
}
List<macro.ParameterDeclarationImpl> _positionalFormalParameters(
ast.FormalParameterList? node,
macro.TypeAnnotationImpl _typeAnnotation(
ast.TypeAnnotation node,
TypeAnnotationLocation location,
) {
if (node != null) {
return node.parameters
.where((e) => e.isPositional)
.map(_formalParameter)
.toList();
} else {
return const [];
}
}
macro.TypeAnnotationImpl _typeAnnotation(ast.TypeAnnotation node) {
node as ast.TypeAnnotationImpl;
switch (node) {
case ast.GenericFunctionTypeImpl():
var namedParameters = <macro.FunctionTypeParameterImpl>[];
var positionalParameters = <macro.FunctionTypeParameterImpl>[];
var formalParameters = node.parameters.parameters;
for (var (index, node) in formalParameters.indexed) {
var formalParameter = _functionTypeFormalParameter(
node,
FormalParameterTypeLocation(location, index),
);
if (node.isNamed) {
namedParameters.add(formalParameter);
} else {
positionalParameters.add(formalParameter);
}
}
return macro.FunctionTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
isNullable: node.question != null,
namedParameters: node.parameters.parameters
.where((e) => e.isNamed)
.map(_functionTypeFormalParameter)
.toList(),
positionalParameters: node.parameters.parameters
.where((e) => e.isPositional)
.map(_functionTypeFormalParameter)
.toList(),
returnType: _typeAnnotationOrDynamic(node.returnType),
namedParameters: namedParameters,
positionalParameters: positionalParameters,
returnType: _typeAnnotationOrDynamic(
node.returnType,
ReturnTypeLocation(location),
),
typeParameters: _typeParameters(node.typeParameters),
);
case ast.NamedTypeImpl():
return _namedType(node);
return _namedType(node, location);
case ast.RecordTypeAnnotationImpl():
return _typeAnnotationRecord(node);
}
@ -1636,30 +1736,43 @@ class DeclarationBuilderFromNode {
macro.TypeAnnotationImpl _typeAnnotationFunctionReturnType(
ast.FunctionDeclaration node,
) {
final element = node.declaredElement!;
final returnType = node.returnType;
if (returnType == null) {
final element = node.declaredElement!;
return _OmittedTypeAnnotationFunctionReturnType(element);
}
return _typeAnnotation(returnType);
return _typeAnnotation(
returnType,
ReturnTypeLocation(
ElementTypeLocation(element),
),
);
}
macro.TypeAnnotationImpl _typeAnnotationMethodReturnType(
ast.MethodDeclaration node,
) {
final element = node.declaredElement!;
final returnType = node.returnType;
if (returnType == null) {
final element = node.declaredElement!;
return _OmittedTypeAnnotationFunctionReturnType(element);
}
return _typeAnnotation(returnType);
return _typeAnnotation(
returnType,
ReturnTypeLocation(
ElementTypeLocation(element),
),
);
}
macro.TypeAnnotationImpl _typeAnnotationOrDynamic(ast.TypeAnnotation? node) {
macro.TypeAnnotationImpl _typeAnnotationOrDynamic(
ast.TypeAnnotation? node,
TypeAnnotationLocation location,
) {
if (node == null) {
return _OmittedTypeAnnotationDynamic();
}
return _typeAnnotation(node);
return _typeAnnotation(node, location);
}
macro.RecordTypeAnnotationImpl _typeAnnotationRecord(
@ -1671,6 +1784,7 @@ class DeclarationBuilderFromNode {
macro.RecordFieldDeclarationImpl buildField(
ast.RecordTypeAnnotationField field,
TypeAnnotationLocation location,
) {
final name = field.name?.lexeme ?? '';
return macro.RecordFieldDeclarationImpl(
@ -1683,24 +1797,40 @@ class DeclarationBuilderFromNode {
library: macroLibrary,
metadata: const [],
name: name,
type: _typeAnnotationOrDynamic(field.type),
type: _typeAnnotationOrDynamic(field.type, location),
);
}
return macro.RecordTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
positionalFields: node.positionalFields.map(buildField).toList(),
namedFields: node.namedFields?.fields.map(buildField).toList() ?? [],
positionalFields: node.positionalFields.indexed.map((pair) {
return buildField(
pair.$2,
RecordPositionalFieldTypeLocation(pair.$1),
);
}).toList(),
namedFields: node.namedFields?.fields.indexed.map((pair) {
return buildField(
pair.$2,
RecordNamedFieldTypeLocation(pair.$1),
);
}).toList() ??
[],
isNullable: node.question != null,
);
}
List<macro.TypeAnnotationImpl> _typeAnnotations(
List<ast.TypeAnnotation>? elements,
TypeAnnotationLocation location,
) {
if (elements != null) {
return List.generate(
elements.length, (i) => _typeAnnotation(elements[i]));
return List.generate(elements.length, (index) {
return _typeAnnotation(
elements[index],
ListIndexTypeLocation(location, index),
);
});
} else {
return const [];
}
@ -1709,11 +1839,15 @@ class DeclarationBuilderFromNode {
macro.TypeAnnotationImpl _typeAnnotationVariable(
ast.TypeAnnotation? type,
VariableElement element,
TypeAnnotationLocation location,
) {
if (type == null) {
return _OmittedTypeAnnotationVariable(element);
}
return _typeAnnotation(type);
return _typeAnnotation(
type,
VariableTypeLocation(location),
);
}
macro.TypeParameterDeclarationImpl _typeParameter(
@ -1725,7 +1859,12 @@ class DeclarationBuilderFromNode {
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(element),
bound: node.bound.mapOrNull(_typeAnnotation),
bound: node.bound.mapOrNull((type) {
return _typeAnnotation(
type,
TypeParameterBoundLocation(),
);
}),
);
}
@ -1955,6 +2094,18 @@ class MixinDeclarationImpl extends macro.MixinDeclarationImpl
});
}
class NamedTypeAnnotation extends macro.NamedTypeAnnotationImpl {
final TypeAnnotationLocation location;
NamedTypeAnnotation({
required super.id,
required super.isNullable,
required super.identifier,
required super.typeArguments,
required this.location,
});
}
class VariableDeclarationImpl extends macro.VariableDeclarationImpl
implements HasElement {
@override

View file

@ -0,0 +1,90 @@
// Copyright (c) 2024, 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.
import 'package:analyzer/dart/element/element.dart';
final class ElementTypeLocation extends TypeAnnotationLocation {
final Element element;
ElementTypeLocation(this.element);
}
final class ExtendsClauseTypeLocation extends TypeAnnotationLocation {
final TypeAnnotationLocation parent;
ExtendsClauseTypeLocation(this.parent);
}
final class ExtensionElementOnTypeLocation extends TypeAnnotationLocation {
final ExtensionElement element;
ExtensionElementOnTypeLocation(this.element);
}
final class ExtensionTypeElementRepresentationTypeLocation
extends TypeAnnotationLocation {
final ExtensionTypeElement element;
ExtensionTypeElementRepresentationTypeLocation(this.element);
}
final class FormalParameterTypeLocation extends TypeAnnotationLocation {
final TypeAnnotationLocation parent;
final int index;
FormalParameterTypeLocation(this.parent, this.index);
}
final class ImplementsClauseTypeLocation extends TypeAnnotationLocation {
final TypeAnnotationLocation parent;
ImplementsClauseTypeLocation(this.parent);
}
final class ListIndexTypeLocation extends TypeAnnotationLocation {
final TypeAnnotationLocation parent;
final int index;
ListIndexTypeLocation(this.parent, this.index);
}
final class OnClauseTypeLocation extends TypeAnnotationLocation {
final TypeAnnotationLocation parent;
OnClauseTypeLocation(this.parent);
}
final class RecordNamedFieldTypeLocation extends TypeAnnotationLocation {
final int index;
RecordNamedFieldTypeLocation(this.index);
}
final class RecordPositionalFieldTypeLocation extends TypeAnnotationLocation {
final int index;
RecordPositionalFieldTypeLocation(this.index);
}
final class ReturnTypeLocation extends TypeAnnotationLocation {
final TypeAnnotationLocation parent;
ReturnTypeLocation(this.parent);
}
sealed class TypeAnnotationLocation {}
final class TypeParameterBoundLocation extends TypeAnnotationLocation {}
final class VariableTypeLocation extends TypeAnnotationLocation {
final TypeAnnotationLocation parent;
VariableTypeLocation(this.parent);
}
final class WithClauseTypeLocation extends TypeAnnotationLocation {
final TypeAnnotationLocation parent;
WithClauseTypeLocation(this.parent);
}

View file

@ -388,6 +388,131 @@ class A {
]);
}
test_diagnostic_report_atTypeAnnotation_class_extends() async {
await assertErrorsInCode('''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'superclass',
])
class A extends Object {}
''', [
error(WarningCode.MACRO_WARNING, 88, 6),
]);
}
test_diagnostic_report_atTypeAnnotation_field_type() async {
await assertErrorsInCode('''
import 'diagnostic.dart';
class A {
@ReportAtTypeAnnotation([
'variableType',
])
int foo = 0;
}
''', [
error(WarningCode.MACRO_WARNING, 92, 3),
]);
}
test_diagnostic_report_atTypeAnnotation_function_formalParameter_named() async {
await assertErrorsInCode('''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'namedFormalParameterType 0',
])
void foo(int a, {String? b, bool? c}) {}
''', [
error(WarningCode.MACRO_WARNING, 105, 7),
]);
}
test_diagnostic_report_atTypeAnnotation_function_formalParameter_positional() async {
await assertErrorsInCode('''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'positionalFormalParameterType 1',
])
void foo(int a, String b) {}
''', [
error(WarningCode.MACRO_WARNING, 109, 6),
]);
}
test_diagnostic_report_atTypeAnnotation_function_returnType() async {
await assertErrorsInCode('''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'returnType',
])
int foo() => 0;
''', [
error(WarningCode.MACRO_WARNING, 72, 3),
]);
}
test_diagnostic_report_atTypeAnnotation_functionType_returnType() async {
await assertErrorsInCode('''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'returnType',
'returnType',
])
int Function() foo() => throw 0;
''', [
error(WarningCode.MACRO_WARNING, 88, 3),
]);
}
test_diagnostic_report_atTypeAnnotation_method_formalParameter_positional() async {
await assertErrorsInCode('''
import 'diagnostic.dart';
class A {
@ReportAtTypeAnnotation([
'positionalFormalParameterType 1',
])
void foo(int a, String b) {}
}
''', [
error(WarningCode.MACRO_WARNING, 127, 6),
]);
}
test_diagnostic_report_atTypeAnnotation_method_returnType() async {
await assertErrorsInCode('''
import 'diagnostic.dart';
class A {
@ReportAtTypeAnnotation([
'returnType',
])
int foo() => 0;
}
''', [
error(WarningCode.MACRO_WARNING, 90, 3),
]);
}
test_diagnostic_report_atTypeAnnotation_namedTypeArgument() async {
await assertErrorsInCode('''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'returnType',
'namedTypeArgument 1',
])
Map<int, String> foo() => throw 0;
''', [
error(WarningCode.MACRO_WARNING, 106, 6),
]);
}
test_diagnostic_report_contextMessages_superClassMethods() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {

View file

@ -9,6 +9,7 @@ import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/field_name_non_promotability_info.dart';
import 'package:analyzer/src/summary2/export.dart';
import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/summary2/macro_type_location.dart';
import 'package:analyzer/src/task/inference_error.dart';
import 'package:collection/collection.dart';
import 'package:test/test.dart';
@ -740,6 +741,40 @@ class _ElementWriter {
}
void _writeMacroDiagnostics(Element e) {
void writeTypeAnnotationLocation(TypeAnnotationLocation location) {
switch (location) {
case ElementTypeLocation():
_sink.writelnWithIndent('ElementTypeLocation');
_sink.withIndent(() {
_elementPrinter.writeNamedElement('element', location.element);
});
case ExtendsClauseTypeLocation():
writeTypeAnnotationLocation(location.parent);
_sink.writelnWithIndent('ExtendsClauseTypeLocation');
case FormalParameterTypeLocation():
writeTypeAnnotationLocation(location.parent);
_sink.writelnWithIndent('FormalParameterTypeLocation');
_sink.withIndent(() {
_sink.writelnWithIndent('index: ${location.index}');
});
case ListIndexTypeLocation():
writeTypeAnnotationLocation(location.parent);
_sink.writelnWithIndent('ListIndexTypeLocation');
_sink.withIndent(() {
_sink.writelnWithIndent('index: ${location.index}');
});
case ReturnTypeLocation():
writeTypeAnnotationLocation(location.parent);
_sink.writelnWithIndent('ReturnTypeLocation');
case VariableTypeLocation():
writeTypeAnnotationLocation(location.parent);
_sink.writelnWithIndent('VariableTypeLocation');
default:
// TODO(scheglov): Handle this case.
throw UnimplementedError('${location.runtimeType}');
}
}
void writeMessage(MacroDiagnosticMessage object) {
// Write the message.
final validator = configuration.macroDiagnosticMessageValidator;
@ -776,6 +811,13 @@ class _ElementWriter {
_sink.withIndent(() {
_elementPrinter.writeNamedElement('element', target.element);
});
case TypeAnnotationMacroDiagnosticTarget():
_sink.writelnWithIndent(
'target: TypeAnnotationMacroDiagnosticTarget',
);
_sink.withIndent(() {
writeTypeAnnotationLocation(target.location);
});
}
}

View file

@ -75,6 +75,106 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart';
}
}
/*macro*/ class ReportAtTypeAnnotation
implements
ClassDeclarationsMacro,
FunctionDeclarationsMacro,
FieldDeclarationsMacro,
MethodDeclarationsMacro {
final List<String> pathList;
const ReportAtTypeAnnotation(this.pathList);
@override
buildDeclarationsForClass(declaration, builder) {
_report(declaration, builder);
}
@override
buildDeclarationsForField(declaration, builder) {
_report(declaration, builder);
}
@override
buildDeclarationsForFunction(declaration, builder) {
_report(declaration, builder);
}
@override
buildDeclarationsForMethod(declaration, builder) {
_report(declaration, builder);
}
TypeAnnotation _getTarget(Declaration declaration) {
var current = _nextTarget(declaration, pathList.first);
for (var step in pathList.skip(1)) {
current = _nextTarget(current, step);
}
return current;
}
TypeAnnotation _nextTarget(Object current, String step) {
if (current is ClassDeclaration) {
if (step == 'superclass') {
return current.superclass!;
}
}
if (current is FunctionDeclaration) {
if (step == 'returnType') {
return current.returnType;
}
if (_verbIndex(step, 'namedFormalParameterType') case var index?) {
return current.namedParameters.elementAt(index).type;
}
if (_verbIndex(step, 'positionalFormalParameterType') case var index?) {
return current.positionalParameters.elementAt(index).type;
}
}
if (current is FunctionTypeAnnotation) {
if (step == 'returnType') {
return current.returnType;
}
}
if (current is NamedTypeAnnotation) {
if (_verbIndex(step, 'namedTypeArgument') case var index?) {
return current.typeArguments.elementAt(index);
}
}
if (current is VariableDeclaration) {
if (step == 'variableType') {
return current.type;
}
}
throw UnimplementedError('[current: $current][step: $step]');
}
void _report(Declaration declaration, DeclarationBuilder builder) {
builder.report(
Diagnostic(
DiagnosticMessage(
'Reported message',
target: _getTarget(declaration).asDiagnosticTarget,
),
Severity.warning,
),
);
}
static int? _verbIndex(String step, String verb) {
var prefix = '$verb ';
if (step.startsWith(prefix)) {
var indexStr = step.substring(prefix.length);
return int.parse(indexStr);
}
return null;
}
}
/*macro*/ class ReportErrorAtTargetDeclaration
extends ReportAtTargetDeclaration {
const ReportErrorAtTargetDeclaration();

View file

@ -3755,6 +3755,391 @@ library
''');
}
test_macroDiagnostics_report_atTypeAnnotation_class_extends() async {
newFile(
'$testPackageLibPath/diagnostic.dart',
_getMacroCode('diagnostic.dart'),
);
final library = await buildLibrary(r'''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'superclass',
])
class A extends Object {}
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/diagnostic.dart
definingUnit
classes
class A @78
macroDiagnostics
MacroDiagnostic
message: MacroDiagnosticMessage
message: Reported message
target: TypeAnnotationMacroDiagnosticTarget
ElementTypeLocation
element: self::@class::A
ExtendsClauseTypeLocation
severity: warning
''');
}
test_macroDiagnostics_report_atTypeAnnotation_field_type() async {
newFile(
'$testPackageLibPath/diagnostic.dart',
_getMacroCode('diagnostic.dart'),
);
final library = await buildLibrary(r'''
import 'diagnostic.dart';
class A {
@ReportAtTypeAnnotation([
'variableType',
])
final int foo = 0;
}
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/diagnostic.dart
definingUnit
classes
class A @33
fields
final foo @102
type: int
shouldUseTypeForInitializerInference: true
macroDiagnostics
MacroDiagnostic
message: MacroDiagnosticMessage
message: Reported message
target: TypeAnnotationMacroDiagnosticTarget
ElementTypeLocation
element: self::@class::A::@field::foo
VariableTypeLocation
severity: warning
accessors
synthetic get foo @-1
returnType: int
''');
}
test_macroDiagnostics_report_atTypeAnnotation_function_formalParameter_named() async {
newFile(
'$testPackageLibPath/diagnostic.dart',
_getMacroCode('diagnostic.dart'),
);
final library = await buildLibrary(r'''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'namedFormalParameterType 0',
])
void foo(int a, {String? b, bool? c}) {}
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/diagnostic.dart
definingUnit
functions
foo @93
parameters
requiredPositional a @101
type: int
optionalNamed default b @113
type: String?
optionalNamed default c @122
type: bool?
returnType: void
macroDiagnostics
MacroDiagnostic
message: MacroDiagnosticMessage
message: Reported message
target: TypeAnnotationMacroDiagnosticTarget
ElementTypeLocation
element: self::@function::foo
FormalParameterTypeLocation
index: 1
VariableTypeLocation
severity: warning
''');
}
test_macroDiagnostics_report_atTypeAnnotation_function_formalParameter_positional() async {
newFile(
'$testPackageLibPath/diagnostic.dart',
_getMacroCode('diagnostic.dart'),
);
final library = await buildLibrary(r'''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'positionalFormalParameterType 1',
])
void foo(int a, String b) {}
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/diagnostic.dart
definingUnit
functions
foo @98
parameters
requiredPositional a @106
type: int
requiredPositional b @116
type: String
returnType: void
macroDiagnostics
MacroDiagnostic
message: MacroDiagnosticMessage
message: Reported message
target: TypeAnnotationMacroDiagnosticTarget
ElementTypeLocation
element: self::@function::foo
FormalParameterTypeLocation
index: 1
VariableTypeLocation
severity: warning
''');
}
test_macroDiagnostics_report_atTypeAnnotation_function_returnType() async {
newFile(
'$testPackageLibPath/diagnostic.dart',
_getMacroCode('diagnostic.dart'),
);
final library = await buildLibrary(r'''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'returnType',
])
int foo() => 0;
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/diagnostic.dart
definingUnit
functions
foo @76
returnType: int
macroDiagnostics
MacroDiagnostic
message: MacroDiagnosticMessage
message: Reported message
target: TypeAnnotationMacroDiagnosticTarget
ElementTypeLocation
element: self::@function::foo
ReturnTypeLocation
severity: warning
''');
}
test_macroDiagnostics_report_atTypeAnnotation_functionType_returnType() async {
newFile(
'$testPackageLibPath/diagnostic.dart',
_getMacroCode('diagnostic.dart'),
);
final library = await buildLibrary(r'''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'returnType',
'returnType',
])
int Function() foo() => throw 0;
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/diagnostic.dart
definingUnit
functions
foo @103
returnType: int Function()
macroDiagnostics
MacroDiagnostic
message: MacroDiagnosticMessage
message: Reported message
target: TypeAnnotationMacroDiagnosticTarget
ElementTypeLocation
element: self::@function::foo
ReturnTypeLocation
ReturnTypeLocation
severity: warning
''');
}
test_macroDiagnostics_report_atTypeAnnotation_method_formalParameter_positional() async {
newFile(
'$testPackageLibPath/diagnostic.dart',
_getMacroCode('diagnostic.dart'),
);
final library = await buildLibrary(r'''
import 'diagnostic.dart';
class A {
@ReportAtTypeAnnotation([
'positionalFormalParameterType 1',
])
void foo(int a, String b) {}
}
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/diagnostic.dart
definingUnit
classes
class A @33
methods
foo @116
parameters
requiredPositional a @124
type: int
requiredPositional b @134
type: String
returnType: void
macroDiagnostics
MacroDiagnostic
message: MacroDiagnosticMessage
message: Reported message
target: TypeAnnotationMacroDiagnosticTarget
ElementTypeLocation
element: self::@class::A::@method::foo
FormalParameterTypeLocation
index: 1
VariableTypeLocation
severity: warning
''');
}
test_macroDiagnostics_report_atTypeAnnotation_method_returnType() async {
newFile(
'$testPackageLibPath/diagnostic.dart',
_getMacroCode('diagnostic.dart'),
);
final library = await buildLibrary(r'''
import 'diagnostic.dart';
class A {
@ReportAtTypeAnnotation([
'returnType',
])
int foo() => 0;
}
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/diagnostic.dart
definingUnit
classes
class A @33
methods
foo @94
returnType: int
macroDiagnostics
MacroDiagnostic
message: MacroDiagnosticMessage
message: Reported message
target: TypeAnnotationMacroDiagnosticTarget
ElementTypeLocation
element: self::@class::A::@method::foo
ReturnTypeLocation
severity: warning
''');
}
test_macroDiagnostics_report_atTypeAnnotation_namedTypeArgument() async {
newFile(
'$testPackageLibPath/diagnostic.dart',
_getMacroCode('diagnostic.dart'),
);
final library = await buildLibrary(r'''
import 'diagnostic.dart';
@ReportAtTypeAnnotation([
'returnType',
'namedTypeArgument 1',
])
Map<int, String> foo() {}
''');
configuration
..withConstructors = false
..withMetadata = false;
checkElementText(library, r'''
library
imports
package:test/diagnostic.dart
definingUnit
functions
foo @114
returnType: Map<int, String>
macroDiagnostics
MacroDiagnostic
message: MacroDiagnosticMessage
message: Reported message
target: TypeAnnotationMacroDiagnosticTarget
ElementTypeLocation
element: self::@function::foo
ReturnTypeLocation
ListIndexTypeLocation
index: 1
severity: warning
''');
}
test_macroDiagnostics_report_contextMessages() async {
newFile(
'$testPackageLibPath/diagnostic.dart',