diff --git a/CHANGELOG.md b/CHANGELOG.md index 5671378bf..edc8c0c3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # OverReact Changelog +## Unreleased +- Analyzer plugin + - Fix startup error (AOT compilation) in Dart 3 + - Allow analyzer 6.x, allowing parsing language versions <=3.7 + ## 5.6.1 - Allow w_common 4 #1004 https://github.com/Workiva/over_react/pull/1004 diff --git a/lib/src/builder/parsing/meta.dart b/lib/src/builder/parsing/meta.dart index 9f9b44345..3e59f0cd5 100644 --- a/lib/src/builder/parsing/meta.dart +++ b/lib/src/builder/parsing/meta.dart @@ -12,38 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:mirrors' as mirrors; - import 'package:analyzer/dart/ast/ast.dart'; import 'package:build/build.dart' show log; import 'package:collection/collection.dart' show IterableExtension; -import 'package:over_react/src/builder/vendor/transformer_utils/transformer_utils.dart'; -import 'ast_util.dart'; +import 'package:over_react/src/component_declaration/annotations.dart' as a; -/// Uses reflection to instantiate and return the first annotation on [member] of type -/// [T], or null if no matching annotations are found. -/// -/// > See [instantiateAnnotation] for more information. -T? instantiateAnnotationTyped(AnnotatedNode member, - {dynamic Function(Expression argument)? onUnsupportedArgument}) { - return instantiateAnnotation(member, T, onUnsupportedArgument: onUnsupportedArgument) as T?; -} +import '../vendor/transformer_utils/src/analyzer_helpers.dart'; -/// Returns the first annotation AST node on [member] of type [annotationType], +/// Returns the first annotation AST node on [node] of type [T], /// or null if no matching annotations are found. -Annotation? _getMatchingAnnotation(AnnotatedNode member, Type annotationType) { - // Be sure to use `originalDeclaration` so that generic parameters work. - final classMirror = mirrors.reflectClass(annotationType).originalDeclaration; - final className = mirrors.MirrorSystem.getName(classMirror.simpleName); - return member.getAnnotationWithName(className); -} +Annotation? _getMatchingAnnotationFromGeneric(AnnotatedNode node) => + _getMatchingAnnotation(_AnnotationClass.fromGeneric(), node); + +/// Returns the first annotation AST node on [node] of type [annotationClass], +/// or null if no matching annotations are found. +Annotation? _getMatchingAnnotation(_AnnotationClass annotationClass, AnnotatedNode node) => + node.metadata.firstWhereOrNull((m) => m.name.name == annotationClass.className); /// Utility class that allows partial instantiation of annotations, to support reading /// annotation data in a context without a resolved AST. See [isIncomplete] for more info. -/// -/// Based off of [NodeWithMeta]. -class InstantiatedMeta { +class InstantiatedMeta { /// The node of the [TMeta] annotation, if it exists. final Annotation metaNode; @@ -62,8 +51,8 @@ class InstantiatedMeta { /// The original node will be available via [node]. /// /// The instantiated annotation will be available via [value]. - static InstantiatedMeta? fromNode(AnnotatedNode node) { - final metaNode = _getMatchingAnnotation(node, T); + static InstantiatedMeta? fromNode(AnnotatedNode node) { + final metaNode = _getMatchingAnnotationFromGeneric(node); if (metaNode == null) return null; final unsupportedArguments = []; @@ -99,7 +88,7 @@ class InstantiatedMeta { /// Utility that allows partial instantiation of a `Component`/`Component2` annotation. /// /// See superclass for more information. -class InstantiatedComponentMeta extends InstantiatedMeta { +class InstantiatedComponentMeta extends InstantiatedMeta { static const String _subtypeOfParamName = 'subtypeOf'; final Identifier? subtypeOfValue; @@ -108,7 +97,7 @@ class InstantiatedComponentMeta extends InstantiatedMeta { Annotation metaNode, TMeta meta, List unsupportedArguments, this.subtypeOfValue) : super._(metaNode, meta, unsupportedArguments); - static InstantiatedComponentMeta? fromNode(AnnotatedNode node) { + static InstantiatedComponentMeta? fromNode(AnnotatedNode node) { try { final instantiated = InstantiatedMeta.fromNode(node); if (instantiated == null) return null; @@ -140,3 +129,106 @@ class InstantiatedComponentMeta extends InstantiatedMeta { } } } + +T? instantiateAnnotationTyped( + AnnotatedNode node, { + dynamic Function(Expression argument)? onUnsupportedArgument, +}) { + final annotationClass = _AnnotationClass.fromGeneric(); + + final annotation = _getMatchingAnnotation(annotationClass, node); + if (annotation == null) return null; + + final args = parseAnnotationArgs(annotation, onUnsupportedArgument: onUnsupportedArgument); + S namedArg(String name) => args.named[name] as S; + + switch (annotationClass) { + case _AnnotationClass.props: + return a.Props( + keyNamespace: namedArg('keyNamespace'), + disableRequiredPropValidation: namedArg('disableRequiredPropValidation'), + disableValidationForClassDefaultProps: + namedArg('disableValidationForClassDefaultProps') ?? true, + ) as T; + case _AnnotationClass.abstractProps: + return a.AbstractProps( + keyNamespace: namedArg('keyNamespace'), + ) as T; + case _AnnotationClass.propsMixin: + // ignore: deprecated_member_use_from_same_package + return a.PropsMixin( + keyNamespace: namedArg('keyNamespace'), + ) as T; + case _AnnotationClass.state: + return a.State( + keyNamespace: namedArg('keyNamespace'), + ) as T; + case _AnnotationClass.abstractState: + return a.AbstractState( + keyNamespace: namedArg('keyNamespace'), + ) as T; + case _AnnotationClass.stateMixin: + // ignore: deprecated_member_use_from_same_package + return a.StateMixin( + keyNamespace: namedArg('keyNamespace'), + ) as T; + case _AnnotationClass.component2: + return a.Component2( + isWrapper: namedArg('isWrapper') ?? false, + subtypeOf: namedArg('subtypeOf'), + isErrorBoundary: namedArg('isErrorBoundary') ?? false, + ) as T; + case _AnnotationClass.component: + // ignore: deprecated_member_use_from_same_package + return a.Component( + isWrapper: namedArg('isWrapper') ?? false, + subtypeOf: namedArg('subtypeOf'), + ) as T; + case _AnnotationClass.accessor: + return a.Accessor( + key: namedArg('key'), + keyNamespace: namedArg('keyNamespace'), + isRequired: namedArg('isRequired') ?? false, + isNullable: namedArg('isNullable') ?? false, + requiredErrorMessage: namedArg('requiredErrorMessage'), + doNotGenerate: namedArg('doNotGenerate') ?? false, + ) as T; + } +} + +enum _AnnotationClass { + props('Props'), + abstractProps('AbstractProps'), + propsMixin('PropsMixin'), + state('State'), + abstractState('AbstractState'), + stateMixin('StateMixin'), + component2('Component2'), + component('Component'), + accessor('Accessor'); + + final String className; + + const _AnnotationClass(this.className); + + static _AnnotationClass fromGeneric() { + if (_isSubtypeOf()) return _AnnotationClass.props; + if (_isSubtypeOf()) return _AnnotationClass.abstractProps; + // ignore: deprecated_member_use_from_same_package + if (_isSubtypeOf()) return _AnnotationClass.propsMixin; + if (_isSubtypeOf()) return _AnnotationClass.state; + if (_isSubtypeOf()) return _AnnotationClass.abstractState; + // ignore: deprecated_member_use_from_same_package + if (_isSubtypeOf()) return _AnnotationClass.stateMixin; + if (_isSubtypeOf()) return _AnnotationClass.component2; + // ignore: deprecated_member_use_from_same_package + if (_isSubtypeOf()) return _AnnotationClass.component; + if (_isSubtypeOf()) return _AnnotationClass.accessor; + + throw ArgumentError('Unsupported generic: $T. Must correspond to a type in this enum.'); + } +} + +bool _isSubtypeOf() => _SubtypeOfHelper() is _SubtypeOfHelper; + +class _SubtypeOfHelper {} diff --git a/lib/src/builder/util.dart b/lib/src/builder/util.dart index 9bc2ac49e..49474c922 100644 --- a/lib/src/builder/util.dart +++ b/lib/src/builder/util.dart @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:mirrors'; - import 'package:analyzer/dart/ast/ast.dart'; import 'package:build/build.dart' show AssetId; import 'package:over_react/src/builder/parsing.dart'; @@ -26,10 +24,6 @@ const privateSourcePrefix = r'_$'; const privatePrefix = r'_'; const publicGeneratedPrefix = r'$'; -String getName(Type type) { - return MirrorSystem.getName(reflectType(type).simpleName); -} - /// Converts [id] to a "package:" URI. /// /// This will return a schemeless URI if [id] doesn't represent a library in diff --git a/lib/src/builder/vendor/transformer_utils/src/analyzer_helpers.dart b/lib/src/builder/vendor/transformer_utils/src/analyzer_helpers.dart index 788c1ed99..5f3f644fa 100644 --- a/lib/src/builder/vendor/transformer_utils/src/analyzer_helpers.dart +++ b/lib/src/builder/vendor/transformer_utils/src/analyzer_helpers.dart @@ -16,10 +16,7 @@ library; -import 'dart:mirrors' as mirrors; - import 'package:analyzer/dart/ast/ast.dart'; -import 'package:collection/collection.dart' show IterableExtension; /// Returns a copy of a class [member] declaration with [body] as a new /// implementation. @@ -38,19 +35,6 @@ String copyClassMember(ClassMember? member, String body) { 'Only FieldDeclaration and MethodDeclaration are supported.'); } -/// Finds and returns all declarations within a compilation [unit] that are -/// annotated with the given [annotation] class. -/// -/// If this is being leveraged within a transformer, you can associate the -/// returned [DeclarationWithMeta] instance with the asset in which it is -/// located by passing in an [assetId]. -Iterable getDeclarationsAnnotatedBy(CompilationUnit unit, Type annotation) { - var annotationName = _getReflectedName(annotation); - return unit.declarations.where((member) { - return member.metadata.any((meta) => meta.name.name == annotationName); - }); -} - /// Returns the value of the specified [expression] AST node if it represents a literal. /// /// For non-literal nodes, the return value will be the result of calling [onUnsupportedExpression] with [expression]. @@ -85,47 +69,23 @@ dynamic getValue(Expression expression, 'Must be a uninterpolated string, boolean, integer, or null literal.'); } -/// Returns the first annotation AST node on [member] of type [annotationType], -/// or null if no matching annotations are found. -Annotation? getMatchingAnnotation(AnnotatedNode member, Type annotationType) { - // Be sure to use `originalDeclaration` so that generic parameters work. - final classMirror = - mirrors.reflectClass(annotationType).originalDeclaration as mirrors.ClassMirror; - String className = mirrors.MirrorSystem.getName(classMirror.simpleName); - - // Find the annotation that matches [type]'s name. - return member.metadata.firstWhereOrNull((annotation) { - return _getClassName(annotation) == className; - }); +/// The parsed arguments of an annotation constructor call, from [parseAnnotationArgs]. +class AnnotationArgs { + final Map named; + final List positional; + + AnnotationArgs({required this.named, required this.positional}); } -/// Uses reflection to instantiate and returns the first annotation on [member] of type -/// [annotationType], or null if no matching annotations are found. -/// -/// Annotation constructors are currently limited to the values supported by [getValue]. -/// -/// Naively assumes that the name of the [annotationType] class is canonical. -dynamic instantiateAnnotation(AnnotatedNode member, Type annotationType, +/// Returns annotation class constructor arguments parsed from [annotation], +/// converting supported literal values via [getValue], and passing unsupported +/// argument values through [onUnsupportedArgument]. +AnnotationArgs parseAnnotationArgs(Annotation annotation, {dynamic Function(Expression argument)? onUnsupportedArgument}) { - final matchingAnnotation = getMatchingAnnotation(member, annotationType); - - // If no annotation is found, return null. - if (matchingAnnotation == null) { - return null; - } - - final matchingAnnotationArgs = matchingAnnotation.arguments; - if (matchingAnnotationArgs == null) { - throw Exception('Annotation not invocation of constructor: `$matchingAnnotation`. ' - 'This is likely due to invalid usage of the annotation class, but could' - 'also be a name conflict with the specified type `$annotationType`'); - } - - // Get the parameters from the annotation's AST. - Map namedParameters = {}; + Map namedParameters = {}; List positionalParameters = []; - matchingAnnotationArgs.arguments.forEach((argument) { + annotation.arguments?.arguments.forEach((argument) { var onUnsupportedExpression = onUnsupportedArgument == null ? null : (_) => onUnsupportedArgument(argument); @@ -133,7 +93,7 @@ dynamic instantiateAnnotation(AnnotatedNode member, Type annotationType, var name = argument.name.label.name; var value = getValue(argument.expression, onUnsupportedExpression: onUnsupportedExpression); - namedParameters[Symbol(name)] = value; + namedParameters[name] = value; } else { var value = getValue(argument, onUnsupportedExpression: onUnsupportedExpression); @@ -141,22 +101,10 @@ dynamic instantiateAnnotation(AnnotatedNode member, Type annotationType, } }); - // Instantiate and return an instance of the annotation using reflection. - String constructorName = _getConstructorName(matchingAnnotation) ?? ''; - - // Be sure to use `originalDeclaration` so that generic parameters work. - final classMirror = - mirrors.reflectClass(annotationType).originalDeclaration as mirrors.ClassMirror; - - try { - var instanceMirror = - classMirror.newInstance(Symbol(constructorName), positionalParameters, namedParameters); - return instanceMirror.reflectee; - } catch (e, stacktrace) { - throw Exception('Unable to instantiate annotation: $matchingAnnotation. This is ' - 'likely due to improper usage, or a naming conflict with ' - 'annotationType $annotationType. Original error: $e. Stacktrace: $stacktrace'); - } + return AnnotationArgs( + named: namedParameters, + positional: positionalParameters, + ); } String _copyFieldDeclaration(FieldDeclaration decl, String initializer) { @@ -221,31 +169,3 @@ String _copyMethodDeclaration(MethodDeclaration decl, String body) { result = '$result {\n$body\n }'; return result; } - -/// Returns the name of the class being instantiated for [annotation], or null -/// if the annotation is not the invocation of a constructor. -/// -/// Workaround for a Dart analyzer issue where the constructor name is included -/// in [annotation.name]. -String _getClassName(Annotation annotation) => annotation.name.name.split('.').first; - -/// Returns the name of the constructor being instantiated for [annotation], or -/// null if the annotation is not the invocation of a named constructor. -/// -/// Workaround for a Dart analyzer issue where the constructor name is included -/// in [annotation.name]. -String? _getConstructorName(Annotation annotation) { - var constructorName = annotation.constructorName?.name; - if (constructorName == null) { - var periodIndex = annotation.name.name.indexOf('.'); - if (periodIndex != -1) { - constructorName = annotation.name.name.substring(periodIndex + 1); - } - } - - return constructorName; -} - -/// Get the name of a [type] via reflection. -String _getReflectedName(Type type) => - mirrors.MirrorSystem.getName(mirrors.reflectType(type).simpleName); diff --git a/lib/src/builder/vendor/transformer_utils/src/node_with_meta.dart b/lib/src/builder/vendor/transformer_utils/src/node_with_meta.dart deleted file mode 100644 index 54c806645..000000000 --- a/lib/src/builder/vendor/transformer_utils/src/node_with_meta.dart +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2015 Workiva Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Copied from https://github.com/Workiva/dart_transformer_utils/tree/0.2.23/lib/ - -library; - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:build/build.dart'; - -import './analyzer_helpers.dart'; - -/// Utility class that allows for easy access to an annotated node's -/// instantiated annotation. -class NodeWithMeta { - /// The optionally-annotated node. - final TNode node; - - /// The optional asset ID associated with this node. - final AssetId? assetId; - - /// The node of the [TMeta] annotation, if it exists. - final Annotation? metaNode; - - /// A reflectively-instantiated version of [metaNode], if it exists. - TMeta? _meta; - - /// The arguments passed to the metadata that are not supported by [getValue], - /// (or by special handling in subclasses) and therefore not represented in the instantiation of [meta]. - final List unsupportedArguments = []; - - /// Construct a [NodeWithMeta] instance from an [AnnotatedNode]. - /// The original node will be available via [node]. - /// The instantiated annotation of type `TMeta` will be available via [meta]. - NodeWithMeta(this.node, {this.assetId}) : this.metaNode = getMatchingAnnotation(node, TMeta) { - this._meta = instantiateAnnotation(node, TMeta, onUnsupportedArgument: unsupportedArguments.add) - as TMeta?; - } - - /// Whether this node's metadata has arguments that could not be initialized using [getValue] - /// (or by special handling in subclasses), and therefore cannot represented in the instantiation of [meta]. - bool get isIncomplete => unsupportedArguments.isNotEmpty; - - /// A reflectively-instantiated version of [metaNode], if it exists. - /// - /// Throws a [StateError] if this node's metadata is incomplete. - TMeta? get meta { - if (isIncomplete) { - throw StateError('Metadata is incomplete; unsupported arguments $unsupportedArguments. ' - 'Use `potentiallyIncompleteMeta` instead.'); - } - return _meta; - } - - /// A reflectively-instantiated version of [metaNode], if it exists. - TMeta? get potentiallyIncompleteMeta => _meta; -} diff --git a/lib/src/builder/vendor/transformer_utils/transformer_utils.dart b/lib/src/builder/vendor/transformer_utils/transformer_utils.dart index 3cac85dd7..ae13acef7 100644 --- a/lib/src/builder/vendor/transformer_utils/transformer_utils.dart +++ b/lib/src/builder/vendor/transformer_utils/transformer_utils.dart @@ -16,9 +16,7 @@ library; -export './src/analyzer_helpers.dart' - show copyClassMember, getDeclarationsAnnotatedBy, instantiateAnnotation; +export './src/analyzer_helpers.dart' show copyClassMember; export './src/barback_utils.dart' show assetIdToPackageUri, getSpanForNode; -export './src/node_with_meta.dart' show NodeWithMeta; export './src/text_util.dart' show stringLiteral; export './src/transformed_source_file.dart' show TransformedSourceFile, getSpan; diff --git a/test/vm_tests/builder/parsing/meta_test.dart b/test/vm_tests/builder/parsing/meta_test.dart deleted file mode 100644 index a353a17c3..000000000 --- a/test/vm_tests/builder/parsing/meta_test.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2020 Workiva Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -@TestOn('vm') -import 'package:over_react/src/builder/parsing/meta.dart'; -import 'package:test/test.dart'; - -import '../ast_test_util.dart'; - -void main() { - group('InstantiatedMeta', () { - test('instantiates and provides access to an annotation and node', () { - final member = parseAndGetSingleMember(r''' - @TestAnnotation("hello") - var a; - '''); - final meta = InstantiatedMeta.fromNode(member)!; - - expect(meta.metaNode.name.name, 'TestAnnotation'); - expect(meta.value, isNotNull); - expect(meta.value.positional, 'hello'); - }); - - test('partially instantiates an "incomplete" annotation', () { - final member = parseAndGetSingleMember(r''' - @TestAnnotation(someIdentifier, named: "hello") - var a; - '''); - final meta = InstantiatedMeta.fromNode(member)!; - - expect(meta.metaNode.name.name, 'TestAnnotation'); - - expect(meta.isIncomplete, isTrue); - expect(meta.unsupportedArguments, hasLength(1)); - expect(() => meta.value, throwsStateError); - - expect(meta.potentiallyIncompleteValue, isNotNull, - reason: 'should still have attempted to instantiate the incomplete annotation'); - expect(meta.potentiallyIncompleteValue.named, equals('hello'), - reason: 'should still have passed the supported argument'); - expect(meta.potentiallyIncompleteValue.positional, isNull, - reason: 'should have used null for unsupported argument'); - }); - - test('gracefully handles a node without a matching annotation and returns null', () { - final member = parseAndGetSingleMember(r''' - var a; - '''); - final meta = InstantiatedMeta.fromNode(member); - - expect(meta, isNull); - }); - }); -} - -class TestAnnotation { - final Object? positional; - final Object? named; - final Object? namedConstructorOnly; - - const TestAnnotation(this.positional, {this.named}) : namedConstructorOnly = null; - - const TestAnnotation.namedConstructor({this.namedConstructorOnly}) - : positional = null, - named = null; -} diff --git a/test/vm_tests/builder/vendor/transformer_utils/unit/analyzer_helpers_test.dart b/test/vm_tests/builder/vendor/transformer_utils/unit/analyzer_helpers_test.dart index 25c35e875..9b13f9adc 100644 --- a/test/vm_tests/builder/vendor/transformer_utils/unit/analyzer_helpers_test.dart +++ b/test/vm_tests/builder/vendor/transformer_utils/unit/analyzer_helpers_test.dart @@ -17,7 +17,6 @@ @TestOn('vm') library; -import 'package:analyzer/dart/analysis/utilities.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:over_react/src/builder/vendor/transformer_utils/src/analyzer_helpers.dart'; import 'package:test/test.dart'; @@ -204,183 +203,4 @@ main() { }); }); }); - - group('getDeclarationsAnnotatedBy()', () { - test('no matching declarations', () { - var result = parseString( - content: - ['var a;', '@OtherAnnotation()', 'var b;', 'var c;'].join('\n'), - throwIfDiagnostics: false); - var matches = getDeclarationsAnnotatedBy(result.unit, TestAnnotation); - expect(matches, isEmpty); - }); - - test('one matching declaration', () { - var result = parseString( - content: [ - '@TestAnnotation("test")', - 'var a;', - '@OtherAnnotation()', - 'var b;', - 'var c;' - ].join('\n'), - throwIfDiagnostics: false); - var matches = getDeclarationsAnnotatedBy(result.unit, TestAnnotation); - expect(matches.length, 1); - }); - - test('multiple matching declarations', () { - var result = parseString( - content: [ - '@TestAnnotation("test")', - 'var a;', - '@OtherAnnotation()', - 'var b;', - '@TestAnnotation("test")', - 'var c;' - ].join('\n'), - throwIfDiagnostics: false); - var matches = getDeclarationsAnnotatedBy(result.unit, TestAnnotation); - expect(matches.length, 2); - }); - }); - - group('instantiateAnnotation()', () { - group('instantiates an annotation with a parameter value specified as', () { - test('a string literal', () { - var node = parseAndGetSingleMember('@TestAnnotation("hello")\nvar a;'); - final instance = instantiateAnnotation(node, TestAnnotation) as TestAnnotation; - expect(instance.positional, 'hello'); - }); - - test('a concatenated string literal', () { - var node = parseAndGetSingleMember('@TestAnnotation("he" "y")\nvar a;'); - final instance = instantiateAnnotation(node, TestAnnotation) as TestAnnotation; - expect(instance.positional, 'hey'); - }); - - test('a boolean literal', () { - var node = parseAndGetSingleMember('@TestAnnotation(true)\nvar a;'); - final instance = instantiateAnnotation(node, TestAnnotation) as TestAnnotation; - expect(instance.positional, true); - }); - - test('an integer literal', () { - var node = parseAndGetSingleMember('@TestAnnotation(1)\nvar a;'); - final instance = instantiateAnnotation(node, TestAnnotation) as TestAnnotation; - expect(instance.positional, 1); - }); - - test('a null literal', () { - var node = parseAndGetSingleMember('@TestAnnotation(null)\nvar a;'); - final instance = instantiateAnnotation(node, TestAnnotation) as TestAnnotation; - expect(instance.positional, null); - }); - }); - - group('throws when an annotation parameter value is unsupported:', () { - test('a constant expression', () { - var node = parseAndGetSingleMember('@TestAnnotation(const [])\nvar a;'); - expect(() => instantiateAnnotation(node, TestAnnotation), - throwsUnsupportedError); - }); - - test('an interpolated String', () { - var node = parseAndGetSingleMember('@TestAnnotation("\$v")\nvar a;'); - expect(() => instantiateAnnotation(node, TestAnnotation), - throwsUnsupportedError); - }); - - test('an identifier', () { - var node = - parseAndGetSingleMember('@TestAnnotation(identifier)\nvar a;'); - expect(() => instantiateAnnotation(node, TestAnnotation), - throwsUnsupportedError); - }); - - group('(except when `onUnsupportedArgument` is specified)', () { - test('positional parameter', () { - Expression? unsupportedArgument; - - final instance = instantiateAnnotation( - parseAndGetSingleMember('@TestAnnotation(const [])\nvar a;'), - TestAnnotation, onUnsupportedArgument: (Expression expression) { - unsupportedArgument = expression; - return 'value to be passed to constructor instead'; - }) as TestAnnotation; - - expect(unsupportedArgument, isA()); - expect(instance.positional, - equals('value to be passed to constructor instead'), - reason: - 'should have passed the return value of `onUnsupportedArgument` to the constructor'); - }); - - test('named parameter', () { - Expression? unsupportedArgument; - - final instance = instantiateAnnotation( - parseAndGetSingleMember( - '@TestAnnotation.namedConstructor(namedConstructorOnly: const [])\nvar a;'), - TestAnnotation, onUnsupportedArgument: (Expression expression) { - unsupportedArgument = expression; - return 'value to be passed to constructor instead'; - }) as TestAnnotation; - - expect(unsupportedArgument, isA()); - expect((unsupportedArgument! as NamedExpression).name.label.name, - equals('namedConstructorOnly')); - expect(instance.namedConstructorOnly, - equals('value to be passed to constructor instead'), - reason: - 'should have passed the return value of `onUnsupportedArgument` to the constructor'); - }); - }); - }); - - test('annotation with both named and positional parameters', () { - var node = - parseAndGetSingleMember('@TestAnnotation(1, named: 2)\nvar a;'); - final instance = instantiateAnnotation(node, TestAnnotation) as TestAnnotation; - expect(instance.positional, 1); - expect(instance.named, 2); - }); - - test('instantiates an annotation using a named constructor', () { - var node = parseAndGetSingleMember( - '@TestAnnotation.namedConstructor(namedConstructorOnly: true)\nvar a;'); - final instance = instantiateAnnotation(node, TestAnnotation) as TestAnnotation; - expect(instance.namedConstructorOnly, true); - }); - - test('throws if the annotation cannot be constructed', () { - var node = parseAndGetSingleMember( - '@TestAnnotation(1, 2, 3, 4, "way more parameters than were declared")\nvar a;'); - expect(() { - instantiateAnnotation(node, TestAnnotation); - }, throwsA(hasToStringValue(contains('Unable to instantiate annotation')))); - }); - - test('throws if the annotation is not used as a constructor', () { - var node = parseAndGetSingleMember('@TestAnnotation\nvar a;'); - expect(() { - instantiateAnnotation(node, TestAnnotation); - }, throwsA(hasToStringValue(contains('Annotation not invocation of constructor')))); - }); - - test('returns null when the member is not annotated', () { - var node = parseAndGetSingleMember('var a;'); - expect(instantiateAnnotation(node, TestAnnotation), isNull); - }); - - test('returns null when the member has only non-matching annotations', () { - var node = parseAndGetSingleMember('@NonexistantAnnotation\nvar a;'); - expect(instantiateAnnotation(node, TestAnnotation), isNull); - }); - - test('returns null when the member has no annotations', () { - var node = parseAndGetSingleMember('var a;'); - expect(instantiateAnnotation(node, TestAnnotation), isNull); - }); - }); } diff --git a/test/vm_tests/builder/vendor/transformer_utils/unit/node_with_meta_test.dart b/test/vm_tests/builder/vendor/transformer_utils/unit/node_with_meta_test.dart deleted file mode 100644 index 6d40676af..000000000 --- a/test/vm_tests/builder/vendor/transformer_utils/unit/node_with_meta_test.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015 Workiva Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Copied from https://github.com/Workiva/dart_transformer_utils/tree/0.2.23/test/ - -@TestOn('vm') -library; - -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:test/test.dart'; - -import 'package:over_react/src/builder/vendor/transformer_utils/transformer_utils.dart'; - -import '../test_utils.dart'; - -void main() { - group('NodeWithMeta', () { - test('instantiates and provides access to an annotation and node', () { - var member = parseAndGetSingleMember('@TestAnnotation("hello")\nvar a;'); - var nodeWithMeta = - NodeWithMeta( - member as TopLevelVariableDeclaration); - - expect(nodeWithMeta.node, same(member)); - expect(nodeWithMeta.metaNode, isNotNull); - expect(nodeWithMeta.metaNode!.name.name, 'TestAnnotation'); - expect(nodeWithMeta.meta, isNotNull); - expect(nodeWithMeta.meta!.positional, 'hello'); - }); - - test('partially instantiates an "incomplete" annotation', () { - var member = parseAndGetSingleMember( - '@TestAnnotation(someIdentifier, named: "hello")\nvar a;'); - var nodeWithMeta = - NodeWithMeta( - member as TopLevelVariableDeclaration); - - expect(nodeWithMeta.node, same(member)); - expect(nodeWithMeta.metaNode, isNotNull); - expect(nodeWithMeta.metaNode!.name.name, 'TestAnnotation'); - - expect(nodeWithMeta.isIncomplete, isTrue); - expect(nodeWithMeta.unsupportedArguments, hasLength(1)); - expect(() => nodeWithMeta.meta, throwsStateError); - - expect(nodeWithMeta.potentiallyIncompleteMeta, isNotNull, - reason: - 'should still have attempted to instantiate the incomplete annotation'); - expect(nodeWithMeta.potentiallyIncompleteMeta!.named, equals('hello'), - reason: 'should still have passed the supported argument'); - expect(nodeWithMeta.potentiallyIncompleteMeta!.positional, isNull, - reason: 'should have used null for unsupported argument'); - }); - - test('gracefully handles a node without an annotation', () { - var member = parseAndGetSingleMember('var a;'); - var nodeWithMeta = - NodeWithMeta( - member as TopLevelVariableDeclaration); - - expect(nodeWithMeta.node, same(member)); - expect(nodeWithMeta.metaNode, isNull); - expect(nodeWithMeta.meta, isNull); - }); - }); -} diff --git a/tools/analyzer_plugin/lib/src/assist/toggle_stateful.dart b/tools/analyzer_plugin/lib/src/assist/toggle_stateful.dart index 4adf0fbff..ab9cd952e 100644 --- a/tools/analyzer_plugin/lib/src/assist/toggle_stateful.dart +++ b/tools/analyzer_plugin/lib/src/assist/toggle_stateful.dart @@ -47,7 +47,7 @@ class ToggleComponentStatefulness extends AssistContributorBase with ComponentDe await super.computeAssists(request, collector); if (!setupCompute() || !initializeAssistApi(request.result.content)) return; - newComponentBaseClass = _getNewBase(componentSupertypeNode.name.name); + newComponentBaseClass = _getNewBase(componentSupertypeNode.nameLexeme); // If there is no known corresponding base class, short circuit. if (newComponentBaseClass == null) return; diff --git a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart index 81c75407a..8f8f5efb2 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart @@ -34,6 +34,7 @@ import 'package:analyzer_plugin/protocol/protocol_common.dart' show Location; import 'package:over_react_analyzer_plugin/src/diagnostic/analyzer_debug_helper.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart'; import 'package:over_react_analyzer_plugin/src/indent_util.dart'; +import 'package:over_react_analyzer_plugin/src/over_react_builder_parsing.dart' show TypeNameHelper; import 'package:over_react_analyzer_plugin/src/util/ast_util.dart'; import 'package:over_react_analyzer_plugin/src/util/function_components.dart'; import 'package:over_react_analyzer_plugin/src/util/pretty_print.dart'; @@ -680,7 +681,7 @@ class ExhaustiveDeps extends DiagnosticContributor { // NamedType case. This is for references to generic type parameters // (references to other types will get filtered out by the isDeclaredInPureScope check above). (reference) { - dependency = reference.name.name; + dependency = reference.nameLexeme; // These aren't possible for type annotations. isStable = false; isUsedAsCascadeTarget = false; @@ -1980,7 +1981,7 @@ _Recommendations collectRecommendations({ String? getConstructionExpressionType(Expression node) { if (node is InstanceCreationExpression) { if (node.isConst) return null; - return node.constructorName.type.name.name; + return node.constructorName.type.nameLexeme; } else if (node is ListLiteral) { return _DepType.list; } else if (node is SetOrMapLiteral) { diff --git a/tools/analyzer_plugin/lib/src/diagnostic/visitors/non_static_reference_visitor.dart b/tools/analyzer_plugin/lib/src/diagnostic/visitors/non_static_reference_visitor.dart index 46596437d..083383090 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/visitors/non_static_reference_visitor.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/visitors/non_static_reference_visitor.dart @@ -70,7 +70,7 @@ bool referencesImplicitThis(SimpleIdentifier identifier) { } // not a class member final Element? enclosingElement = element.enclosingElement; - if (enclosingElement is! InterfaceOrAugmentationElement) { + if (enclosingElement is! InterfaceElement) { return false; } // comment diff --git a/tools/analyzer_plugin/lib/src/doc_utils/docs_meta_annotation.dart b/tools/analyzer_plugin/lib/src/doc_utils/docs_meta_annotation.dart index 234a8e6c1..db2bc10eb 100644 --- a/tools/analyzer_plugin/lib/src/doc_utils/docs_meta_annotation.dart +++ b/tools/analyzer_plugin/lib/src/doc_utils/docs_meta_annotation.dart @@ -6,7 +6,7 @@ import 'package:over_react_analyzer_plugin/src/doc_utils/maturity.dart'; /// * `AssistKind` static field within an assist class instance /// * `DiagnosticCode` static field within a `DiagnosticContributor` instance /// -/// > See: [DocumentedAssistContributorMeta.fromAnnotatedFieldAst] and [DocumentedDiagnosticContributorMeta..fromAnnotatedField] +/// > See: `getAssistDocsMetaFromNode` and `getDiagnosticDocsMetaFromNode` class DocsMeta implements IContributorMetaBase { @override final String description; diff --git a/tools/analyzer_plugin/lib/src/doc_utils/documented_contributor_meta.dart b/tools/analyzer_plugin/lib/src/doc_utils/documented_contributor_meta.dart index 4eeb2aa9d..c484b2866 100644 --- a/tools/analyzer_plugin/lib/src/doc_utils/documented_contributor_meta.dart +++ b/tools/analyzer_plugin/lib/src/doc_utils/documented_contributor_meta.dart @@ -27,10 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import 'package:analyzer/dart/element/element.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart'; import 'package:over_react_analyzer_plugin/src/doc_utils/maturity.dart'; -import 'package:over_react_analyzer_plugin/src/util/constant_instantiation.dart'; /// An interface implemented by registered metadata models and the annotation(s) /// that enable contribution to those models. @@ -68,25 +66,12 @@ abstract class DocumentedContributorMetaBase implements IContributorMetaBase { /// /// > __Required.__ String get name; - - /// Returns a `DocsMeta` instance constructed by parsing the AST of the provided annotated [element]. - static DocsMeta getDocsMetaFromAnnotation(FieldElement element) { - final annotation = - element.metadata.singleWhere((a) => a.element!.thisOrAncestorOfType()!.name == 'DocsMeta'); - final annotationObj = annotation.computeConstantValue()!; - final description = annotationObj.getField('description')!.toStringValue()!; - final details = annotationObj.getField('details')!.toStringValue(); - final maturity = getMatchingConst(annotationObj.getField('maturity')!, Maturity.VALUES); - final since = annotationObj.getField('since')!.toStringValue() ?? IContributorMetaBase.defaultSince; - - return DocsMeta(description, details: details, since: since, maturity: maturity); - } } /// Documentation metadata for an "assist" contributor. /// /// Must be constructed by parsing the AST of an element annotated with a [DocsMeta] annotation -/// via the [DocumentedAssistContributorMeta.fromAnnotatedFieldAst] factory constructor. +/// via the [`getAssistDocsMetaFromNode`] constructor. class DocumentedAssistContributorMeta extends DocumentedContributorMetaBase implements Comparable { @override @@ -100,27 +85,11 @@ class DocumentedAssistContributorMeta extends DocumentedContributorMetaBase @override final Maturity maturity; - DocumentedAssistContributorMeta._(this.name, this.description, + DocumentedAssistContributorMeta(this.name, this.description, {this.details, this.since = IContributorMetaBase.defaultSince, this.maturity = IContributorMetaBase.defaultMaturity}); - /// Creates a new instance from the field [element] annotated with a [DocsMeta] annotation. - factory DocumentedAssistContributorMeta.fromAnnotatedFieldAst(FieldElement element) { - final metaFromAnnotation = DocumentedContributorMetaBase.getDocsMetaFromAnnotation(element); - - final assistKindObj = element.computeConstantValue()!; - final name = assistKindObj.getField('id')!.toStringValue()!; - - return DocumentedAssistContributorMeta._( - name, - metaFromAnnotation.description, - details: metaFromAnnotation.details, - since: metaFromAnnotation.since, - maturity: metaFromAnnotation.maturity, - ); - } - @override int compareTo(DocumentedAssistContributorMeta other) { return name.compareTo(other.name); @@ -130,7 +99,7 @@ class DocumentedAssistContributorMeta extends DocumentedContributorMetaBase /// Documentation metadata for a "diagnostic" contributor _(e.g. lint / "rule")_. /// /// Must be constructed by parsing the AST of an element annotated with a [DocsMeta] annotation -/// via the [DocumentedDiagnosticContributorMeta..fromAnnotatedField] factory constructor. +/// via the `getDiagnosticDocsMetaFromNode` factory constructor. class DocumentedDiagnosticContributorMeta extends DocumentedContributorMetaBase implements Comparable { @override @@ -154,7 +123,7 @@ class DocumentedDiagnosticContributorMeta extends DocumentedContributorMetaBase /// > Should always match the [DiagnosticCode.type] value. final AnalysisErrorType type; - DocumentedDiagnosticContributorMeta._( + DocumentedDiagnosticContributorMeta( this.name, this.description, { required this.severity, @@ -164,26 +133,6 @@ class DocumentedDiagnosticContributorMeta extends DocumentedContributorMetaBase this.details, }); - /// Creates a new instance from the field [element] annotated with a [DocsMeta] annotation. - factory DocumentedDiagnosticContributorMeta.fromAnnotatedField(FieldElement element) { - final metaFromAnnotation = DocumentedContributorMetaBase.getDocsMetaFromAnnotation(element); - - final diagnosticCodeObj = element.computeConstantValue()!; - final name = diagnosticCodeObj.getField('name')!.toStringValue()!; - final severity = diagnosticCodeObj.getField('errorSeverity')!; - final type = diagnosticCodeObj.getField('type')!; - - return DocumentedDiagnosticContributorMeta._( - name, - metaFromAnnotation.description, - details: metaFromAnnotation.details, - since: metaFromAnnotation.since, - maturity: metaFromAnnotation.maturity, - severity: getMatchingConst(severity, AnalysisErrorSeverity.VALUES), - type: getMatchingConst(type, AnalysisErrorType.VALUES), - ); - } - @override int compareTo(DocumentedDiagnosticContributorMeta other) { var g = severity.name.compareTo(other.severity.name); diff --git a/tools/analyzer_plugin/pubspec.yaml b/tools/analyzer_plugin/pubspec.yaml index 4f36b1eae..39d8bdb45 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -6,7 +6,7 @@ repository: https://github.com/Workiva/over_react/tree/master/tools/analyzer_plu environment: sdk: '>=2.19.0 <3.0.0' dependencies: - analyzer: ^5.11.0 + analyzer: '>=5.11.0 <7.0.0' analyzer_plugin: ^0.11.0 collection: ^1.15.0-nullsafety.4 meta: ^1.16.0 diff --git a/tools/analyzer_plugin/test/integration/stubs.dart b/tools/analyzer_plugin/test/integration/stubs.dart index 6af6f0050..b66650633 100644 --- a/tools/analyzer_plugin/test/integration/stubs.dart +++ b/tools/analyzer_plugin/test/integration/stubs.dart @@ -101,6 +101,7 @@ class StubServerPlugin implements ServerPlugin { handleEditGetRefactoring(parameters) => throw UnimplementedError(); @override + // ignore: override_on_non_overriding_member handleKytheGetKytheEntries(parameters) => throw UnimplementedError(); @override diff --git a/tools/analyzer_plugin/test/unit/util/prop_declaration/util.dart b/tools/analyzer_plugin/test/unit/util/prop_declaration/util.dart index fd19f2242..3730ba48b 100644 --- a/tools/analyzer_plugin/test/unit/util/prop_declaration/util.dart +++ b/tools/analyzer_plugin/test/unit/util/prop_declaration/util.dart @@ -42,6 +42,8 @@ Future resolveFileAndGeneratedPart(SharedAnalysisContext sha .where((e) => e.severity != Severity.info && !e.errorCode.name.toLowerCase().startsWith('unused_')) // FIXME(FED-2015) remove once these are properly ignored in generated code .where((e) => e.errorCode.name.toLowerCase() != 'invalid_use_of_visible_for_overriding_member') + // For some reason this is triggering for '$mustCallSuper'; ignore for now. TODO look into what's going on here + .where((e) => e.errorCode.name.toLowerCase() != 'invalid_annotation_target') .toList(); final libraryResult = await getResolvedUnit(libraryFullPath); diff --git a/tools/analyzer_plugin/tool/doc.dart b/tools/analyzer_plugin/tool/doc.dart index 08819c23e..f226c2d52 100644 --- a/tools/analyzer_plugin/tool/doc.dart +++ b/tools/analyzer_plugin/tool/doc.dart @@ -35,6 +35,7 @@ import 'package:over_react_analyzer_plugin/src/doc_utils/contributor_meta_regist import 'package:over_react_analyzer_plugin/src/doc_utils/documented_contributor_meta.dart'; import 'package:over_react_analyzer_plugin/src/util/constants.dart'; +import 'doc_generation_utils/annotations_from_ast.dart'; import 'doc_generation_utils/config.dart'; import 'doc_generation_utils/generate_assist_docs.dart'; import 'doc_generation_utils/generate_rule_docs.dart'; @@ -50,7 +51,7 @@ final configs = [ getSortedContributorMetas: () => RulesIndexer.rules, typeNameOfContributorClass: 'DiagnosticContributor', typeNameOfAnnotatedField: 'DiagnosticCode', - getMeta: DocumentedDiagnosticContributorMeta.fromAnnotatedField, + getMeta: getDiagnosticDocsMetaFromNode, getPageGenerator: (meta) => RuleDocGenerator(meta as DocumentedDiagnosticContributorMeta), getIndexGenerator: RulesIndexer.new, generateAdditionalDocs: (outDir) => OptionsSample(RulesIndexer.rules).generate(outDir), @@ -63,7 +64,7 @@ final configs = [ typeNameOfContributorClass: 'AssistContributorBase', typeNameOfAnnotatedField: 'AssistKind', packageNameContainingAnnotatedFieldType: 'analyzer_plugin', - getMeta: DocumentedAssistContributorMeta.fromAnnotatedFieldAst, + getMeta: getAssistDocsMetaFromNode, getPageGenerator: (meta) => AssistDocGenerator(meta as DocumentedAssistContributorMeta), getIndexGenerator: AssistsIndexer.new, ), diff --git a/tools/analyzer_plugin/tool/doc_generation_utils/annotations_from_ast.dart b/tools/analyzer_plugin/tool/doc_generation_utils/annotations_from_ast.dart new file mode 100644 index 000000000..ca5e25642 --- /dev/null +++ b/tools/analyzer_plugin/tool/doc_generation_utils/annotations_from_ast.dart @@ -0,0 +1,55 @@ +import 'package:analyzer/dart/element/element.dart'; +import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart'; +import 'package:over_react_analyzer_plugin/src/doc_utils/documented_contributor_meta.dart'; +import 'package:over_react_analyzer_plugin/src/doc_utils/maturity.dart'; + +import 'constant_instantiation.dart'; + +/// Returns a `DocsMeta` instance constructed by parsing the AST of the provided annotated [element]. +DocsMeta _getDocsMetaFromAnnotation(FieldElement element) { + final annotation = + element.metadata.singleWhere((a) => a.element!.thisOrAncestorOfType()!.name == 'DocsMeta'); + final annotationObj = annotation.computeConstantValue()!; + final description = annotationObj.getField('description')!.toStringValue()!; + final details = annotationObj.getField('details')!.toStringValue(); + final maturity = getMatchingConst(annotationObj.getField('maturity')!, Maturity.VALUES); + final since = annotationObj.getField('since')!.toStringValue() ?? IContributorMetaBase.defaultSince; + + return DocsMeta(description, details: details, since: since, maturity: maturity); +} + +/// Creates a new instance from the field [element] annotated with a [DocsMeta] annotation. +DocumentedDiagnosticContributorMeta getDiagnosticDocsMetaFromNode(FieldElement element) { + final metaFromAnnotation = _getDocsMetaFromAnnotation(element); + + final diagnosticCodeObj = element.computeConstantValue()!; + final name = diagnosticCodeObj.getField('name')!.toStringValue()!; + final severity = diagnosticCodeObj.getField('errorSeverity')!; + final type = diagnosticCodeObj.getField('type')!; + + return DocumentedDiagnosticContributorMeta( + name, + metaFromAnnotation.description, + details: metaFromAnnotation.details, + since: metaFromAnnotation.since, + maturity: metaFromAnnotation.maturity, + severity: getMatchingConst(severity, AnalysisErrorSeverity.VALUES), + type: getMatchingConst(type, AnalysisErrorType.VALUES), + ); +} + +/// Creates a new instance from the field [element] annotated with a [DocsMeta] annotation. +DocumentedAssistContributorMeta getAssistDocsMetaFromNode(FieldElement element) { + final metaFromAnnotation = _getDocsMetaFromAnnotation(element); + + final assistKindObj = element.computeConstantValue()!; + final name = assistKindObj.getField('id')!.toStringValue()!; + + return DocumentedAssistContributorMeta( + name, + metaFromAnnotation.description, + details: metaFromAnnotation.details, + since: metaFromAnnotation.since, + maturity: metaFromAnnotation.maturity, + ); +} diff --git a/tools/analyzer_plugin/lib/src/util/constant_instantiation.dart b/tools/analyzer_plugin/tool/doc_generation_utils/constant_instantiation.dart similarity index 100% rename from tools/analyzer_plugin/lib/src/util/constant_instantiation.dart rename to tools/analyzer_plugin/tool/doc_generation_utils/constant_instantiation.dart