| // Copyright (c) 2023, 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:_fe_analyzer_shared/src/macros/api.dart'; |
| |
| /*macro*/ class MacroWithArguments implements ClassDeclarationsMacro { |
| final Object? a1; |
| final Object? a2; |
| |
| const MacroWithArguments(this.a1, this.a2); |
| |
| @override |
| buildDeclarationsForClass(declaration, builder) {} |
| } |
| |
| /*macro*/ class NothingMacro |
| implements ClassTypesMacro, ClassDeclarationsMacro, ClassDefinitionMacro { |
| const NothingMacro(); |
| |
| @override |
| buildDeclarationsForClass(clazz, builder) {} |
| |
| @override |
| buildDefinitionForClass(clazz, builder) {} |
| |
| @override |
| buildTypesForClass(clazz, builder) {} |
| } |
| |
| /*macro*/ class ReportAtFirstMethod implements ClassDeclarationsMacro { |
| const ReportAtFirstMethod(); |
| |
| Severity get _severity => Severity.warning; |
| |
| @override |
| buildDeclarationsForClass(declaration, builder) async { |
| final methods = await builder.methodsOf(declaration); |
| if (methods case [final method, ...]) { |
| builder.report( |
| Diagnostic( |
| DiagnosticMessage( |
| 'Reported message', |
| target: method.asDiagnosticTarget, |
| ), |
| _severity, |
| ), |
| ); |
| } |
| } |
| } |
| |
| /*macro*/ class ReportAtTargetDeclaration |
| implements |
| ClassTypesMacro, |
| ConstructorTypesMacro, |
| FieldTypesMacro, |
| MethodTypesMacro, |
| MixinTypesMacro { |
| const ReportAtTargetDeclaration(); |
| |
| Severity get _severity => Severity.warning; |
| |
| @override |
| buildTypesForClass(declaration, builder) { |
| _report(declaration, builder); |
| } |
| |
| @override |
| buildTypesForConstructor(declaration, builder) { |
| _report(declaration, builder); |
| } |
| |
| @override |
| buildTypesForField(declaration, builder) { |
| _report(declaration, builder); |
| } |
| |
| @override |
| buildTypesForMethod(declaration, builder) { |
| _report(declaration, builder); |
| } |
| |
| @override |
| buildTypesForMixin(declaration, builder) { |
| _report(declaration, builder); |
| } |
| |
| void _report(Declaration declaration, Builder builder) { |
| builder.report( |
| Diagnostic( |
| DiagnosticMessage( |
| 'Reported message', |
| target: declaration.asDiagnosticTarget, |
| ), |
| _severity, |
| ), |
| ); |
| } |
| } |
| |
| /*macro*/ class ReportAtTypeAnnotation |
| implements |
| ClassDeclarationsMacro, |
| FunctionDeclarationsMacro, |
| FieldDeclarationsMacro, |
| MethodDeclarationsMacro, |
| VariableDeclarationsMacro { |
| final List<String> pathList; |
| |
| const ReportAtTypeAnnotation(this.pathList); |
| |
| @override |
| buildDeclarationsForClass(declaration, builder) async { |
| await _report(declaration, builder); |
| } |
| |
| @override |
| buildDeclarationsForField(declaration, builder) async { |
| await _report(declaration, builder); |
| } |
| |
| @override |
| buildDeclarationsForFunction(declaration, builder) async { |
| await _report(declaration, builder); |
| } |
| |
| @override |
| buildDeclarationsForMethod(declaration, builder) async { |
| await _report(declaration, builder); |
| } |
| |
| @override |
| buildDeclarationsForVariable(declaration, builder) async { |
| await _report(declaration, builder); |
| } |
| |
| Future<TypeAnnotation> _getTarget( |
| Declaration declaration, |
| DeclarationBuilder builder, |
| ) async { |
| var current = await _nextTarget(builder, declaration, pathList.first); |
| for (var step in pathList.skip(1)) { |
| current = await _nextTarget(builder, current, step); |
| } |
| return current; |
| } |
| |
| Future<TypeAnnotation> _nextTarget( |
| DeclarationBuilder builder, |
| Object current, |
| String step, |
| ) async { |
| if (current is ClassDeclaration) { |
| if (step == 'superclass') { |
| return current.superclass!; |
| } |
| } |
| |
| if (current is MemberDeclaration) { |
| if (_verbSuffix(step, 'field') case var fieldName?) { |
| var definingType = await builder.typeDeclarationOf( |
| current.definingType, |
| ); |
| var fields = await builder.fieldsOf(definingType); |
| var field = fields.singleWhere((field) { |
| return field.identifier.name == fieldName; |
| }); |
| return field.type; |
| } |
| } |
| |
| 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 (_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 NamedTypeAnnotation) { |
| if (_verbIndex(step, 'namedTypeArgument') case var index?) { |
| return current.typeArguments.elementAt(index); |
| } |
| } |
| |
| if (current is RecordTypeAnnotation) { |
| if (_verbIndex(step, 'namedField') case var index?) { |
| return current.namedFields.elementAt(index).type; |
| } |
| if (_verbIndex(step, 'positionalField') case var index?) { |
| return current.positionalFields.elementAt(index).type; |
| } |
| } |
| |
| if (current is VariableDeclaration) { |
| if (step == 'variableType') { |
| return current.type; |
| } |
| } |
| |
| throw UnimplementedError('[current: $current][step: $step]'); |
| } |
| |
| Future<void> _report( |
| Declaration declaration, |
| DeclarationBuilder builder, |
| ) async { |
| var target = await _getTarget(declaration, builder); |
| builder.report( |
| Diagnostic( |
| DiagnosticMessage( |
| 'Reported message', |
| target: target.asDiagnosticTarget, |
| ), |
| Severity.warning, |
| ), |
| ); |
| } |
| |
| static int? _verbIndex(String step, String verb) { |
| var indexStr = _verbSuffix(step, verb); |
| if (indexStr != null) { |
| return int.parse(indexStr); |
| } |
| return null; |
| } |
| |
| static String? _verbSuffix(String step, String verb) { |
| var prefix = '$verb '; |
| if (step.startsWith(prefix)) { |
| return step.substring(prefix.length); |
| } |
| return null; |
| } |
| } |
| |
| /*macro*/ class ReportErrorAtTargetDeclaration |
| extends ReportAtTargetDeclaration { |
| const ReportErrorAtTargetDeclaration(); |
| |
| @override |
| Severity get _severity => Severity.error; |
| } |
| |
| /*macro*/ class ReportInfoAtTargetDeclaration |
| extends ReportAtTargetDeclaration { |
| const ReportInfoAtTargetDeclaration(); |
| |
| @override |
| Severity get _severity => Severity.info; |
| } |
| |
| /*macro*/ class ReportWithContextMessages implements ClassDeclarationsMacro { |
| final bool forSuperClass; |
| final bool withDeclarationTarget; |
| |
| const ReportWithContextMessages({ |
| this.forSuperClass = false, |
| this.withDeclarationTarget = true, |
| }); |
| |
| @override |
| buildDeclarationsForClass(declaration, builder) async { |
| final List<MethodDeclaration> methods; |
| if (forSuperClass) { |
| final superIdentifier = declaration.superclass!.identifier; |
| final superType = await builder.typeDeclarationOf(superIdentifier); |
| superType as ClassDeclaration; |
| methods = await builder.methodsOf(superType); |
| } else { |
| methods = await builder.methodsOf(declaration); |
| } |
| |
| builder.report( |
| Diagnostic( |
| DiagnosticMessage( |
| 'Reported message', |
| target: withDeclarationTarget ? declaration.asDiagnosticTarget : null, |
| ), |
| Severity.warning, |
| contextMessages: methods.map((method) { |
| return DiagnosticMessage( |
| 'See ${method.identifier.name}', |
| target: method.asDiagnosticTarget, |
| ); |
| }).toList(), |
| ), |
| ); |
| } |
| } |
| |
| /*macro*/ class ReportWithoutTargetError implements ClassTypesMacro { |
| const ReportWithoutTargetError(); |
| |
| @override |
| buildTypesForClass(clazz, builder) { |
| builder.report( |
| Diagnostic( |
| DiagnosticMessage('Reported message'), |
| Severity.error, |
| ), |
| ); |
| } |
| } |
| |
| /*macro*/ class ReportWithoutTargetInfo implements ClassTypesMacro { |
| const ReportWithoutTargetInfo(); |
| |
| @override |
| buildTypesForClass(clazz, builder) { |
| builder.report( |
| Diagnostic( |
| DiagnosticMessage('Reported message'), |
| Severity.info, |
| ), |
| ); |
| } |
| } |
| |
| /*macro*/ class ReportWithoutTargetWarning implements ClassTypesMacro { |
| const ReportWithoutTargetWarning(); |
| |
| @override |
| buildTypesForClass(clazz, builder) { |
| builder.report( |
| Diagnostic( |
| DiagnosticMessage('Reported message'), |
| Severity.warning, |
| ), |
| ); |
| } |
| } |
| |
| /*macro*/ class TargetClassOrMixinMacro |
| implements ClassTypesMacro, MixinTypesMacro { |
| const TargetClassOrMixinMacro(); |
| |
| @override |
| buildTypesForClass(declaration, builder) {} |
| |
| @override |
| buildTypesForMixin(declaration, builder) {} |
| } |
| |
| /*macro*/ class ThrowExceptionDeclarationsPhase |
| implements |
| ClassDeclarationsMacro, |
| ConstructorDeclarationsMacro, |
| FieldDeclarationsMacro, |
| MethodDeclarationsMacro { |
| const ThrowExceptionDeclarationsPhase(); |
| |
| @override |
| buildDeclarationsForClass(clazz, builder) { |
| _doThrow(); |
| } |
| |
| @override |
| buildDeclarationsForConstructor(constructor, builder) { |
| _doThrow(); |
| } |
| |
| @override |
| buildDeclarationsForField(declaration, builder) { |
| _doThrow(); |
| } |
| |
| @override |
| buildDeclarationsForMethod(method, builder) { |
| _doThrow(); |
| } |
| |
| void _doThrow() { |
| throw 'My declarations phase'; |
| } |
| } |
| |
| /*macro*/ class ThrowExceptionDefinitionsPhase implements ClassDefinitionMacro { |
| const ThrowExceptionDefinitionsPhase(); |
| |
| @override |
| buildDefinitionForClass(clazz, builder) { |
| _doThrow(); |
| } |
| |
| void _doThrow() { |
| throw 'My definitions phase'; |
| } |
| } |
| |
| /*macro*/ class ThrowExceptionTypesPhase implements ClassTypesMacro { |
| const ThrowExceptionTypesPhase(); |
| |
| @override |
| buildTypesForClass(clazz, builder) { |
| _doThrow(); |
| } |
| |
| void _doThrow() { |
| throw 'My types phase'; |
| } |
| } |