diff --git a/.github/workflows/dart_ci.yml b/.github/workflows/dart_ci.yml index 745cebcfc..5fb6c84f0 100644 --- a/.github/workflows/dart_ci.yml +++ b/.github/workflows/dart_ci.yml @@ -16,9 +16,7 @@ jobs: fail-fast: false matrix: # Can't run on `dev` (Dart 3) until we're fully null-safe. - # We can't run on Dart >=2.19 until https://github.com/dart-lang/sdk/issues/51128 - # is fixed, or we work around it, which requires upgrading to analyzer ^5.1.0. - sdk: [ 2.13.4, 2.18.7 ] + sdk: [ 2.18.7, 2.19.6 ] steps: - uses: actions/checkout@v2 - uses: dart-lang/setup-dart@v0.2 @@ -88,16 +86,11 @@ jobs: fail-fast: false matrix: # Can't run on `dev` (Dart 3) until we're fully null-safe. - # We can't run on Dart >=2.19 until https://github.com/dart-lang/sdk/issues/51128 - # is fixed, or we work around it, which requires upgrading to analyzer ^5.1.0. - sdk: [ 2.13.4, 2.18.7 ] + sdk: [ 2.18.7, 2.19.6 ] analyzer: - - ^1.0.0 - - ^2.0.0 - # Don't validate analyzer ^2.0.0 in 2.13.4 since it can't resolve. - exclude: - - sdk: 2.13.4 - analyzer: ^2.0.0 + # We only have one version currently, but we'll leave this CI step in place + # for the next time we need to support multiple analyzer versions. + - ^5.1.0 steps: - uses: actions/checkout@v2 - uses: dart-lang/setup-dart@v0.2 diff --git a/.gitignore b/.gitignore index c70d3dfb0..095463ceb 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ pubspec.lock # Should not track the output of code coverage for now coverage/ + +tools/analyzer_plugin/test/test_fixtures/**/lib/dynamic_test_files/ +tools/analyzer_plugin/test/temporary_test_fixtures/** diff --git a/Dockerfile b/Dockerfile index a89342ca1..6146f797a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM google/dart:2.13 +FROM dart:2.18 # Expose env vars for git ssh access ARG GIT_SSH_KEY diff --git a/app/over_react_redux/todo_client/pubspec.yaml b/app/over_react_redux/todo_client/pubspec.yaml index 6c2ebede4..1b4556607 100644 --- a/app/over_react_redux/todo_client/pubspec.yaml +++ b/app/over_react_redux/todo_client/pubspec.yaml @@ -26,7 +26,7 @@ dev_dependencies: test: ^1.15.7 test_html_builder: ^3.0.0 time: ^1.2.0 - w_common: '>=2.0.0 <4.0.0' + w_common: ^3.0.0 workiva_analysis_options: ^1.1.0 dependency_overrides: diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index 2849191e7..162bd966c 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -54,7 +54,7 @@ abstract class TypedMapAccessorsGenerator extends BoilerplateDeclarationGenerato String get accessorsMixinName; - ClassOrMixinDeclaration get node => member.node as ClassOrMixinDeclaration; + ClassishDeclaration get node => member.node.asClassish(); TypeParameterList get typeParameters => member.nodeHelper.typeParameters; @@ -363,7 +363,9 @@ class _TypedMapMixinAccessorsGenerator extends TypedMapAccessorsGenerator { @override void generate() { - outputContentsBuffer..write(_generateAccessorsMixin())..write(_generateMetaConstImpl()); + outputContentsBuffer + ..write(_generateAccessorsMixin()) + ..write(_generateMetaConstImpl()); } } @@ -422,9 +424,8 @@ class _LegacyTypedMapAccessorsGenerator extends TypedMapAccessorsGenerator { return ''; } - final classishNode = node.asClassish(); - final metadata = classishNode.metadata; - final typeParameters = classishNode.typeParameters; + final metadata = node.metadata; + final typeParameters = node.typeParameters; final typeParamsOnClass = typeParameters?.toSource() ?? ''; final typeParamsOnSuper = removeBoundsFromTypeParameters(typeParameters); final accessorsMixinName = names.legacyAccessorsMixinName; @@ -462,7 +463,7 @@ class TypedMapType { static const TypedMapType stateMixin = TypedMapType(false, false, true); } -String _copyClassMembers(ClassOrMixinDeclaration node) { +String _copyClassMembers(ClassishDeclaration node) { bool isValidForCopying(ClassMember member) { // Static members should be copied over as needed by [_copyStaticMembers]. // Otherwise, fields which are not synthetic have concrete getters/setters @@ -493,7 +494,7 @@ String _copyClassMembers(ClassOrMixinDeclaration node) { return buffer.toString(); } -String _copyStaticMembers(ClassOrMixinDeclaration node) { +String _copyStaticMembers(ClassishDeclaration node) { final buffer = StringBuffer(); node.members.where(isStaticMember).forEach((member) { // Don't copy over anything named `meta`, since the static meta field is already going to be generated. diff --git a/lib/src/builder/codegen/component_factory_generator.dart b/lib/src/builder/codegen/component_factory_generator.dart index 32d1bad74..a055e7940 100644 --- a/lib/src/builder/codegen/component_factory_generator.dart +++ b/lib/src/builder/codegen/component_factory_generator.dart @@ -100,6 +100,8 @@ class ComponentFactoryProxyGenerator extends BoilerplateDeclarationGenerator { outputContentsBuffer.writeln(' skipMethods: const [],'); } - outputContentsBuffer..writeln(');')..writeln(); + outputContentsBuffer + ..writeln(');') + ..writeln(); } } diff --git a/lib/src/builder/codegen/typed_map_impl_generator.dart b/lib/src/builder/codegen/typed_map_impl_generator.dart index 936d25699..f0691bed3 100644 --- a/lib/src/builder/codegen/typed_map_impl_generator.dart +++ b/lib/src/builder/codegen/typed_map_impl_generator.dart @@ -133,7 +133,7 @@ abstract class TypedMapImplGenerator extends BoilerplateDeclarationGenerator { String _generateConcretePropsOrStateImpl({ String componentFactoryName, String propKeyNamespace, - List allPropsMixins, + List allPropsMixins, }) { if (isProps) { if (componentFactoryName == null || propKeyNamespace == null) { @@ -153,7 +153,9 @@ abstract class TypedMapImplGenerator extends BoilerplateDeclarationGenerator { classDeclaration.write('abstract '); } - classDeclaration..write(_generateImplClassHeader())..write(' {'); + classDeclaration + ..write(_generateImplClassHeader()) + ..write(' {'); final propsOrState = isProps ? 'props' : 'state'; @@ -235,7 +237,9 @@ abstract class TypedMapImplGenerator extends BoilerplateDeclarationGenerator { // Component2-specific classes if (isComponent2) { // TODO need to remove this workaround once https://github.com/dart-lang/sdk/issues/36217 is fixed get nice dart2js output - buffer..writeln()..writeln(''' + buffer + ..writeln() + ..writeln(''' // Concrete $propsOrState implementation that can be backed by any [Map]. ${internalGeneratedMemberDeprecationLine()}class ${names.plainMapImplName}$typeParamsOnClass extends ${names.implName}$typeParamsOnSuper { // This initializer of `_$propsOrState` to an empty map, as well as the reassignment @@ -350,7 +354,7 @@ class _TypedMapImplGenerator extends TypedMapImplGenerator { @override final Version version; - final List allPropsMixins; + final List allPropsMixins; _TypedMapImplGenerator.props(ClassComponentDeclaration declaration) : names = TypedMapNames(declaration.props.either.name.name), @@ -434,8 +438,9 @@ class _TypedMapImplGenerator extends TypedMapImplGenerator { return 'class ${names.implName}$typeParamsOnClass' ' extends ${isProps ? 'UiProps' : 'UiState'}' ' with\n' - ' ${names.consumerName}$typeParamsOnSuper\n,' - ' ${names.generatedMixinName}$typeParamsOnSuper${generatedMixinWarningCommentLine(names, isProps: isProps)}'; + ' ${names.consumerName}$typeParamsOnSuper,\n' + ' ${generatedMixinWarningCommentLine(names, isProps: isProps)}' + ' ${names.generatedMixinName}$typeParamsOnSuper'; } else if (member is BoilerplatePropsOrState) { final header = StringBuffer() ..write('class ${names.implName}$typeParamsOnClass' @@ -462,14 +467,14 @@ class _TypedMapImplGenerator extends TypedMapImplGenerator { final names = TypedMapNames(mixin.name.name); header.write('${names.consumerName}$typeArguments'); header.write(','); + // Add a space at the beginning of the line so that dartfmt indents it + // with the following line, as opposed to "sticking" it to the beginning of the line. + header.write('\n ' + generatedMixinWarningCommentLine(names, isProps: isProps)); header.write('${names.generatedMixinName}$typeArguments'); // Don't write the comma if we're at the end of the list. - // Do this manually instead of using `.join` so that we can always have - // the warning comment be at the end of the line, regardless of whether the comma is there. if (i != mixins.length - 1) { header.write(','); } - header.write(generatedMixinWarningCommentLine(names, isProps: isProps)); } } diff --git a/lib/src/builder/codegen/util.dart b/lib/src/builder/codegen/util.dart index 143b41735..4d4681aaf 100644 --- a/lib/src/builder/codegen/util.dart +++ b/lib/src/builder/codegen/util.dart @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:analyzer/dart/ast/ast.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:over_react/src/component_declaration/annotations.dart' as annotations; @@ -70,7 +69,7 @@ String generatedMixinWarningCommentLine(TypedMapNames mixinNames, {@required boo void generatePropsMeta( StringBuffer buffer, - List mixins, { + List mixins, { String classType = 'PropsMetaCollection', String fieldName = 'propsMeta', }) { @@ -79,7 +78,7 @@ void generatePropsMeta( ..writeln(' @override') ..writeln(' $classType get $fieldName => const $classType({'); for (final name in mixins) { - final names = TypedMapNames(name.name); + final names = TypedMapNames(name); buffer.write(' ${generatedMixinWarningCommentLine(names, isProps: true)}'); buffer.writeln(' ${names.consumerName}: ${names.publicGeneratedMetaName},'); } diff --git a/lib/src/builder/parsing/ast_util.dart b/lib/src/builder/parsing/ast_util.dart index d42475f9d..e787eae8f 100644 --- a/lib/src/builder/parsing/ast_util.dart +++ b/lib/src/builder/parsing/ast_util.dart @@ -93,8 +93,8 @@ extension SuperclassConstraint on MixinDeclaration { } } -/// Utilities for determining if a [ClassOrMixinDeclaration] has an abstract getter. -extension AbstractGetter on ClassOrMixinDeclaration { +/// Utilities for determining if a declaration has an abstract getter. +extension AbstractGetter on ClassishDeclaration { /// Returns whether this class/mixin contains an abstract getter with the provided [name] /// and a return type exactly matching [type] bool hasAbstractGetter(String type, String name) => @@ -106,6 +106,15 @@ extension AbstractGetter on ClassOrMixinDeclaration { member.returnType?.toSource() == type); } +/// An extension that supports APIs that changed from [Identifier] to [Token], +/// in order to cut down on diffs in the analyzer 5 upgrade (and subsequent +/// merge conflicts with the null-safety branch. +/// +/// TODO remove this and inline the [name] member. +extension NameIdentifierTokenCompat on Token { + String get name => lexeme; +} + /// Utilities that provide for easier access to [AnnotatedNode] metadata. extension MetadataHelper on AnnotatedNode { // Annotations don't always parse as expected, so `.name` can also include the constructor diff --git a/lib/src/builder/parsing/ast_util/classish.dart b/lib/src/builder/parsing/ast_util/classish.dart index 86100f036..63b283a24 100644 --- a/lib/src/builder/parsing/ast_util/classish.dart +++ b/lib/src/builder/parsing/ast_util/classish.dart @@ -19,7 +19,8 @@ extension Classish on NamedCompilationUnitMember { ClassishDeclaration asClassish() => ClassishDeclaration(this); } -/// Provides a common interface for [ClassOrMixinDeclaration] and [ClassTypeAlias]. +/// Provides a common interface for [ClassDeclaration], [MixinDeclaration], +/// and [ClassTypeAlias]. abstract class ClassishDeclaration { factory ClassishDeclaration(NamedCompilationUnitMember node) { if (node is ClassDeclaration) { @@ -41,7 +42,7 @@ abstract class ClassishDeclaration { // // Shared - SimpleIdentifier get name => node.name; + Token get name => node.name; NodeList get metadata => node.metadata; TypeParameterList get typeParameters; @@ -49,9 +50,9 @@ abstract class ClassishDeclaration { Token get classOrMixinKeyword; /// All interfaces used by this class, including mixin superclass constraints. - List get interfaces; + List get interfaces; - List get allSuperTypes => [ + List get allSuperTypes => [ ...interfaces, ...mixins, if (superclass != null) superclass, @@ -63,25 +64,12 @@ abstract class ClassishDeclaration { WithClause get withClause; Token get abstractKeyword; bool get hasAbstractKeyword => abstractKeyword != null; - TypeName get superclass; + NamedType get superclass; - List get mixins => withClause?.mixinTypes ?? const []; + List get mixins => withClause?.mixinTypes ?? const []; } -abstract class _ClassishClassOrMixin extends ClassishDeclaration { - _ClassishClassOrMixin._() : super._(); - - @override - ClassOrMixinDeclaration get node; - - @override - List get members => node.members; - - @override - TypeParameterList get typeParameters => node.typeParameters; -} - -class _ClassishClass extends _ClassishClassOrMixin { +class _ClassishClass extends ClassishDeclaration { @override final ClassDeclaration node; @@ -91,21 +79,27 @@ class _ClassishClass extends _ClassishClassOrMixin { Token get abstractKeyword => node.abstractKeyword; @override - List get interfaces => [ + List get interfaces => [ ...?node.implementsClause?.interfaces, ]; @override - TypeName get superclass => node.extendsClause?.superclass; + NamedType get superclass => node.extendsClause?.superclass; @override WithClause get withClause => node.withClause; @override Token get classOrMixinKeyword => node.classKeyword; + + @override + List get members => node.members; + + @override + TypeParameterList get typeParameters => node.typeParameters; } -class _ClasssishMixin extends _ClassishClassOrMixin { +class _ClasssishMixin extends ClassishDeclaration { @override final MixinDeclaration node; @@ -118,16 +112,22 @@ class _ClasssishMixin extends _ClassishClassOrMixin { Token get classOrMixinKeyword => node.mixinKeyword; @override - List get interfaces => [ + List get interfaces => [ ...?node.implementsClause?.interfaces, ...?node.onClause?.superclassConstraints, ]; @override - TypeName get superclass => null; + NamedType get superclass => null; @override WithClause get withClause => null; + + @override + List get members => node.members; + + @override + TypeParameterList get typeParameters => node.typeParameters; } class _ClassishClassTypeAlias extends ClassishDeclaration { @@ -146,7 +146,7 @@ class _ClassishClassTypeAlias extends ClassishDeclaration { List get members => const []; @override - TypeName get superclass => node.superclass; + NamedType get superclass => node.superclass; @override TypeParameterList get typeParameters => node.typeParameters; @@ -155,7 +155,7 @@ class _ClassishClassTypeAlias extends ClassishDeclaration { WithClause get withClause => node.withClause; @override - List get interfaces => [ + List get interfaces => [ ...?node.implementsClause?.interfaces, ]; } diff --git a/lib/src/builder/parsing/declarations.dart b/lib/src/builder/parsing/declarations.dart index fed8ae3d7..c66dad2cd 100644 --- a/lib/src/builder/parsing/declarations.dart +++ b/lib/src/builder/parsing/declarations.dart @@ -188,7 +188,7 @@ mixin _TypedMapMixinShorthandDeclaration { final mixin = propsOrState.b; if (mixin == null) return; - bool isBadConstraint(TypeName constraint) { + bool isBadConstraint(NamedType constraint) { final name = constraint.nameWithoutPrefix; return name != helpers.propsOrStateBaseClassString && helpers.propsOrStateMixinNamePattern.hasMatch(name); @@ -226,9 +226,9 @@ extension on Union { /// /// This is the safest way to retrieve that information because it takes /// into account the nature of the [Union] typing of `props`. - List get allPropsMixins => this.switchCase( - (a) => a.nodeHelper.mixins.map((name) => name.name).toList(), - (b) => [b.name], + List get allPropsMixins => this.switchCase( + (a) => a.nodeHelper.mixins.map((name) => name.name.name).toList(), + (b) => [b.name.name], ); } @@ -249,7 +249,7 @@ class ClassComponentDeclaration extends BoilerplateDeclaration @override get type => DeclarationType.classComponentDeclaration; - List get allPropsMixins => props.allPropsMixins; + List get allPropsMixins => props.allPropsMixins; @override void validate(ErrorCollector errorCollector) { @@ -285,7 +285,7 @@ class PropsMapViewOrFunctionComponentDeclaration extends BoilerplateDeclaration /// Can be either [BoilerplateProps] or [BoilerplatePropsMixin], but not both. final Union props; - List get allPropsMixins => props.allPropsMixins; + List get allPropsMixins => props.allPropsMixins; @override get _members => [...factories, props.either]; diff --git a/lib/src/builder/parsing/members.dart b/lib/src/builder/parsing/members.dart index f8c843d35..1e2c49828 100644 --- a/lib/src/builder/parsing/members.dart +++ b/lib/src/builder/parsing/members.dart @@ -13,6 +13,8 @@ // limitations under the License. import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/token.dart'; +import 'package:collection/collection.dart'; import 'package:meta/meta.dart'; import 'package:over_react/src/builder/codegen/names.dart'; import 'package:over_react/src/component_declaration/annotations.dart' as annotations; @@ -58,7 +60,7 @@ abstract class BoilerplateMember { /// given version. void validate(Version version, ErrorCollector errorCollector); - SimpleIdentifier get name; + Token get name; @override String toString() => '$runtimeType: ${name.name}'; diff --git a/lib/src/builder/parsing/members/component.dart b/lib/src/builder/parsing/members/component.dart index 2a2b8d64a..2f8b84277 100644 --- a/lib/src/builder/parsing/members/component.dart +++ b/lib/src/builder/parsing/members/component.dart @@ -32,7 +32,7 @@ class BoilerplateComponent extends BoilerplateMember { final NamedCompilationUnitMember node; @override - SimpleIdentifier get name => nodeHelper.name; + Token get name => nodeHelper.name; /// A metadata class that lifts helpful fields out of [node] to a top level, /// in addition to providing additional getters relevant member parsing. @@ -119,15 +119,16 @@ class BoilerplateComponent extends BoilerplateMember { }); // Ensure that Component2 declarations do not use legacy lifecycle methods. - if (isComponent2(version) && node is ClassOrMixinDeclaration) { + if (isComponent2(version)) { Map legacyLifecycleMethodsMap = { 'componentWillReceiveProps': 'Use getDerivedStateFromProps instead.', 'componentWillMount': 'Use componentDidMount instead.', 'componentWillUpdate': 'Use getSnapshotBeforeUpdate and/or componentDidUpdate instead.', }; + final methods = nodeHelper.members.whereType().toList(); legacyLifecycleMethodsMap.forEach((methodName, helpMessage) { - final method = (node as ClassOrMixinDeclaration).getMethod(methodName); + final method = methods.firstWhereOrNull((m) => m.name.name == methodName); if (method != null) { errorCollector.addError(unindent(''' When using Component2, a class cannot use ${method.name} because React 16 has removed ${method.name} diff --git a/lib/src/builder/parsing/members/factory.dart b/lib/src/builder/parsing/members/factory.dart index d7053f3b7..79a3290b8 100644 --- a/lib/src/builder/parsing/members/factory.dart +++ b/lib/src/builder/parsing/members/factory.dart @@ -23,7 +23,7 @@ class BoilerplateFactory extends BoilerplateMember { final TopLevelVariableDeclaration node; @override - SimpleIdentifier get name => node.firstVariable.name; + Token get name => node.firstVariable.name; @override annotations.Factory get meta => const annotations.Factory(); diff --git a/lib/src/builder/parsing/members/props_and_state.dart b/lib/src/builder/parsing/members/props_and_state.dart index 68572d8b2..4f6f36249 100644 --- a/lib/src/builder/parsing/members/props_and_state.dart +++ b/lib/src/builder/parsing/members/props_and_state.dart @@ -47,7 +47,7 @@ abstract class BoilerplatePropsOrState extends BoilerplateTypedMapMember annotations.TypedMap meta; @override - SimpleIdentifier get name => nodeHelper.name; + Token get name => nodeHelper.name; @override String get debugString => '${super.debugString}, companion: ${companion?.name}'; @@ -110,15 +110,13 @@ abstract class BoilerplatePropsOrState extends BoilerplateTypedMapMember break; case Version.v3_legacyDart2Only: _sharedLegacyValidation(errorCollector); - if (node is ClassOrMixinDeclaration) { - checkForMetaPresence(node as ClassOrMixinDeclaration, errorCollector); - } + checkForMetaPresence(nodeHelper, errorCollector); break; } } void _sharedLegacyValidation(ErrorCollector errorCollector) { - if (node is! ClassOrMixinDeclaration) { + if (node is! ClassDeclaration && node is! MixinDeclaration) { errorCollector.addError( 'Legacy boilerplate must use classes or mixins, and not shorthand class declaration', errorCollector.spanFor(node)); diff --git a/lib/src/builder/parsing/members/props_and_state_mixins.dart b/lib/src/builder/parsing/members/props_and_state_mixins.dart index 40f52cf94..548201a80 100644 --- a/lib/src/builder/parsing/members/props_and_state_mixins.dart +++ b/lib/src/builder/parsing/members/props_and_state_mixins.dart @@ -19,19 +19,19 @@ part of '../members.dart'; /// See [BoilerplateMember] for more information. abstract class BoilerplatePropsOrStateMixin extends BoilerplateTypedMapMember with PropsStateStringHelpers { - BoilerplatePropsOrStateMixin(this.node, this.companion, VersionConfidences confidence) + BoilerplatePropsOrStateMixin(this.nodeHelper, this.companion, VersionConfidences confidence) : super(confidence) { meta = getPropsOrStateAnnotation(isProps, node); } - /// The [MixinDeclaration] backing the member + /// The node backing the member. @override - final ClassOrMixinDeclaration node; + NamedCompilationUnitMember get node => nodeHelper.node; /// A metadata class that lifts helpful fields out of [node] to a top level, /// in addition to providing additional getters relevant member parsing. @override - ClassishDeclaration get nodeHelper => node.asClassish(); + final ClassishDeclaration nodeHelper; /// The companion class for the props or state mixins. /// @@ -46,7 +46,7 @@ abstract class BoilerplatePropsOrStateMixin extends BoilerplateTypedMapMember annotations.TypedMap meta; @override - SimpleIdentifier get name => node.name; + Token get name => nodeHelper.name; @override String get debugString => '${super.debugString}, companion: ${companion?.name}'; @@ -67,7 +67,7 @@ abstract class BoilerplatePropsOrStateMixin extends BoilerplateTypedMapMember errorCollector.spanFor(node.name)); } - if (!node.hasAbstractGetter('Map', propsOrStateString)) { + if (!nodeHelper.hasAbstractGetter('Map', propsOrStateString)) { errorCollector.addError( '$propsOrStateMixinString classes must declare an abstract $propsOrStateString getter `Map get $propsOrStateString;` ' 'so that they can be statically analyzed properly.', @@ -98,7 +98,7 @@ abstract class BoilerplatePropsOrStateMixin extends BoilerplateTypedMapMember break; case Version.v3_legacyDart2Only: _sharedLegacyValidation(); - checkForMetaPresence(node, errorCollector); + checkForMetaPresence(node.asClassish(), errorCollector); break; } } @@ -109,7 +109,7 @@ abstract class BoilerplatePropsOrStateMixin extends BoilerplateTypedMapMember /// See [BoilerplateMember] for more information. class BoilerplatePropsMixin extends BoilerplatePropsOrStateMixin { BoilerplatePropsMixin( - ClassOrMixinDeclaration node, ClassishDeclaration companion, VersionConfidences confidence) + ClassishDeclaration node, ClassishDeclaration companion, VersionConfidences confidence) : super(node, companion, confidence); @override @@ -121,7 +121,7 @@ class BoilerplatePropsMixin extends BoilerplatePropsOrStateMixin { /// See [BoilerplateMember] for more information. class BoilerplateStateMixin extends BoilerplatePropsOrStateMixin { BoilerplateStateMixin( - ClassOrMixinDeclaration node, ClassishDeclaration companion, VersionConfidences confidence) + ClassishDeclaration node, ClassishDeclaration companion, VersionConfidences confidence) : super(node, companion, confidence); @override diff --git a/lib/src/builder/parsing/members/props_and_state_util.dart b/lib/src/builder/parsing/members/props_and_state_util.dart index 811eeaf3b..d6ab575f4 100644 --- a/lib/src/builder/parsing/members/props_and_state_util.dart +++ b/lib/src/builder/parsing/members/props_and_state_util.dart @@ -68,7 +68,7 @@ annotations.TypedMap getPropsOrStateAnnotation(bool isProps, AnnotatedNode node) /// If a [ClassMember] exists in [node] with the name `meta`, this will /// throw an error if the member is not static and a warning if the member /// is static. -void checkForMetaPresence(ClassOrMixinDeclaration node, ErrorCollector errorCollector) { +void checkForMetaPresence(ClassishDeclaration node, ErrorCollector errorCollector) { final metaField = metaFieldOrNull(node); final metaMethod = metaMethodOrNull(node); final isNotNull = metaField != null || metaMethod != null; diff --git a/lib/src/builder/parsing/members_from_ast.dart b/lib/src/builder/parsing/members_from_ast.dart index e290c8cf2..8d48fb7cc 100644 --- a/lib/src/builder/parsing/members_from_ast.dart +++ b/lib/src/builder/parsing/members_from_ast.dart @@ -236,7 +236,7 @@ class _BoilerplateMemberDetector { // Special-case: `@Props()` is allowed on the new boilerplate mixins if (node is MixinDeclaration) { onPropsMixin(BoilerplatePropsMixin( - node, + classish, companion, _annotatedPropsOrStateMixinConfidence(classish, companion, disableAnnotationAssert: true))); @@ -253,7 +253,7 @@ class _BoilerplateMemberDetector { // Special-case: `@State()` is allowed on the new boilerplate mixins if (node is MixinDeclaration) { onStateMixin(BoilerplateStateMixin( - node, + classish, companion, _annotatedPropsOrStateMixinConfidence(classish, companion, disableAnnotationAssert: true))); @@ -264,12 +264,12 @@ class _BoilerplateMemberDetector { return true; case 'PropsMixin': - onPropsMixin(BoilerplatePropsMixin(node as ClassOrMixinDeclaration, companion, + onPropsMixin(BoilerplatePropsMixin(classish, companion, _annotatedPropsOrStateMixinConfidence(classish, companion))); return true; case 'StateMixin': - onStateMixin(BoilerplateStateMixin(node as ClassOrMixinDeclaration, companion, + onStateMixin(BoilerplateStateMixin(classish, companion, _annotatedPropsOrStateMixinConfidence(classish, companion))); return true; @@ -409,11 +409,11 @@ class _BoilerplateMemberDetector { if (node is MixinDeclaration) { if (propsMixinNamePattern.hasMatch(name) && node.hasSuperclassConstraint('UiProps')) { - onPropsMixin(BoilerplatePropsMixin(node, companion, getConfidence())); + onPropsMixin(BoilerplatePropsMixin(classish, companion, getConfidence())); return true; } if (stateMixinNamePattern.hasMatch(name) && node.hasSuperclassConstraint('UiState')) { - onStateMixin(BoilerplateStateMixin(node, companion, getConfidence())); + onStateMixin(BoilerplateStateMixin(classish, companion, getConfidence())); return true; } } else { diff --git a/lib/src/builder/util.dart b/lib/src/builder/util.dart index a82610b0b..2072e789a 100644 --- a/lib/src/builder/util.dart +++ b/lib/src/builder/util.dart @@ -16,6 +16,7 @@ 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'; import 'package:path/path.dart' as p; import 'package:source_span/source_span.dart'; @@ -87,7 +88,7 @@ String messageWithSpan(String message, {SourceSpan span}) { /// Returns any [FieldDeclaration]s on [node] which have the name `meta`, /// otherwise `null`. -FieldDeclaration metaFieldOrNull(ClassOrMixinDeclaration node) { +FieldDeclaration metaFieldOrNull(ClassishDeclaration node) { return node.members.whereType().firstWhere(fieldDeclarationHasMeta, orElse: () => null); } @@ -107,7 +108,7 @@ bool fieldDeclarationHasName(FieldDeclaration field, String name) { /// Returns any [MethodDeclaration]s on [node] which have the name `meta`, /// otherwise `null`. -MethodDeclaration metaMethodOrNull(ClassOrMixinDeclaration node) { +MethodDeclaration metaMethodOrNull(ClassishDeclaration node) { return node.members.whereType().firstWhere((member) => member.name.name == 'meta', orElse: () => null); diff --git a/lib/src/component/abstract_transition_props.over_react.g.dart b/lib/src/component/abstract_transition_props.over_react.g.dart index 7fbd86dfd..276543c64 100644 --- a/lib/src/component/abstract_transition_props.over_react.g.dart +++ b/lib/src/component/abstract_transition_props.over_react.g.dart @@ -125,8 +125,8 @@ _$$TransitionPropsMixin _$TransitionPropsMapView([Map backingProps]) => abstract class _$$TransitionPropsMixin extends UiProps with TransitionPropsMixin, - $TransitionPropsMixin // If this generated mixin is undefined, it's likely because TransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TransitionPropsMixin, and check that $TransitionPropsMixin is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TransitionPropsMixin, and check that $TransitionPropsMixin is exported/imported properly. + $TransitionPropsMixin { _$$TransitionPropsMixin._(); factory _$$TransitionPropsMixin(Map backingMap) { diff --git a/lib/src/component/error_boundary.over_react.g.dart b/lib/src/component/error_boundary.over_react.g.dart index 49d935dac..3999f2a18 100644 --- a/lib/src/component/error_boundary.over_react.g.dart +++ b/lib/src/component/error_boundary.over_react.g.dart @@ -34,8 +34,8 @@ _$$ErrorBoundaryProps _$ErrorBoundary([Map backingProps]) => abstract class _$$ErrorBoundaryProps extends UiProps with ErrorBoundaryProps, - $ErrorBoundaryProps // If this generated mixin is undefined, it's likely because ErrorBoundaryProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ErrorBoundaryProps, and check that $ErrorBoundaryProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ErrorBoundaryProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ErrorBoundaryProps, and check that $ErrorBoundaryProps is exported/imported properly. + $ErrorBoundaryProps { _$$ErrorBoundaryProps._(); factory _$$ErrorBoundaryProps(Map backingMap) { @@ -111,8 +111,8 @@ class _$$ErrorBoundaryProps$JsMap extends _$$ErrorBoundaryProps { abstract class _$$ErrorBoundaryState extends UiState with ErrorBoundaryState, - $ErrorBoundaryState // If this generated mixin is undefined, it's likely because ErrorBoundaryState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of ErrorBoundaryState, and check that $ErrorBoundaryState is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ErrorBoundaryState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of ErrorBoundaryState, and check that $ErrorBoundaryState is exported/imported properly. + $ErrorBoundaryState { _$$ErrorBoundaryState._(); factory _$$ErrorBoundaryState(Map backingMap) { diff --git a/lib/src/component/error_boundary_recoverable.over_react.g.dart b/lib/src/component/error_boundary_recoverable.over_react.g.dart index e5726e3a9..604b53dbd 100644 --- a/lib/src/component/error_boundary_recoverable.over_react.g.dart +++ b/lib/src/component/error_boundary_recoverable.over_react.g.dart @@ -35,7 +35,8 @@ _$$RecoverableErrorBoundaryProps _$RecoverableErrorBoundary( abstract class _$$RecoverableErrorBoundaryProps extends UiProps with v2.ErrorBoundaryProps, - v2.$ErrorBoundaryProps // If this generated mixin is undefined, it's likely because v2.ErrorBoundaryProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of v2.ErrorBoundaryProps, and check that v2.$ErrorBoundaryProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because v2.ErrorBoundaryProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of v2.ErrorBoundaryProps, and check that v2.$ErrorBoundaryProps is exported/imported properly. + v2.$ErrorBoundaryProps implements RecoverableErrorBoundaryProps { _$$RecoverableErrorBoundaryProps._(); @@ -115,7 +116,8 @@ class _$$RecoverableErrorBoundaryProps$JsMap abstract class _$$RecoverableErrorBoundaryState extends UiState with v2.ErrorBoundaryState, - v2.$ErrorBoundaryState // If this generated mixin is undefined, it's likely because v2.ErrorBoundaryState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of v2.ErrorBoundaryState, and check that v2.$ErrorBoundaryState is exported/imported properly. + // If this generated mixin is undefined, it's likely because v2.ErrorBoundaryState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of v2.ErrorBoundaryState, and check that v2.$ErrorBoundaryState is exported/imported properly. + v2.$ErrorBoundaryState implements RecoverableErrorBoundaryState { _$$RecoverableErrorBoundaryState._(); diff --git a/lib/src/component/resize_sensor.over_react.g.dart b/lib/src/component/resize_sensor.over_react.g.dart index 8b4877f71..0eed4363f 100644 --- a/lib/src/component/resize_sensor.over_react.g.dart +++ b/lib/src/component/resize_sensor.over_react.g.dart @@ -32,8 +32,8 @@ _$$ResizeSensorProps _$ResizeSensor([Map backingProps]) => backingProps == null abstract class _$$ResizeSensorProps extends UiProps with ResizeSensorProps, - $ResizeSensorProps // If this generated mixin is undefined, it's likely because ResizeSensorProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ResizeSensorProps, and check that $ResizeSensorProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ResizeSensorProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ResizeSensorProps, and check that $ResizeSensorProps is exported/imported properly. + $ResizeSensorProps { _$$ResizeSensorProps._(); factory _$$ResizeSensorProps(Map backingMap) { diff --git a/lib/src/component/suspense_component.over_react.g.dart b/lib/src/component/suspense_component.over_react.g.dart index c05caa2d0..14c3a71b3 100644 --- a/lib/src/component/suspense_component.over_react.g.dart +++ b/lib/src/component/suspense_component.over_react.g.dart @@ -57,7 +57,8 @@ final UiFactoryConfig<_$$SuspenseProps> $SuspenseConfig = _$SuspenseConfig; abstract class _$$SuspenseProps extends UiProps with SuspensePropsMixin, - $SuspensePropsMixin // If this generated mixin is undefined, it's likely because SuspensePropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SuspensePropsMixin, and check that $SuspensePropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because SuspensePropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SuspensePropsMixin, and check that $SuspensePropsMixin is exported/imported properly. + $SuspensePropsMixin implements SuspenseProps { _$$SuspenseProps._(); diff --git a/lib/src/component/with_transition.over_react.g.dart b/lib/src/component/with_transition.over_react.g.dart index 8d1e58a46..f87efd3e8 100644 --- a/lib/src/component/with_transition.over_react.g.dart +++ b/lib/src/component/with_transition.over_react.g.dart @@ -33,9 +33,11 @@ _$$WithTransitionProps _$WithTransition([Map backingProps]) => abstract class _$$WithTransitionProps extends UiProps with v2.TransitionPropsMixin, - v2.$TransitionPropsMixin, // If this generated mixin is undefined, it's likely because v2.TransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of v2.TransitionPropsMixin, and check that v2.$TransitionPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because v2.TransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of v2.TransitionPropsMixin, and check that v2.$TransitionPropsMixin is exported/imported properly. + v2.$TransitionPropsMixin, WithTransitionPropsMixin, - $WithTransitionPropsMixin // If this generated mixin is undefined, it's likely because WithTransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of WithTransitionPropsMixin, and check that $WithTransitionPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because WithTransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of WithTransitionPropsMixin, and check that $WithTransitionPropsMixin is exported/imported properly. + $WithTransitionPropsMixin implements WithTransitionProps { _$$WithTransitionProps._(); @@ -115,8 +117,8 @@ class _$$WithTransitionProps$JsMap extends _$$WithTransitionProps { abstract class _$$WithTransitionState extends UiState with WithTransitionState, - $WithTransitionState // If this generated mixin is undefined, it's likely because WithTransitionState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of WithTransitionState, and check that $WithTransitionState is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because WithTransitionState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of WithTransitionState, and check that $WithTransitionState is exported/imported properly. + $WithTransitionState { _$$WithTransitionState._(); factory _$$WithTransitionState(Map backingMap) { diff --git a/lib/src/util/safe_render_manager/safe_render_manager_helper.over_react.g.dart b/lib/src/util/safe_render_manager/safe_render_manager_helper.over_react.g.dart index 7c750a143..68797aa13 100644 --- a/lib/src/util/safe_render_manager/safe_render_manager_helper.over_react.g.dart +++ b/lib/src/util/safe_render_manager/safe_render_manager_helper.over_react.g.dart @@ -33,8 +33,8 @@ _$$SafeRenderManagerHelperProps _$SafeRenderManagerHelper([Map backingProps]) => abstract class _$$SafeRenderManagerHelperProps extends UiProps with SafeRenderManagerHelperProps, - $SafeRenderManagerHelperProps // If this generated mixin is undefined, it's likely because SafeRenderManagerHelperProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SafeRenderManagerHelperProps, and check that $SafeRenderManagerHelperProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because SafeRenderManagerHelperProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SafeRenderManagerHelperProps, and check that $SafeRenderManagerHelperProps is exported/imported properly. + $SafeRenderManagerHelperProps { _$$SafeRenderManagerHelperProps._(); factory _$$SafeRenderManagerHelperProps(Map backingMap) { @@ -112,8 +112,8 @@ class _$$SafeRenderManagerHelperProps$JsMap abstract class _$$SafeRenderManagerHelperState extends UiState with SafeRenderManagerHelperState, - $SafeRenderManagerHelperState // If this generated mixin is undefined, it's likely because SafeRenderManagerHelperState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of SafeRenderManagerHelperState, and check that $SafeRenderManagerHelperState is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because SafeRenderManagerHelperState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of SafeRenderManagerHelperState, and check that $SafeRenderManagerHelperState is exported/imported properly. + $SafeRenderManagerHelperState { _$$SafeRenderManagerHelperState._(); factory _$$SafeRenderManagerHelperState(Map backingMap) { diff --git a/pubspec.yaml b/pubspec.yaml index b73bbf060..1df00e0cb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,11 +7,11 @@ environment: dependencies: collection: ^1.14.11 - analyzer: '>=1.7.2 <3.0.0' + analyzer: ^5.1.0 build: '>=1.0.0 <3.0.0' - built_redux: ^8.0.0 + built_redux: ^8.0.6 built_value: ^8.0.0 - dart_style: '>=1.2.5 <3.0.0' + dart_style: ^2.0.0 js: ^0.6.1+1 logging: ^1.0.0 meta: ^1.6.0 @@ -20,7 +20,7 @@ dependencies: redux: '>=3.0.0 <6.0.0' source_span: ^1.4.1 transformer_utils: ^0.2.6 - w_common: '>=2.0.0 <4.0.0' + w_common: ^3.0.0 w_flux: ^2.10.21 platform_detect: '>=1.3.4 <3.0.0' quiver: ">=0.25.0 <4.0.0" @@ -28,17 +28,15 @@ dependencies: dev_dependencies: build_resolvers: '>=1.0.5 <3.0.0' - build_runner: '>=1.7.1 <3.0.0' - build_test: ">=0.10.9 <3.0.0" - build_web_compilers: '>=2.12.0 <4.0.0' + build_runner: ^2.0.0 + build_test: ^2.0.0 + build_web_compilers: ^3.0.0 built_value_generator: ^8.0.0 - dart_dev: '>=3.6.4 <5.0.0' - dependency_validator: '>=2.0.0 <4.0.0' + dart_dev: ^4.0.0 + dependency_validator: ^3.0.0 glob: ^2.0.0 io: '>=0.3.2+1 <2.0.0' - # Pin mockito to work around https://github.com/dart-lang/mockito/issues/552 - # until we can get to 5.3.1, which requires a newer analyzer. - mockito: '>=5.0.0 <5.3.0' + mockito: ^5.3.1 react_testing_library: ^2.1.0 over_react_test: ^2.10.2 pedantic: ^1.8.0 diff --git a/test/mockito.mocks.dart b/test/mockito.mocks.dart index 106539dc6..7a1f0ab6f 100644 --- a/test/mockito.mocks.dart +++ b/test/mockito.mocks.dart @@ -1,4 +1,4 @@ -// Mocks generated by Mockito 5.2.0 from annotations +// Mocks generated by Mockito 5.4.0 from annotations // in over_react/test/mockito.dart. // Do not manually edit this file. @@ -17,6 +17,7 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class /// A class which mocks [Logger]. /// diff --git a/test/over_react/component/abstract_transition_test.over_react.g.dart b/test/over_react/component/abstract_transition_test.over_react.g.dart index 9bb14dbd8..108b44f2b 100644 --- a/test/over_react/component/abstract_transition_test.over_react.g.dart +++ b/test/over_react/component/abstract_transition_test.over_react.g.dart @@ -32,9 +32,11 @@ _$$TransitionerProps _$Transitioner([Map backingProps]) => backingProps == null abstract class _$$TransitionerProps extends UiProps with TransitionerPropsMixin, - $TransitionerPropsMixin, // If this generated mixin is undefined, it's likely because TransitionerPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TransitionerPropsMixin, and check that $TransitionerPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because TransitionerPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TransitionerPropsMixin, and check that $TransitionerPropsMixin is exported/imported properly. + $TransitionerPropsMixin, TransitionPropsMixin, - $TransitionPropsMixin // If this generated mixin is undefined, it's likely because TransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TransitionPropsMixin, and check that $TransitionPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because TransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TransitionPropsMixin, and check that $TransitionPropsMixin is exported/imported properly. + $TransitionPropsMixin implements TransitionerProps { _$$TransitionerProps._(); @@ -114,7 +116,8 @@ class _$$TransitionerProps$JsMap extends _$$TransitionerProps { abstract class _$$TransitionerState extends UiState with AbstractTransitionState, - $AbstractTransitionState // If this generated mixin is undefined, it's likely because AbstractTransitionState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of AbstractTransitionState, and check that $AbstractTransitionState is exported/imported properly. + // If this generated mixin is undefined, it's likely because AbstractTransitionState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of AbstractTransitionState, and check that $AbstractTransitionState is exported/imported properly. + $AbstractTransitionState implements TransitionerState { _$$TransitionerState._(); diff --git a/test/over_react/component/element_type_test.over_react.g.dart b/test/over_react/component/element_type_test.over_react.g.dart index 8e28703a5..3f99016f0 100644 --- a/test/over_react/component/element_type_test.over_react.g.dart +++ b/test/over_react/component/element_type_test.over_react.g.dart @@ -32,8 +32,8 @@ _$$CustomTestProps _$CustomTest([Map backingProps]) => backingProps == null abstract class _$$CustomTestProps extends UiProps with CustomTestProps, - $CustomTestProps // If this generated mixin is undefined, it's likely because CustomTestProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomTestProps, and check that $CustomTestProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because CustomTestProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomTestProps, and check that $CustomTestProps is exported/imported properly. + $CustomTestProps { _$$CustomTestProps._(); factory _$$CustomTestProps(Map backingMap) { @@ -214,8 +214,8 @@ final UiFactoryConfig<_$$CustomFnTestProps> $CustomFnTestConfig = abstract class _$$CustomFnTestProps extends UiProps with CustomFnTestProps, - $CustomFnTestProps // If this generated mixin is undefined, it's likely because CustomFnTestProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomFnTestProps, and check that $CustomFnTestProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because CustomFnTestProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomFnTestProps, and check that $CustomFnTestProps is exported/imported properly. + $CustomFnTestProps { _$$CustomFnTestProps._(); factory _$$CustomFnTestProps(Map backingMap) { diff --git a/test/over_react/component/error_boundary/shared_stack_tests.over_react.g.dart b/test/over_react/component/error_boundary/shared_stack_tests.over_react.g.dart index 0230a62f4..099f68604 100644 --- a/test/over_react/component/error_boundary/shared_stack_tests.over_react.g.dart +++ b/test/over_react/component/error_boundary/shared_stack_tests.over_react.g.dart @@ -33,8 +33,8 @@ _$$ThrowingComponent2Props _$ThrowingComponent2([Map backingProps]) => abstract class _$$ThrowingComponent2Props extends UiProps with ThrowingComponent2Props, - $ThrowingComponent2Props // If this generated mixin is undefined, it's likely because ThrowingComponent2Props is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ThrowingComponent2Props, and check that $ThrowingComponent2Props is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ThrowingComponent2Props is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ThrowingComponent2Props, and check that $ThrowingComponent2Props is exported/imported properly. + $ThrowingComponent2Props { _$$ThrowingComponent2Props._(); factory _$$ThrowingComponent2Props(Map backingMap) { @@ -326,8 +326,8 @@ final UiFactoryConfig<_$$ThrowingFunctionComponentProps> abstract class _$$ThrowingFunctionComponentProps extends UiProps with ThrowingFunctionComponentProps, - $ThrowingFunctionComponentProps // If this generated mixin is undefined, it's likely because ThrowingFunctionComponentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ThrowingFunctionComponentProps, and check that $ThrowingFunctionComponentProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ThrowingFunctionComponentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ThrowingFunctionComponentProps, and check that $ThrowingFunctionComponentProps is exported/imported properly. + $ThrowingFunctionComponentProps { _$$ThrowingFunctionComponentProps._(); factory _$$ThrowingFunctionComponentProps(Map backingMap) { @@ -415,8 +415,8 @@ final UiFactoryConfig<_$$ThrowingForwardRefComponentProps> abstract class _$$ThrowingForwardRefComponentProps extends UiProps with ThrowingForwardRefComponentProps, - $ThrowingForwardRefComponentProps // If this generated mixin is undefined, it's likely because ThrowingForwardRefComponentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ThrowingForwardRefComponentProps, and check that $ThrowingForwardRefComponentProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ThrowingForwardRefComponentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ThrowingForwardRefComponentProps, and check that $ThrowingForwardRefComponentProps is exported/imported properly. + $ThrowingForwardRefComponentProps { _$$ThrowingForwardRefComponentProps._(); factory _$$ThrowingForwardRefComponentProps(Map backingMap) { diff --git a/test/over_react/component/fixtures/lazy_load_me_component.over_react.g.dart b/test/over_react/component/fixtures/lazy_load_me_component.over_react.g.dart index d9c8d7e6a..35ea035d4 100644 --- a/test/over_react/component/fixtures/lazy_load_me_component.over_react.g.dart +++ b/test/over_react/component/fixtures/lazy_load_me_component.over_react.g.dart @@ -28,7 +28,8 @@ final UiFactoryConfig<_$$LazyLoadMeProps> $LazyLoadMeConfig = abstract class _$$LazyLoadMeProps extends UiProps with LazyLoadMePropsMixin, - $LazyLoadMePropsMixin // If this generated mixin is undefined, it's likely because LazyLoadMePropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of LazyLoadMePropsMixin, and check that $LazyLoadMePropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because LazyLoadMePropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of LazyLoadMePropsMixin, and check that $LazyLoadMePropsMixin is exported/imported properly. + $LazyLoadMePropsMixin implements LazyLoadMeProps { _$$LazyLoadMeProps._(); diff --git a/test/over_react/component/fixtures/lazy_load_me_props.over_react.g.dart b/test/over_react/component/fixtures/lazy_load_me_props.over_react.g.dart index 67b4a467e..cf9a2f178 100644 --- a/test/over_react/component/fixtures/lazy_load_me_props.over_react.g.dart +++ b/test/over_react/component/fixtures/lazy_load_me_props.over_react.g.dart @@ -53,8 +53,8 @@ _$$LazyLoadMePropsMixin _$LazyLoadMePropsMapView([Map backingProps]) => abstract class _$$LazyLoadMePropsMixin extends UiProps with LazyLoadMePropsMixin, - $LazyLoadMePropsMixin // If this generated mixin is undefined, it's likely because LazyLoadMePropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of LazyLoadMePropsMixin, and check that $LazyLoadMePropsMixin is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because LazyLoadMePropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of LazyLoadMePropsMixin, and check that $LazyLoadMePropsMixin is exported/imported properly. + $LazyLoadMePropsMixin { _$$LazyLoadMePropsMixin._(); factory _$$LazyLoadMePropsMixin(Map backingMap) { diff --git a/test/over_react/component/fixtures/pure_test_components.over_react.g.dart b/test/over_react/component/fixtures/pure_test_components.over_react.g.dart index e3bf68c3a..c2969c949 100644 --- a/test/over_react/component/fixtures/pure_test_components.over_react.g.dart +++ b/test/over_react/component/fixtures/pure_test_components.over_react.g.dart @@ -33,7 +33,8 @@ _$$PureTestWrapperProps _$PureTestWrapper([Map backingProps]) => abstract class _$$PureTestWrapperProps extends UiProps with SharedPureTestPropsMixin, - $SharedPureTestPropsMixin // If this generated mixin is undefined, it's likely because SharedPureTestPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SharedPureTestPropsMixin, and check that $SharedPureTestPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because SharedPureTestPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SharedPureTestPropsMixin, and check that $SharedPureTestPropsMixin is exported/imported properly. + $SharedPureTestPropsMixin implements PureTestWrapperProps { _$$PureTestWrapperProps._(); @@ -181,9 +182,11 @@ _$$PureTestProps _$PureTest([Map backingProps]) => backingProps == null abstract class _$$PureTestProps extends UiProps with SharedPureTestPropsMixin, - $SharedPureTestPropsMixin, // If this generated mixin is undefined, it's likely because SharedPureTestPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SharedPureTestPropsMixin, and check that $SharedPureTestPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because SharedPureTestPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SharedPureTestPropsMixin, and check that $SharedPureTestPropsMixin is exported/imported properly. + $SharedPureTestPropsMixin, PureTestPropsMixin, - $PureTestPropsMixin // If this generated mixin is undefined, it's likely because PureTestPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of PureTestPropsMixin, and check that $PureTestPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because PureTestPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of PureTestPropsMixin, and check that $PureTestPropsMixin is exported/imported properly. + $PureTestPropsMixin implements PureTestProps { _$$PureTestProps._(); @@ -263,8 +266,8 @@ class _$$PureTestProps$JsMap extends _$$PureTestProps { abstract class _$$PureTestState extends UiState with PureTestState, - $PureTestState // If this generated mixin is undefined, it's likely because PureTestState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of PureTestState, and check that $PureTestState is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because PureTestState is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of PureTestState, and check that $PureTestState is exported/imported properly. + $PureTestState { _$$PureTestState._(); factory _$$PureTestState(Map backingMap) { diff --git a/test/over_react/component/memo_test.over_react.g.dart b/test/over_react/component/memo_test.over_react.g.dart index 971b6bfe2..302008ea9 100644 --- a/test/over_react/component/memo_test.over_react.g.dart +++ b/test/over_react/component/memo_test.over_react.g.dart @@ -239,8 +239,8 @@ final UiFactoryConfig<_$$FunctionCustomPropsProps> $FunctionCustomPropsConfig = abstract class _$$FunctionCustomPropsProps extends UiProps with FunctionCustomPropsProps, - $FunctionCustomPropsProps // If this generated mixin is undefined, it's likely because FunctionCustomPropsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of FunctionCustomPropsProps, and check that $FunctionCustomPropsProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because FunctionCustomPropsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of FunctionCustomPropsProps, and check that $FunctionCustomPropsProps is exported/imported properly. + $FunctionCustomPropsProps { _$$FunctionCustomPropsProps._(); factory _$$FunctionCustomPropsProps(Map backingMap) { diff --git a/test/over_react/component/ref_util_test.over_react.g.dart b/test/over_react/component/ref_util_test.over_react.g.dart index 6d19238ff..9acdf545c 100644 --- a/test/over_react/component/ref_util_test.over_react.g.dart +++ b/test/over_react/component/ref_util_test.over_react.g.dart @@ -32,8 +32,8 @@ _$$BasicProps _$Basic([Map backingProps]) => backingProps == null abstract class _$$BasicProps extends UiProps with BasicProps, - $BasicProps // If this generated mixin is undefined, it's likely because BasicProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicProps, and check that $BasicProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because BasicProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicProps, and check that $BasicProps is exported/imported properly. + $BasicProps { _$$BasicProps._(); factory _$$BasicProps(Map backingMap) { @@ -220,8 +220,8 @@ final UiFactoryConfig<_$$BasicUiFunctionProps> $BasicUiFunctionConfig = abstract class _$$BasicUiFunctionProps extends UiProps with BasicUiFunctionProps, - $BasicUiFunctionProps // If this generated mixin is undefined, it's likely because BasicUiFunctionProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicUiFunctionProps, and check that $BasicUiFunctionProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because BasicUiFunctionProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicUiFunctionProps, and check that $BasicUiFunctionProps is exported/imported properly. + $BasicUiFunctionProps { _$$BasicUiFunctionProps._(); factory _$$BasicUiFunctionProps(Map backingMap) { @@ -307,7 +307,8 @@ final UiFactoryConfig<_$$SecondaryBasicUiFunctionProps> abstract class _$$SecondaryBasicUiFunctionProps extends UiProps with BasicUiFunctionProps, - $BasicUiFunctionProps // If this generated mixin is undefined, it's likely because BasicUiFunctionProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicUiFunctionProps, and check that $BasicUiFunctionProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because BasicUiFunctionProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicUiFunctionProps, and check that $BasicUiFunctionProps is exported/imported properly. + $BasicUiFunctionProps implements SecondaryBasicUiFunctionProps { _$$SecondaryBasicUiFunctionProps._(); diff --git a/test/over_react/component/with_transition_test.over_react.g.dart b/test/over_react/component/with_transition_test.over_react.g.dart index e7e3b5fdf..35a108b01 100644 --- a/test/over_react/component/with_transition_test.over_react.g.dart +++ b/test/over_react/component/with_transition_test.over_react.g.dart @@ -33,9 +33,11 @@ _$$WithTransitionTesterProps _$WithTransitionTester([Map backingProps]) => abstract class _$$WithTransitionTesterProps extends UiProps with WithTransitionPropsMixin, - $WithTransitionPropsMixin, // If this generated mixin is undefined, it's likely because WithTransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of WithTransitionPropsMixin, and check that $WithTransitionPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because WithTransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of WithTransitionPropsMixin, and check that $WithTransitionPropsMixin is exported/imported properly. + $WithTransitionPropsMixin, TransitionPropsMixin, - $TransitionPropsMixin // If this generated mixin is undefined, it's likely because TransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TransitionPropsMixin, and check that $TransitionPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because TransitionPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TransitionPropsMixin, and check that $TransitionPropsMixin is exported/imported properly. + $TransitionPropsMixin implements WithTransitionTesterProps { _$$WithTransitionTesterProps._(); diff --git a/test/over_react/component_declaration/function_type_checking_test/components.over_react.g.dart b/test/over_react/component_declaration/function_type_checking_test/components.over_react.g.dart index b3384078c..7d61bbbed 100644 --- a/test/over_react/component_declaration/function_type_checking_test/components.over_react.g.dart +++ b/test/over_react/component_declaration/function_type_checking_test/components.over_react.g.dart @@ -39,8 +39,8 @@ abstract class _$$DoNotReferenceThisFactoryExceptForInASingleTestProps extends UiProps with DoNotReferenceThisFactoryExceptForInASingleTestProps, - $DoNotReferenceThisFactoryExceptForInASingleTestProps // If this generated mixin is undefined, it's likely because DoNotReferenceThisFactoryExceptForInASingleTestProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DoNotReferenceThisFactoryExceptForInASingleTestProps, and check that $DoNotReferenceThisFactoryExceptForInASingleTestProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because DoNotReferenceThisFactoryExceptForInASingleTestProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DoNotReferenceThisFactoryExceptForInASingleTestProps, and check that $DoNotReferenceThisFactoryExceptForInASingleTestProps is exported/imported properly. + $DoNotReferenceThisFactoryExceptForInASingleTestProps { _$$DoNotReferenceThisFactoryExceptForInASingleTestProps._(); factory _$$DoNotReferenceThisFactoryExceptForInASingleTestProps( @@ -390,8 +390,8 @@ final UiFactoryConfig<_$$TestAProps> $TestAConfig = _$TestAConfig; abstract class _$$TestAProps extends UiProps with TestAProps, - $TestAProps // If this generated mixin is undefined, it's likely because TestAProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestAProps, and check that $TestAProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestAProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestAProps, and check that $TestAProps is exported/imported properly. + $TestAProps { _$$TestAProps._(); factory _$$TestAProps(Map backingMap) { @@ -474,8 +474,8 @@ final UiFactoryConfig<_$$TestBProps> $TestBConfig = _$TestBConfig; abstract class _$$TestBProps extends UiProps with TestBProps, - $TestBProps // If this generated mixin is undefined, it's likely because TestBProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestBProps, and check that $TestBProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestBProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestBProps, and check that $TestBProps is exported/imported properly. + $TestBProps { _$$TestBProps._(); factory _$$TestBProps(Map backingMap) { @@ -559,8 +559,8 @@ final UiFactoryConfig<_$$TestParentProps> $TestParentConfig = abstract class _$$TestParentProps extends UiProps with TestParentProps, - $TestParentProps // If this generated mixin is undefined, it's likely because TestParentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestParentProps, and check that $TestParentProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestParentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestParentProps, and check that $TestParentProps is exported/imported properly. + $TestParentProps { _$$TestParentProps._(); factory _$$TestParentProps(Map backingMap) { @@ -645,8 +645,8 @@ final UiFactoryConfig<_$$TestSubtypeProps> $TestSubtypeConfig = abstract class _$$TestSubtypeProps extends UiProps with TestSubtypeProps, - $TestSubtypeProps // If this generated mixin is undefined, it's likely because TestSubtypeProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestSubtypeProps, and check that $TestSubtypeProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestSubtypeProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestSubtypeProps, and check that $TestSubtypeProps is exported/imported properly. + $TestSubtypeProps { _$$TestSubtypeProps._(); factory _$$TestSubtypeProps(Map backingMap) { @@ -731,8 +731,8 @@ final UiFactoryConfig<_$$TestSubsubtypeProps> $TestSubsubtypeConfig = abstract class _$$TestSubsubtypeProps extends UiProps with TestSubsubtypeProps, - $TestSubsubtypeProps // If this generated mixin is undefined, it's likely because TestSubsubtypeProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestSubsubtypeProps, and check that $TestSubsubtypeProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestSubsubtypeProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestSubsubtypeProps, and check that $TestSubsubtypeProps is exported/imported properly. + $TestSubsubtypeProps { _$$TestSubsubtypeProps._(); factory _$$TestSubsubtypeProps(Map backingMap) { @@ -817,8 +817,8 @@ final UiFactoryConfig<_$$TestExtendtypeProps> $TestExtendtypeConfig = abstract class _$$TestExtendtypeProps extends UiProps with TestExtendtypeProps, - $TestExtendtypeProps // If this generated mixin is undefined, it's likely because TestExtendtypeProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestExtendtypeProps, and check that $TestExtendtypeProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestExtendtypeProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestExtendtypeProps, and check that $TestExtendtypeProps is exported/imported properly. + $TestExtendtypeProps { _$$TestExtendtypeProps._(); factory _$$TestExtendtypeProps(Map backingMap) { @@ -903,8 +903,8 @@ final UiFactoryConfig<_$$OneLevelWrapperProps> $OneLevelWrapperConfig = abstract class _$$OneLevelWrapperProps extends UiProps with OneLevelWrapperProps, - $OneLevelWrapperProps // If this generated mixin is undefined, it's likely because OneLevelWrapperProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of OneLevelWrapperProps, and check that $OneLevelWrapperProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because OneLevelWrapperProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of OneLevelWrapperProps, and check that $OneLevelWrapperProps is exported/imported properly. + $OneLevelWrapperProps { _$$OneLevelWrapperProps._(); factory _$$OneLevelWrapperProps(Map backingMap) { @@ -989,8 +989,8 @@ final UiFactoryConfig<_$$TwoLevelWrapperProps> $TwoLevelWrapperConfig = abstract class _$$TwoLevelWrapperProps extends UiProps with TwoLevelWrapperProps, - $TwoLevelWrapperProps // If this generated mixin is undefined, it's likely because TwoLevelWrapperProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TwoLevelWrapperProps, and check that $TwoLevelWrapperProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TwoLevelWrapperProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TwoLevelWrapperProps, and check that $TwoLevelWrapperProps is exported/imported properly. + $TwoLevelWrapperProps { _$$TwoLevelWrapperProps._(); factory _$$TwoLevelWrapperProps(Map backingMap) { @@ -1076,8 +1076,8 @@ final UiFactoryConfig<_$$TestUninitializedParentProps> abstract class _$$TestUninitializedParentProps extends UiProps with TestUninitializedParentProps, - $TestUninitializedParentProps // If this generated mixin is undefined, it's likely because TestUninitializedParentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestUninitializedParentProps, and check that $TestUninitializedParentProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestUninitializedParentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestUninitializedParentProps, and check that $TestUninitializedParentProps is exported/imported properly. + $TestUninitializedParentProps { _$$TestUninitializedParentProps._(); factory _$$TestUninitializedParentProps(Map backingMap) { diff --git a/test/over_react/component_declaration/redux_component_test/test_reducer.g.dart b/test/over_react/component_declaration/redux_component_test/test_reducer.g.dart index 298355e77..271446242 100644 --- a/test/over_react/component_declaration/redux_component_test/test_reducer.g.dart +++ b/test/over_react/component_declaration/redux_component_test/test_reducer.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of over_react.component_declaration.redux_component.reducer; +part of 'test_reducer.dart'; // ************************************************************************** // BuiltReduxGenerator @@ -64,7 +64,11 @@ class _$BaseState extends BaseState { @override int get hashCode { - return $jf($jc($jc(0, count1.hashCode), count2.hashCode)); + var _$hash = 0; + _$hash = $jc(_$hash, count1.hashCode); + _$hash = $jc(_$hash, count2.hashCode); + _$hash = $jf(_$hash); + return _$hash; } @override @@ -125,4 +129,4 @@ class BaseStateBuilder implements Builder { } } -// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,deprecated_member_use_from_same_package,lines_longer_than_80_chars,no_leading_underscores_for_local_identifiers,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new,unnecessary_lambdas +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/test/over_react/util/cast_ui_factory_test.over_react.g.dart b/test/over_react/util/cast_ui_factory_test.over_react.g.dart index cd852c602..801115768 100644 --- a/test/over_react/util/cast_ui_factory_test.over_react.g.dart +++ b/test/over_react/util/cast_ui_factory_test.over_react.g.dart @@ -32,8 +32,8 @@ _$$BasicProps _$Basic([Map backingProps]) => backingProps == null abstract class _$$BasicProps extends UiProps with BasicProps, - $BasicProps // If this generated mixin is undefined, it's likely because BasicProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicProps, and check that $BasicProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because BasicProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicProps, and check that $BasicProps is exported/imported properly. + $BasicProps { _$$BasicProps._(); factory _$$BasicProps(Map backingMap) { diff --git a/test/over_react/util/component_debug_name_test.over_react.g.dart b/test/over_react/util/component_debug_name_test.over_react.g.dart index 1443ed3da..074437e2c 100644 --- a/test/over_react/util/component_debug_name_test.over_react.g.dart +++ b/test/over_react/util/component_debug_name_test.over_react.g.dart @@ -33,8 +33,8 @@ _$$TestComponent2Props _$TestComponent2([Map backingProps]) => abstract class _$$TestComponent2Props extends UiProps with TestComponent2Props, - $TestComponent2Props // If this generated mixin is undefined, it's likely because TestComponent2Props is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestComponent2Props, and check that $TestComponent2Props is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestComponent2Props is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestComponent2Props, and check that $TestComponent2Props is exported/imported properly. + $TestComponent2Props { _$$TestComponent2Props._(); factory _$$TestComponent2Props(Map backingMap) { diff --git a/test/over_react/util/js_component_test.over_react.g.dart b/test/over_react/util/js_component_test.over_react.g.dart index 32cfe167f..eb49c1a00 100644 --- a/test/over_react/util/js_component_test.over_react.g.dart +++ b/test/over_react/util/js_component_test.over_react.g.dart @@ -165,9 +165,11 @@ final UiFactoryConfig<_$$TestProps> $_TestConfig = _$_TestConfig; abstract class _$$TestProps extends UiProps with TestPropsMixin, - $TestPropsMixin, // If this generated mixin is undefined, it's likely because TestPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestPropsMixin, and check that $TestPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because TestPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestPropsMixin, and check that $TestPropsMixin is exported/imported properly. + $TestPropsMixin, ASecondPropsMixin, - $ASecondPropsMixin // If this generated mixin is undefined, it's likely because ASecondPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ASecondPropsMixin, and check that $ASecondPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because ASecondPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ASecondPropsMixin, and check that $ASecondPropsMixin is exported/imported properly. + $ASecondPropsMixin implements TestProps { _$$TestProps._(); diff --git a/test/over_react/util/prop_conversion_test.over_react.g.dart b/test/over_react/util/prop_conversion_test.over_react.g.dart index f4c24bf01..4e9223020 100644 --- a/test/over_react/util/prop_conversion_test.over_react.g.dart +++ b/test/over_react/util/prop_conversion_test.over_react.g.dart @@ -33,8 +33,8 @@ _$$ClassComponentProps _$ClassComponent([Map backingProps]) => abstract class _$$ClassComponentProps extends UiProps with ClassComponentProps, - $ClassComponentProps // If this generated mixin is undefined, it's likely because ClassComponentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ClassComponentProps, and check that $ClassComponentProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ClassComponentProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ClassComponentProps, and check that $ClassComponentProps is exported/imported properly. + $ClassComponentProps { _$$ClassComponentProps._(); factory _$$ClassComponentProps(Map backingMap) { @@ -426,8 +426,8 @@ final UiFactoryConfig<_$$ExpectsDartMapPropProps> $ExpectsDartMapPropConfig = abstract class _$$ExpectsDartMapPropProps extends UiProps with ExpectsDartMapPropProps, - $ExpectsDartMapPropProps // If this generated mixin is undefined, it's likely because ExpectsDartMapPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartMapPropProps, and check that $ExpectsDartMapPropProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ExpectsDartMapPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartMapPropProps, and check that $ExpectsDartMapPropProps is exported/imported properly. + $ExpectsDartMapPropProps { _$$ExpectsDartMapPropProps._(); factory _$$ExpectsDartMapPropProps(Map backingMap) { @@ -512,8 +512,8 @@ final UiFactoryConfig<_$$ExpectsDartStylePropProps> abstract class _$$ExpectsDartStylePropProps extends UiProps with ExpectsDartStylePropProps, - $ExpectsDartStylePropProps // If this generated mixin is undefined, it's likely because ExpectsDartStylePropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartStylePropProps, and check that $ExpectsDartStylePropProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ExpectsDartStylePropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsDartStylePropProps, and check that $ExpectsDartStylePropProps is exported/imported properly. + $ExpectsDartStylePropProps { _$$ExpectsDartStylePropProps._(); factory _$$ExpectsDartStylePropProps(Map backingMap) { @@ -600,8 +600,8 @@ final UiFactoryConfig<_$$ExpectsListChildrenPropProps> abstract class _$$ExpectsListChildrenPropProps extends UiProps with ExpectsListChildrenPropProps, - $ExpectsListChildrenPropProps // If this generated mixin is undefined, it's likely because ExpectsListChildrenPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsListChildrenPropProps, and check that $ExpectsListChildrenPropProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because ExpectsListChildrenPropProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExpectsListChildrenPropProps, and check that $ExpectsListChildrenPropProps is exported/imported properly. + $ExpectsListChildrenPropProps { _$$ExpectsListChildrenPropProps._(); factory _$$ExpectsListChildrenPropProps(Map backingMap) { @@ -688,8 +688,8 @@ final UiFactoryConfig<_$$BasicForwardRefProps> $BasicForwardRefConfig = abstract class _$$BasicForwardRefProps extends UiProps with BasicForwardRefProps, - $BasicForwardRefProps // If this generated mixin is undefined, it's likely because BasicForwardRefProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicForwardRefProps, and check that $BasicForwardRefProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because BasicForwardRefProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicForwardRefProps, and check that $BasicForwardRefProps is exported/imported properly. + $BasicForwardRefProps { _$$BasicForwardRefProps._(); factory _$$BasicForwardRefProps(Map backingMap) { @@ -774,9 +774,11 @@ final UiFactoryConfig<_$$DartTestJsWrapperProps> $DartTestJsWrapperConfig = abstract class _$$DartTestJsWrapperProps extends UiProps with TestJsProps, - $TestJsProps, // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + $TestJsProps, DartTestJsWrapperPropsMixin, - $DartTestJsWrapperPropsMixin // If this generated mixin is undefined, it's likely because DartTestJsWrapperPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DartTestJsWrapperPropsMixin, and check that $DartTestJsWrapperPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because DartTestJsWrapperPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DartTestJsWrapperPropsMixin, and check that $DartTestJsWrapperPropsMixin is exported/imported properly. + $DartTestJsWrapperPropsMixin implements DartTestJsWrapperProps { _$$DartTestJsWrapperProps._(); @@ -863,8 +865,8 @@ final UiFactoryConfig<_$$TestJsProps> $TestJsConfig = _$TestJsConfig; abstract class _$$TestJsProps extends UiProps with TestJsProps, - $TestJsProps // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestJsProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestJsProps, and check that $TestJsProps is exported/imported properly. + $TestJsProps { _$$TestJsProps._(); factory _$$TestJsProps(Map backingMap) { diff --git a/test/over_react_redux/fixtures/counter_fn.over_react.g.dart b/test/over_react_redux/fixtures/counter_fn.over_react.g.dart index 75172dcba..7bbd8f739 100644 --- a/test/over_react_redux/fixtures/counter_fn.over_react.g.dart +++ b/test/over_react_redux/fixtures/counter_fn.over_react.g.dart @@ -139,8 +139,8 @@ final UiFactoryConfig<_$$CounterFnProps> $CounterFnConfig = _$CounterFnConfig; abstract class _$$CounterFnProps extends UiProps with CounterFnProps, - $CounterFnProps // If this generated mixin is undefined, it's likely because CounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CounterFnProps, and check that $CounterFnProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because CounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CounterFnProps, and check that $CounterFnProps is exported/imported properly. + $CounterFnProps { _$$CounterFnProps._(); factory _$$CounterFnProps(Map backingMap) { @@ -225,9 +225,11 @@ final UiFactoryConfig<_$$ModelCounterFnProps> $ModelCounterFnConfig = abstract class _$$ModelCounterFnProps extends UiProps with CounterFnProps, - $CounterFnProps, // If this generated mixin is undefined, it's likely because CounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CounterFnProps, and check that $CounterFnProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because CounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CounterFnProps, and check that $CounterFnProps is exported/imported properly. + $CounterFnProps, ModelCounterFnPropsMixin, - $ModelCounterFnPropsMixin // If this generated mixin is undefined, it's likely because ModelCounterFnPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ModelCounterFnPropsMixin, and check that $ModelCounterFnPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because ModelCounterFnPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ModelCounterFnPropsMixin, and check that $ModelCounterFnPropsMixin is exported/imported properly. + $ModelCounterFnPropsMixin implements ModelCounterFnProps { _$$ModelCounterFnProps._(); @@ -317,9 +319,11 @@ final UiFactoryConfig<_$$CustomContextCounterFnProps> abstract class _$$CustomContextCounterFnProps extends UiProps with CounterFnProps, - $CounterFnProps, // If this generated mixin is undefined, it's likely because CounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CounterFnProps, and check that $CounterFnProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because CounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CounterFnProps, and check that $CounterFnProps is exported/imported properly. + $CounterFnProps, CustomContextCounterFnPropsMixin, - $CustomContextCounterFnPropsMixin // If this generated mixin is undefined, it's likely because CustomContextCounterFnPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomContextCounterFnPropsMixin, and check that $CustomContextCounterFnPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because CustomContextCounterFnPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomContextCounterFnPropsMixin, and check that $CustomContextCounterFnPropsMixin is exported/imported properly. + $CustomContextCounterFnPropsMixin implements CustomContextCounterFnProps { _$$CustomContextCounterFnProps._(); diff --git a/test/over_react_redux/hooks/use_dispatch_test.over_react.g.dart b/test/over_react_redux/hooks/use_dispatch_test.over_react.g.dart index 8e5c383d2..0bef8a7f0 100644 --- a/test/over_react_redux/hooks/use_dispatch_test.over_react.g.dart +++ b/test/over_react_redux/hooks/use_dispatch_test.over_react.g.dart @@ -68,8 +68,8 @@ final UiFactoryConfig<_$$UseDispatchCounterFnProps> abstract class _$$UseDispatchCounterFnProps extends UiProps with UseDispatchCounterFnProps, - $UseDispatchCounterFnProps // If this generated mixin is undefined, it's likely because UseDispatchCounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of UseDispatchCounterFnProps, and check that $UseDispatchCounterFnProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because UseDispatchCounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of UseDispatchCounterFnProps, and check that $UseDispatchCounterFnProps is exported/imported properly. + $UseDispatchCounterFnProps { _$$UseDispatchCounterFnProps._(); factory _$$UseDispatchCounterFnProps(Map backingMap) { @@ -157,8 +157,8 @@ final UiFactoryConfig<_$$CustomContextUseDispatchCounterFnProps> abstract class _$$CustomContextUseDispatchCounterFnProps extends UiProps with CustomContextUseDispatchCounterFnProps, - $CustomContextUseDispatchCounterFnProps // If this generated mixin is undefined, it's likely because CustomContextUseDispatchCounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomContextUseDispatchCounterFnProps, and check that $CustomContextUseDispatchCounterFnProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because CustomContextUseDispatchCounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomContextUseDispatchCounterFnProps, and check that $CustomContextUseDispatchCounterFnProps is exported/imported properly. + $CustomContextUseDispatchCounterFnProps { _$$CustomContextUseDispatchCounterFnProps._(); factory _$$CustomContextUseDispatchCounterFnProps(Map backingMap) { diff --git a/test/over_react_redux/hooks/use_store_test.over_react.g.dart b/test/over_react_redux/hooks/use_store_test.over_react.g.dart index 68a084aac..848968ec4 100644 --- a/test/over_react_redux/hooks/use_store_test.over_react.g.dart +++ b/test/over_react_redux/hooks/use_store_test.over_react.g.dart @@ -68,8 +68,8 @@ final UiFactoryConfig<_$$UseStoreCounterFnProps> $UseStoreCounterFnConfig = abstract class _$$UseStoreCounterFnProps extends UiProps with UseStoreCounterFnProps, - $UseStoreCounterFnProps // If this generated mixin is undefined, it's likely because UseStoreCounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of UseStoreCounterFnProps, and check that $UseStoreCounterFnProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because UseStoreCounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of UseStoreCounterFnProps, and check that $UseStoreCounterFnProps is exported/imported properly. + $UseStoreCounterFnProps { _$$UseStoreCounterFnProps._(); factory _$$UseStoreCounterFnProps(Map backingMap) { @@ -156,8 +156,8 @@ final UiFactoryConfig<_$$CustomContextUseStoreCounterFnProps> abstract class _$$CustomContextUseStoreCounterFnProps extends UiProps with CustomContextUseStoreCounterFnProps, - $CustomContextUseStoreCounterFnProps // If this generated mixin is undefined, it's likely because CustomContextUseStoreCounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomContextUseStoreCounterFnProps, and check that $CustomContextUseStoreCounterFnProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because CustomContextUseStoreCounterFnProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CustomContextUseStoreCounterFnProps, and check that $CustomContextUseStoreCounterFnProps is exported/imported properly. + $CustomContextUseStoreCounterFnProps { _$$CustomContextUseStoreCounterFnProps._(); factory _$$CustomContextUseStoreCounterFnProps(Map backingMap) { diff --git a/test/over_react_redux/store_bindings_tests.over_react.g.dart b/test/over_react_redux/store_bindings_tests.over_react.g.dart index 3bd6939f2..6d2d41407 100644 --- a/test/over_react_redux/store_bindings_tests.over_react.g.dart +++ b/test/over_react_redux/store_bindings_tests.over_react.g.dart @@ -109,8 +109,8 @@ final UiFactoryConfig<_$$TestSelectorProps> $TestSelectorConfig = abstract class _$$TestSelectorProps extends UiProps with TestSelectorProps, - $TestSelectorProps // If this generated mixin is undefined, it's likely because TestSelectorProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestSelectorProps, and check that $TestSelectorProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because TestSelectorProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestSelectorProps, and check that $TestSelectorProps is exported/imported properly. + $TestSelectorProps { _$$TestSelectorProps._(); factory _$$TestSelectorProps(Map backingMap) { @@ -195,9 +195,11 @@ final UiFactoryConfig<_$$TestConnectProps> $TestConnectMapViewConfig = abstract class _$$TestConnectProps extends UiProps with TestSelectorProps, - $TestSelectorProps, // If this generated mixin is undefined, it's likely because TestSelectorProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestSelectorProps, and check that $TestSelectorProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because TestSelectorProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestSelectorProps, and check that $TestSelectorProps is exported/imported properly. + $TestSelectorProps, TestConnectPropsMixin, - $TestConnectPropsMixin // If this generated mixin is undefined, it's likely because TestConnectPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestConnectPropsMixin, and check that $TestConnectPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because TestConnectPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of TestConnectPropsMixin, and check that $TestConnectPropsMixin is exported/imported properly. + $TestConnectPropsMixin implements TestConnectProps { _$$TestConnectProps._(); diff --git a/test/vm_tests/builder/parsing/ast_util_test.dart b/test/vm_tests/builder/parsing/ast_util_test.dart index 863a09a1b..acaccfca0 100644 --- a/test/vm_tests/builder/parsing/ast_util_test.dart +++ b/test/vm_tests/builder/parsing/ast_util_test.dart @@ -188,43 +188,43 @@ main() { group('AbstractGetter', () { test('hasAbstractGetter', () { - expect(AbstractGetter(parseAndGetSingleWithType(''' + expect(AbstractGetter(parseAndGetSingleClassish(''' abstract class Foo { String get foo; } ''')).hasAbstractGetter('String', 'foo'), isTrue); - expect(AbstractGetter(parseAndGetSingleWithType(''' + expect(AbstractGetter(parseAndGetSingleClassish(''' abstract class Foo { Generic get foo; } ''')).hasAbstractGetter('Generic', 'foo'), isTrue); - expect(AbstractGetter(parseAndGetSingleWithType(''' + expect(AbstractGetter(parseAndGetSingleClassish(''' abstract class Foo { String get foo => 'impl'; } ''')).hasAbstractGetter('String', 'foo'), isFalse); - expect(AbstractGetter(parseAndGetSingleWithType(''' + expect(AbstractGetter(parseAndGetSingleClassish(''' abstract class Foo { WrongType get foo; } ''')).hasAbstractGetter('String', 'foo'), isFalse); - expect(AbstractGetter(parseAndGetSingleWithType(''' + expect(AbstractGetter(parseAndGetSingleClassish(''' abstract class Foo { String get wrongName; } ''')).hasAbstractGetter('String', 'foo'), isFalse); - expect(AbstractGetter(parseAndGetSingleWithType(''' + expect(AbstractGetter(parseAndGetSingleClassish(''' abstract class Foo { String foo; } ''')).hasAbstractGetter('String', 'foo'), isFalse); - expect(AbstractGetter(parseAndGetSingleWithType(''' + expect(AbstractGetter(parseAndGetSingleClassish(''' abstract class Foo { set foo(String value); } diff --git a/test/vm_tests/builder/parsing/error_collection.dart b/test/vm_tests/builder/parsing/error_collection_test.dart similarity index 99% rename from test/vm_tests/builder/parsing/error_collection.dart rename to test/vm_tests/builder/parsing/error_collection_test.dart index 2426293db..3fdadc6e0 100644 --- a/test/vm_tests/builder/parsing/error_collection.dart +++ b/test/vm_tests/builder/parsing/error_collection_test.dart @@ -97,7 +97,7 @@ main() { test('when the unit is a SyntacticEntity', () { final localFileParseResult = parseString(content: boilerplateString); final propsNameToken = localFileParseResult.unit.childEntities - .whereType() + .whereType() .first .name; diff --git a/test/vm_tests/builder/parsing/members_test.dart b/test/vm_tests/builder/parsing/members_test.dart index 93db26069..7a22f3b8d 100644 --- a/test/vm_tests/builder/parsing/members_test.dart +++ b/test/vm_tests/builder/parsing/members_test.dart @@ -1100,7 +1100,7 @@ main() { final members = BoilerplateMemberHelper.parseAndReturnMembers(boilerplateString); final props = members.whereType().first; - checkForMetaPresence(props.node, collector); + checkForMetaPresence(props.node.asClassish(), collector); expect(errorList, []); expect(warnList, []); }); @@ -1131,7 +1131,7 @@ main() { final members = BoilerplateMemberHelper.parseAndReturnMembers(boilerplateString); final props = members.whereType().first; - checkForMetaPresence(props.node, collector); + checkForMetaPresence(props.node.asClassish(), collector); expect(errorList, []); expect(warnList, [ stringContainsInOrder(['Static class member `meta` is declared in FooProps']), @@ -1164,7 +1164,7 @@ main() { final members = BoilerplateMemberHelper.parseAndReturnMembers(boilerplateString); final props = members.whereType().first; - checkForMetaPresence(props.node, collector); + checkForMetaPresence(props.node.asClassish(), collector); expect(errorList, [ stringContainsInOrder(['Non-static class member `meta` is declared in FooProps']), ]); diff --git a/test_fixtures/gold_output_files/mixin_based/basic.over_react.g.dart.goldFile b/test_fixtures/gold_output_files/mixin_based/basic.over_react.g.dart.goldFile index 1c89197ef..bd1740531 100644 --- a/test_fixtures/gold_output_files/mixin_based/basic.over_react.g.dart.goldFile +++ b/test_fixtures/gold_output_files/mixin_based/basic.over_react.g.dart.goldFile @@ -32,8 +32,8 @@ _$$BasicProps _$Basic([Map backingProps]) => backingProps == null abstract class _$$BasicProps extends UiProps with BasicProps, - $BasicProps // If this generated mixin is undefined, it's likely because BasicProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicProps, and check that $BasicProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because BasicProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicProps, and check that $BasicProps is exported/imported properly. + $BasicProps { _$$BasicProps._(); factory _$$BasicProps(Map backingMap) { diff --git a/test_fixtures/gold_output_files/mixin_based/basic_library.over_react.g.dart.goldFile b/test_fixtures/gold_output_files/mixin_based/basic_library.over_react.g.dart.goldFile index 9c24a163e..fa25b2311 100644 --- a/test_fixtures/gold_output_files/mixin_based/basic_library.over_react.g.dart.goldFile +++ b/test_fixtures/gold_output_files/mixin_based/basic_library.over_react.g.dart.goldFile @@ -33,9 +33,11 @@ _$$BasicPartOfLibProps _$BasicPartOfLib([Map backingProps]) => abstract class _$$BasicPartOfLibProps extends UiProps with ExamplePropsMixin, - $ExamplePropsMixin, // If this generated mixin is undefined, it's likely because ExamplePropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExamplePropsMixin, and check that $ExamplePropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because ExamplePropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ExamplePropsMixin, and check that $ExamplePropsMixin is exported/imported properly. + $ExamplePropsMixin, BasicPartOfLibPropsMixin, - $BasicPartOfLibPropsMixin // If this generated mixin is undefined, it's likely because BasicPartOfLibPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicPartOfLibPropsMixin, and check that $BasicPartOfLibPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because BasicPartOfLibPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicPartOfLibPropsMixin, and check that $BasicPartOfLibPropsMixin is exported/imported properly. + $BasicPartOfLibPropsMixin implements BasicPartOfLibProps { _$$BasicPartOfLibProps._(); @@ -115,9 +117,11 @@ class _$$BasicPartOfLibProps$JsMap extends _$$BasicPartOfLibProps { abstract class _$$BasicPartOfLibState extends UiState with ExampleStateMixin, - $ExampleStateMixin, // If this generated mixin is undefined, it's likely because ExampleStateMixin is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of ExampleStateMixin, and check that $ExampleStateMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because ExampleStateMixin is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of ExampleStateMixin, and check that $ExampleStateMixin is exported/imported properly. + $ExampleStateMixin, BasicPartOfLibStateMixin, - $BasicPartOfLibStateMixin // If this generated mixin is undefined, it's likely because BasicPartOfLibStateMixin is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicPartOfLibStateMixin, and check that $BasicPartOfLibStateMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because BasicPartOfLibStateMixin is not a valid `mixin`-based state mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicPartOfLibStateMixin, and check that $BasicPartOfLibStateMixin is exported/imported properly. + $BasicPartOfLibStateMixin implements BasicPartOfLibState { _$$BasicPartOfLibState._(); @@ -400,9 +404,11 @@ _$$SubPartOfLibProps _$SubPartOfLib([Map backingProps]) => backingProps == null abstract class _$$SubPartOfLibProps extends UiProps with SuperPartOfLibPropsMixin, - $SuperPartOfLibPropsMixin, // If this generated mixin is undefined, it's likely because SuperPartOfLibPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SuperPartOfLibPropsMixin, and check that $SuperPartOfLibPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because SuperPartOfLibPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SuperPartOfLibPropsMixin, and check that $SuperPartOfLibPropsMixin is exported/imported properly. + $SuperPartOfLibPropsMixin, SubPartOfLibPropsMixin, - $SubPartOfLibPropsMixin // If this generated mixin is undefined, it's likely because SubPartOfLibPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SubPartOfLibPropsMixin, and check that $SubPartOfLibPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because SubPartOfLibPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SubPartOfLibPropsMixin, and check that $SubPartOfLibPropsMixin is exported/imported properly. + $SubPartOfLibPropsMixin implements SubPartOfLibProps { _$$SubPartOfLibProps._(); diff --git a/test_fixtures/gold_output_files/mixin_based/basic_two_nine.over_react.g.dart.goldFile b/test_fixtures/gold_output_files/mixin_based/basic_two_nine.over_react.g.dart.goldFile index fe14a011d..212c22517 100644 --- a/test_fixtures/gold_output_files/mixin_based/basic_two_nine.over_react.g.dart.goldFile +++ b/test_fixtures/gold_output_files/mixin_based/basic_two_nine.over_react.g.dart.goldFile @@ -32,8 +32,8 @@ _$$BasicProps _$Basic([Map backingProps]) => backingProps == null abstract class _$$BasicProps extends UiProps with BasicProps, - $BasicProps // If this generated mixin is undefined, it's likely because BasicProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicProps, and check that $BasicProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because BasicProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of BasicProps, and check that $BasicProps is exported/imported properly. + $BasicProps { _$$BasicProps._(); factory _$$BasicProps(Map backingMap) { diff --git a/test_fixtures/gold_output_files/mixin_based/two_nine_with_multiple_factories.over_react.g.dart.goldfile b/test_fixtures/gold_output_files/mixin_based/two_nine_with_multiple_factories.over_react.g.dart.goldfile index a4c3dbb63..223d765f2 100644 --- a/test_fixtures/gold_output_files/mixin_based/two_nine_with_multiple_factories.over_react.g.dart.goldfile +++ b/test_fixtures/gold_output_files/mixin_based/two_nine_with_multiple_factories.over_react.g.dart.goldfile @@ -32,9 +32,11 @@ _$$CounterProps _$_Counter([Map backingProps]) => backingProps == null abstract class _$$CounterProps extends UiProps with CounterPropsMixin, - $CounterPropsMixin, // If this generated mixin is undefined, it's likely because CounterPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CounterPropsMixin, and check that $CounterPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because CounterPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of CounterPropsMixin, and check that $CounterPropsMixin is exported/imported properly. + $CounterPropsMixin, ConnectPropsMixin, - $ConnectPropsMixin // If this generated mixin is undefined, it's likely because ConnectPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ConnectPropsMixin, and check that $ConnectPropsMixin is exported/imported properly. + // If this generated mixin is undefined, it's likely because ConnectPropsMixin is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of ConnectPropsMixin, and check that $ConnectPropsMixin is exported/imported properly. + $ConnectPropsMixin implements CounterProps { _$$CounterProps._(); diff --git a/test_fixtures/gold_output_files/mixin_based/type_parameters.over_react.g.dart.goldFile b/test_fixtures/gold_output_files/mixin_based/type_parameters.over_react.g.dart.goldFile index 3e82e2413..cb813d062 100644 --- a/test_fixtures/gold_output_files/mixin_based/type_parameters.over_react.g.dart.goldFile +++ b/test_fixtures/gold_output_files/mixin_based/type_parameters.over_react.g.dart.goldFile @@ -174,9 +174,8 @@ _$$SingleProps _$Single([Map backingProps]) => backingProps == null abstract class _$$SingleProps extends UiProps with SingleProps, - $SingleProps< - T> // If this generated mixin is undefined, it's likely because SingleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleProps, and check that $SingleProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because SingleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleProps, and check that $SingleProps is exported/imported properly. + $SingleProps { _$$SingleProps._(); factory _$$SingleProps(Map backingMap) { @@ -252,9 +251,8 @@ _$$SingleWithBoundProps _$SingleWithBound([Map backingProps]) => abstract class _$$SingleWithBoundProps extends UiProps with SingleWithBoundProps, - $SingleWithBoundProps< - S> // If this generated mixin is undefined, it's likely because SingleWithBoundProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleWithBoundProps, and check that $SingleWithBoundProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because SingleWithBoundProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleWithBoundProps, and check that $SingleWithBoundProps is exported/imported properly. + $SingleWithBoundProps { _$$SingleWithBoundProps._(); factory _$$SingleWithBoundProps(Map backingMap) { @@ -331,9 +329,8 @@ _$$DoubleProps _$Double([Map backingProps]) => backingProps == null abstract class _$$DoubleProps extends UiProps with DoubleProps, - $DoubleProps // If this generated mixin is undefined, it's likely because DoubleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DoubleProps, and check that $DoubleProps is exported/imported properly. -{ + // If this generated mixin is undefined, it's likely because DoubleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DoubleProps, and check that $DoubleProps is exported/imported properly. + $DoubleProps { _$$DoubleProps._(); factory _$$DoubleProps(Map backingMap) { @@ -408,18 +405,20 @@ _$$ConcreteNoneProps _$ConcreteNone([Map backingProps]) => backingProps == null abstract class _$$ConcreteNoneProps extends UiProps with NoneProps, - $NoneProps, // If this generated mixin is undefined, it's likely because NoneProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of NoneProps, and check that $NoneProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because NoneProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of NoneProps, and check that $NoneProps is exported/imported properly. + $NoneProps, SingleProps, - $SingleProps< - String>, // If this generated mixin is undefined, it's likely because SingleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleProps, and check that $SingleProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because SingleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleProps, and check that $SingleProps is exported/imported properly. + $SingleProps, SingleThatWontBeSpecifiedProps, - $SingleThatWontBeSpecifiedProps, // If this generated mixin is undefined, it's likely because SingleThatWontBeSpecifiedProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleThatWontBeSpecifiedProps, and check that $SingleThatWontBeSpecifiedProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because SingleThatWontBeSpecifiedProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleThatWontBeSpecifiedProps, and check that $SingleThatWontBeSpecifiedProps is exported/imported properly. + $SingleThatWontBeSpecifiedProps, SingleWithBoundProps, - $SingleWithBoundProps< - RegExp>, // If this generated mixin is undefined, it's likely because SingleWithBoundProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleWithBoundProps, and check that $SingleWithBoundProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because SingleWithBoundProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleWithBoundProps, and check that $SingleWithBoundProps is exported/imported properly. + $SingleWithBoundProps, DoubleProps, - $DoubleProps // If this generated mixin is undefined, it's likely because DoubleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DoubleProps, and check that $DoubleProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because DoubleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DoubleProps, and check that $DoubleProps is exported/imported properly. + $DoubleProps implements ConcreteNoneProps { _$$ConcreteNoneProps._(); @@ -504,18 +503,20 @@ _$$ConcreteArgsProps _$ConcreteArgs([Map backingProps]) => backingProps == null abstract class _$$ConcreteArgsProps extends UiProps with NoneProps, - $NoneProps, // If this generated mixin is undefined, it's likely because NoneProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of NoneProps, and check that $NoneProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because NoneProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of NoneProps, and check that $NoneProps is exported/imported properly. + $NoneProps, SingleProps, - $SingleProps< - String>, // If this generated mixin is undefined, it's likely because SingleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleProps, and check that $SingleProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because SingleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleProps, and check that $SingleProps is exported/imported properly. + $SingleProps, SingleThatWontBeSpecifiedProps, - $SingleThatWontBeSpecifiedProps, // If this generated mixin is undefined, it's likely because SingleThatWontBeSpecifiedProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleThatWontBeSpecifiedProps, and check that $SingleThatWontBeSpecifiedProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because SingleThatWontBeSpecifiedProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleThatWontBeSpecifiedProps, and check that $SingleThatWontBeSpecifiedProps is exported/imported properly. + $SingleThatWontBeSpecifiedProps, SingleWithBoundProps, - $SingleWithBoundProps< - RegExp>, // If this generated mixin is undefined, it's likely because SingleWithBoundProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleWithBoundProps, and check that $SingleWithBoundProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because SingleWithBoundProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of SingleWithBoundProps, and check that $SingleWithBoundProps is exported/imported properly. + $SingleWithBoundProps, DoubleProps, - $DoubleProps // If this generated mixin is undefined, it's likely because DoubleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DoubleProps, and check that $DoubleProps is exported/imported properly. + // If this generated mixin is undefined, it's likely because DoubleProps is not a valid `mixin`-based props mixin, or because it is but the generated mixin was not imported. Check the declaration of DoubleProps, and check that $DoubleProps is exported/imported properly. + $DoubleProps implements ConcreteArgsProps { _$$ConcreteArgsProps._(); diff --git a/tools/analyzer_plugin/analysis_options.yaml b/tools/analyzer_plugin/analysis_options.yaml index c1040a326..cac19697a 100644 --- a/tools/analyzer_plugin/analysis_options.yaml +++ b/tools/analyzer_plugin/analysis_options.yaml @@ -3,6 +3,7 @@ include: package:workiva_analysis_options/v1.recommended.yaml analyzer: exclude: - playground/** + - test/test_fixtures/** strong-mode: implicit-casts: false implicit-dynamic: false @@ -14,6 +15,7 @@ analyzer: # Override workiva_analysis_options upgrade to warning unused_import: info import_of_legacy_library_into_null_safe: warning + implementation_imports: warning # ignores from v1.recommended prefer_interpolation_to_compose_strings: ignore @@ -28,3 +30,4 @@ linter: rules: - avoid_returning_null_for_future - avoid_void_async + - implementation_imports diff --git a/tools/analyzer_plugin/lib/src/assist/convert_class_or_function_component.dart b/tools/analyzer_plugin/lib/src/assist/convert_class_or_function_component.dart index 3e78d756a..c3590ea06 100644 --- a/tools/analyzer_plugin/lib/src/assist/convert_class_or_function_component.dart +++ b/tools/analyzer_plugin/lib/src/assist/convert_class_or_function_component.dart @@ -45,9 +45,9 @@ class ConvertClassOrFunctionComponentAssistContributor extends AssistContributor Future _tryConvertClassComponent(ClassDeclaration closestClass) async { // todo use over_react builders' boilerplate parsing to get the associated factory and config instead of this logic - if (!closestClass.name.name.endsWith('Component')) return; + if (!closestClass.name.lexeme.endsWith('Component')) return; - final name = closestClass.name.name.substring(0, closestClass.name.name.length - 'Component'.length); + final name = closestClass.name.lexeme.substring(0, closestClass.name.lexeme.length - 'Component'.length); final generatedFactoryConfigName = '_\$${name}Config'; final factory = node @@ -55,7 +55,7 @@ class ConvertClassOrFunctionComponentAssistContributor extends AssistContributor ?.declarations .whereType() .expand((element) => element.variables.variables) - .firstWhere((element) => element.name.name == name); + .firstWhere((element) => element.name.lexeme == name); if (factory == null) return; final factoryDecl = factory.thisOrAncestorOfType()!; @@ -65,7 +65,7 @@ class ConvertClassOrFunctionComponentAssistContributor extends AssistContributor // todo migrate defaults? - final render = closestClass.members.whereType().firstWhereOrNull((m) => m.name.name == 'render'); + final render = closestClass.members.whereType().firstWhereOrNull((m) => m.name.lexeme == 'render'); if (render == null) return; final renderBody = render.body; @@ -137,7 +137,7 @@ class ConvertClassOrFunctionComponentAssistContributor extends AssistContributor // todo use over_react builders' boilerplate parsing to get the associated factory and name of assiciated props class instead of this logic - final name = factory.name.name; + final name = factory.name.lexeme; final componentClassName = '${name}Component'; final generatedFactoryName = '_\$$name'; final propsClassName = '${name}Props'; @@ -159,7 +159,7 @@ class ConvertClassOrFunctionComponentAssistContributor extends AssistContributor .thisOrAncestorOfType() ?.declarations .whereType() - .firstWhereOrNull((element) => element.name.name == propsClassName) ?? + .firstWhereOrNull((element) => element.name.lexeme == propsClassName) ?? factoryInitializer.thisOrAncestorOfType()!) .end; diff --git a/tools/analyzer_plugin/lib/src/assist/refs/add_create_ref.dart b/tools/analyzer_plugin/lib/src/assist/refs/add_create_ref.dart index c7d0db325..e3ccd73de 100644 --- a/tools/analyzer_plugin/lib/src/assist/refs/add_create_ref.dart +++ b/tools/analyzer_plugin/lib/src/assist/refs/add_create_ref.dart @@ -209,7 +209,7 @@ Pair _getRefInsertionLocation(AstNode node, LineInfo lineInfo) { child.tryCast()?.variables ?? child.tryCast()?.variables; if (variables != null && - variables.variables.any((decl) => decl.name.name != 'Ref' && decl.name.name.endsWith('Ref'))) { + variables.variables.any((decl) => decl.name.lexeme != 'Ref' && decl.name.lexeme.endsWith('Ref'))) { offset = nextLine(child.end, lineInfo); break; } diff --git a/tools/analyzer_plugin/lib/src/async_plugin_apis/diagnostic.dart b/tools/analyzer_plugin/lib/src/async_plugin_apis/diagnostic.dart index 208228a23..bc4d43eda 100644 --- a/tools/analyzer_plugin/lib/src/async_plugin_apis/diagnostic.dart +++ b/tools/analyzer_plugin/lib/src/async_plugin_apis/diagnostic.dart @@ -32,6 +32,7 @@ import 'dart:async'; +import 'package:analyzer/dart/analysis/analysis_context.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer_plugin/channel/channel.dart'; import 'package:analyzer_plugin/plugin/plugin.dart'; @@ -41,7 +42,7 @@ import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin; import 'package:analyzer_plugin/protocol/protocol_generated.dart'; // ignore: implementation_imports -import 'package:analyzer_plugin/src/utilities/fixes/fixes.dart'; +import 'package:analyzer_plugin/src/utilities/fixes/fixes.dart' show DartFixesRequestImpl; import 'package:analyzer_plugin/utilities/fixes/fixes.dart'; import 'package:meta/meta.dart'; import 'package:over_react_analyzer_plugin/src/async_plugin_apis/error_severity_provider.dart'; @@ -56,24 +57,20 @@ import 'package:over_react_analyzer_plugin/src/util/pretty_print.dart'; mixin DiagnosticMixin on ServerPlugin { PluginOptionsReader get pluginOptionsReader; - List getDiagnosticContributors(String path); - - /// Computes errors based on an analysis result and notifies the analyzer. - Future processDiagnosticsForResult(ResolvedUnitResult analysisResult) async { - await getAllErrors(analysisResult); - } + List getDiagnosticContributors(AnalysisContext analysisContext, String path); /// Computes errors based on an analysis result, notifies the analyzer, and /// then returns the list of errors. Future> getAllErrors(ResolvedUnitResult analysisResult) async { - final analysisOptions = pluginOptionsReader.getOptionsForResult(analysisResult); + final analysisContext = analysisResult.session.analysisContext; + final analysisOptions = pluginOptionsReader.getOptionsForContextRoot(analysisContext.contextRoot); try { // If there is something to analyze, do so and notify the analyzer. // Note that notifying with an empty set of errors is important as // this clears errors if they were fixed. final generator = _DiagnosticGenerator( - getDiagnosticContributors(analysisResult.path), + getDiagnosticContributors(analysisContext, analysisResult.path), errorSeverityProvider: AnalysisOptionsErrorSeverityProvider(analysisOptions), ); final result = await generator.generateErrors(analysisResult); @@ -91,11 +88,12 @@ mixin DiagnosticMixin on ServerPlugin { Future handleEditGetFixes(plugin.EditGetFixesParams parameters) async { // We want request errors to propagate if they throw final request = await _getFixesRequest(parameters); - final analysisOptions = pluginOptionsReader.getOptionsForResult(request.result); + final analysisContext = request.result.session.analysisContext; + final analysisOptions = pluginOptionsReader.getOptionsForContextRoot(analysisContext.contextRoot); try { final generator = _DiagnosticGenerator( - getDiagnosticContributors(parameters.file), + getDiagnosticContributors(analysisContext, parameters.file), errorSeverityProvider: AnalysisOptionsErrorSeverityProvider(analysisOptions), ); final result = await generator.generateFixesResponse(request); diff --git a/tools/analyzer_plugin/lib/src/component_usage.dart b/tools/analyzer_plugin/lib/src/component_usage.dart index 450b5ddce..55bb09dba 100644 --- a/tools/analyzer_plugin/lib/src/component_usage.dart +++ b/tools/analyzer_plugin/lib/src/component_usage.dart @@ -71,7 +71,7 @@ class FluentComponentUsage { /// The class element for the builder's props class (or, for parameterized types /// the bound of that type), or `null` if this usage is not fully resolved. - ClassElement? get propsClassElement => builderType?.typeOrBound.tryCast()?.element; + InterfaceElement? get propsClassElement => builderType?.typeOrBound.tryCast()?.element; /// The name of the builder's props class (or, for parameterized types /// the bound of that type), or `null` if this usage is not fully resolved. diff --git a/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart b/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart index 43cc1f1f4..f32329a78 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart @@ -185,13 +185,15 @@ class BadKeyDiagnostic extends ComponentUsageDiagnosticContributor { } static bool inheritsToStringImplFromObject(Element element) => - element - .tryCast() + // Enums have toStrings that include their name. + element is! EnumElement && + (element + .tryCast() ?.lookUpConcreteMethod('toString', element.library!) - ?.thisOrAncestorOfType() + ?.thisOrAncestorOfType() ?.thisType .isDartCoreObject ?? - false; + false); } /// Recursively collects expressions that are used to effectively call `toString()`: diff --git a/tools/analyzer_plugin/lib/src/diagnostic/boilerplate_validator.dart b/tools/analyzer_plugin/lib/src/diagnostic/boilerplate_validator.dart index a8a625a80..cd12e1329 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/boilerplate_validator.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/boilerplate_validator.dart @@ -67,15 +67,15 @@ class BoilerplateValidatorDiagnostic extends DiagnosticContributor { PartDirective? _overReactGeneratedPartDirective; late bool _overReactGeneratedPartDirectiveIsValid; - /// Returns true if the [unit] representing a part file has declarations. + /// Returns true if the [unit] has over_react boilerplate declarations. /// - /// Does not report any errors for the part file, as those are handled when the part file is analyzed - bool _partHasDeclarations(CompilationUnit unit, ResolvedUnitResult parentResult) { + /// Does not report any errors for the unit, as those are handled when each unit is analyzed + bool _unitHasDeclarations(CompilationUnit unit, ParsedUnitResult unitResult) { return orbp .getBoilerplateDeclarations( orbp.detectBoilerplateMembers(unit), orbp.ErrorCollector.callback( - SourceFile.fromString(parentResult.content, url: parentResult.path), + SourceFile.fromString(unitResult.content, url: unitResult.path), // no-op for these. // It is assumed this method will run for parent files, and the part file will get analyzed in its own context. // Need types on the second args to avoid nullability errors, since they come from a non-null-safe library. @@ -206,22 +206,15 @@ class BoilerplateValidatorDiagnostic extends DiagnosticContributor { return; } - final parts = getNonGeneratedParts(result.unit); - - // compute errors for parts files - var anyPartHasDeclarations = false; - for (final part in parts) { - final uri = part.uriSource?.uri; - // URI could not be resolved or source does not exist - if (uri == null) continue; - final partResult = result.session.getParsedUnit(result.session.uriConverter.uriToPath(uri)!); - - if (partResult is ParsedUnitResult && _partHasDeclarations(partResult.unit, result)) { - anyPartHasDeclarations = true; - } - } - - await _computePartDirectiveErrors(result, collector, hasDeclarations || anyPartHasDeclarations); + // Compute errors related to part directives. + final anyUnitHasDeclarations = hasDeclarations || result.libraryElement.units.any((unit) { + final path = result.session.uriConverter.uriToPath(unit.source.uri); + if (path == null) return false; + final unitResult = result.session.getParsedUnit(path); + if (unitResult is! ParsedUnitResult) return false; + return _unitHasDeclarations(unitResult.unit, unitResult); + }); + await _computePartDirectiveErrors(result, collector, anyUnitHasDeclarations); } Future _addPartDirectiveErrorForMember({ diff --git a/tools/analyzer_plugin/lib/src/diagnostic/bool_prop_name_readability.dart b/tools/analyzer_plugin/lib/src/diagnostic/bool_prop_name_readability.dart index 71d77609b..3f0960871 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/bool_prop_name_readability.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/bool_prop_name_readability.dart @@ -3,6 +3,8 @@ import 'package:analyzer/dart/ast/visitor.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart'; import 'package:over_react_analyzer_plugin/src/util/react_types.dart'; +import '../util/util.dart'; + const _desc = r'Name boolean props in a way that makes them easy to read and infer their purpose.'; // const _details = r''' @@ -44,24 +46,22 @@ class BoolPropNameReadabilityDiagnostic extends DiagnosticContributor { @override computeErrors(result, collector) async { final typeProvider = result.libraryElement.typeProvider; - final visitor = PropsVisitor(); + final visitor = PropFieldVisitor(); result.unit.accept(visitor); - final returnMixins = visitor.returnMixins; - - for (final propsClass in returnMixins) { - final mixinFields = propsClass.declaredElement!.fields; - for (final field in mixinFields) { - final propName = field.name; - if (field.type != typeProvider.boolType) continue; - - final fieldDecl = propsClass.getField(propName); - if (fieldDecl == null) continue; - - final readability = checkBoolPropReadability(propName); - if (!readability.isReadable) { - collector.addError(code, result.locationFor(fieldDecl), errorMessageArgs: [propsClass.name, propName]); + for (final tuple in visitor.propFields) { + final propsClass = tuple.item1; + final fields = tuple.item2; + for (final field in fields) { + for (final variable in field.fields.variables) { + final propName = variable.name.lexeme; + if (variable.declaredElement?.type != typeProvider.boolType) continue; + + final readability = checkBoolPropReadability(propName); + if (!readability.isReadable) { + collector.addError(code, result.locationFor(variable), errorMessageArgs: [propsClass.name, propName]); + } } } } @@ -106,24 +106,28 @@ bool hasBooleanContain(String propName) { return propName.toLowerCase().contains(RegExp('(${allowedContainsForBoolProp.join("|")})')); } -class PropsVisitor extends SimpleAstVisitor { - List returnMixins = []; + +class PropFieldVisitor extends SimpleAstVisitor { + List>> propFields = []; @override void visitCompilationUnit(CompilationUnit node) { node.visitChildren(this); } + static List _getPropFields(Iterable members) => + members.whereType().where((f) => !f.isStatic).toList(); + @override void visitClassDeclaration(ClassDeclaration node) { if (node.declaredElement?.isPropsClass ?? false) { - returnMixins.add(node); + propFields.add(Tuple2(node, _getPropFields(node.members))); } } @override void visitMixinDeclaration(MixinDeclaration node) { if (node.declaredElement?.isPropsClass ?? false) { - returnMixins.add(node); + propFields.add(Tuple2(node, _getPropFields(node.members))); } } } diff --git a/tools/analyzer_plugin/lib/src/diagnostic/consumed_props_return_value.dart b/tools/analyzer_plugin/lib/src/diagnostic/consumed_props_return_value.dart index d32ec935b..7436d2843 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/consumed_props_return_value.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/consumed_props_return_value.dart @@ -129,7 +129,7 @@ class ConsumedPropsVisitor extends SimpleAstVisitor { @override void visitMethodDeclaration(MethodDeclaration node) { - if (node.name.name == 'consumedProps') { + if (node.name.lexeme == 'consumedProps') { consumedPropsDeclarations.add(node); } } diff --git a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart index b32f25776..04c5a7ea6 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart @@ -576,11 +576,15 @@ class ExhaustiveDeps extends DiagnosticContributor { // Does this function capture any values // that are in pure scopes (aka render)? final referencedElements = resolvedReferencesWithin(fnNode.body); - for (final ref in referencedElements) { - if (isDeclaredInPureScope(ref.staticElement!) && - // Stable values are fine though, - // although we won't check functions deeper. - !memoizedIsStableKnownHookValue(ref)) { + for (final referenceUnion in referencedElements) { + if (referenceUnion.switchCase( + (identifier) => + isDeclaredInPureScope(identifier.staticElement!) && + // Stable values are fine though, + // although we won't check functions deeper. + !memoizedIsStableKnownHookValue(identifier), + (_) => false, + )) { return false; } } @@ -607,7 +611,7 @@ class ExhaustiveDeps extends DiagnosticContributor { // The original implementation needs to recurse to process references in child scopes, // but we can process all descendant references regardless of scope in one go. - for (final reference in resolvedReferencesWithin(callbackFunction)) { + for (final referenceUnion in resolvedReferencesWithin(callbackFunction)) { // debug( // 'reference.staticElement.ancestors: \n${prettyPrint(reference.staticElement.ancestors.map(elementDebugString).toList())}', // reference); @@ -618,57 +622,87 @@ class ExhaustiveDeps extends DiagnosticContributor { // Note that with the implementation of `resolvedReferencesWithin`, // this is also necessary to filter out references to properties on object. - final referenceElement = reference.staticElement; + final referenceElement = referenceUnion.switchCase( + (identifier) => identifier.staticElement, + (namedType) => namedType.element, + ); if (referenceElement == null) continue; if (!isDeclaredInPureScope(referenceElement)) { continue; } - // Narrow the scope of a dependency if it is, say, a member expression. - // Then normalize the narrowed dependency. - final dependencyNode = getDependency(reference); - final isUsedAsCascadeTarget = dependencyNode.parent?.tryCast()?.target == dependencyNode; - final dependency = analyzePropertyChain( - dependencyNode, - optionalChains, - isInvocationAllowedForNode: true, + // Use `late` because we're using Union.switchCase, meaning the analyzer can't ensure these variables + // are definitely assigned. + late String dependency; + late bool isStable; + late bool isUsedAsCascadeTarget; + referenceUnion.switchCase( + // Identifier case. This will be most refernces. + (reference) { + // Narrow the scope of a dependency if it is, say, a member expression. + // Then normalize the narrowed dependency. + final dependencyNode = getDependency(reference); + isUsedAsCascadeTarget = dependencyNode.parent?.tryCast()?.target == dependencyNode; + dependency = analyzePropertyChain( + dependencyNode, + optionalChains, + isInvocationAllowedForNode: true, + ); + isStable = + memoizedIsStableKnownHookValue(reference) || memoizedIsFunctionWithoutCapturedValues(referenceElement); + + debug(() { + return prettyPrint({ + 'dependency': dependency, + 'dependencyNode': '${dependencyNode.runtimeType} $dependencyNode', + 'reference': '${reference.runtimeType} $reference', + 'isUsedAsCascadeTarget': isUsedAsCascadeTarget, + }); + }, dependencyNode); + + // Accessing ref.current inside effect cleanup is bad. + if ( + // We're in an effect... + isEffect && + // ... and this look like accessing .current... + dependencyNode is Identifier && + getNonCascadedPropertyBeingAccessed(dependencyNode.parent)?.name == 'current' && + // ...in a cleanup function or below... + isInsideEffectCleanup(reference)) { + currentRefsInEffectCleanup[dependency] = _RefInEffectCleanup( + reference: reference, + referenceElement: referenceElement, + ); + } + }, + // 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; + // These aren't possible for type annotations. + isStable = false; + isUsedAsCascadeTarget = false; + + debug(() { + return prettyPrint({ + 'dependency': dependency, + 'reference': '${reference.runtimeType} $reference', + }); + }, reference); + }, ); - debug(() { - return prettyPrint({ - 'dependency': dependency, - 'dependencyNode': '${dependencyNode.runtimeType} $dependencyNode', - 'reference': '${reference.runtimeType} $reference', - 'isUsedAsCascadeTarget': isUsedAsCascadeTarget, - }); - }, dependencyNode); - - // Accessing ref.current inside effect cleanup is bad. - if ( - // We're in an effect... - isEffect && - // ... and this look like accessing .current... - dependencyNode is Identifier && - getNonCascadedPropertyBeingAccessed(dependencyNode.parent)?.name == 'current' && - // ...in a cleanup function or below... - isInsideEffectCleanup(reference)) { - currentRefsInEffectCleanup[dependency] = _RefInEffectCleanup( - reference: reference, - referenceElement: referenceElement, - ); - } // Add the dependency to a map so we can make sure it is referenced // again in our dependencies array. Remember whether it's stable. dependencies.putIfAbsent(dependency, () { return _Dependency( - isStable: - memoizedIsStableKnownHookValue(reference) || memoizedIsFunctionWithoutCapturedValues(referenceElement), + isStable: isStable, references: [], ); }) // Note that for the the `state.set` case, the reference is still `state`. - ..references.add(reference) + ..references.add(referenceUnion) ..isUsedSomewhereAsCascadeTarget |= isUsedAsCascadeTarget; } @@ -732,10 +766,10 @@ class ExhaustiveDeps extends DiagnosticContributor { if (dep.isStable) { stableDependencies.add(key); } - for (final reference in dep.references) { - final parent = reference.parent; + for (final referenceUnion in dep.references) { + final parent = referenceUnion.either.parent; // TODO(greg) make a utility to check for assignments - if (parent is AssignmentExpression && parent.leftHandSide == reference) { + if (parent is AssignmentExpression && parent.leftHandSide == referenceUnion.either) { reportStaleAssignment(parent, key); } } @@ -755,17 +789,18 @@ class ExhaustiveDeps extends DiagnosticContributor { if (setStateInsideEffectWithoutDeps != null) { return; } - for (final reference in references) { + for (final referenceUnion in references) { if (setStateInsideEffectWithoutDeps != null) { continue; } - final isSetState = setStateCallSites.has(reference); + final isSetState = referenceUnion.switchCase(setStateCallSites.has, (_) => false); if (!isSetState) { continue; } - final isDirectlyInsideEffect = reference.thisOrAncestorOfType() == callbackFunction.body; + final isDirectlyInsideEffect = + referenceUnion.either.thisOrAncestorOfType() == callbackFunction.body; if (isDirectlyInsideEffect) { // TODO(ported): we could potentially ignore early returns. setStateInsideEffectWithoutDeps = key; @@ -1080,7 +1115,7 @@ class ExhaustiveDeps extends DiagnosticContributor { // This also preserves generic function expressions. // `void something(T arg) {…}` -> `final something = useCallback((T arg) {…}, […]);` builder.addSimpleReplacement(range.startEnd(construction, construction.name), - 'final ${construction.name.name} = useCallback('); + 'final ${construction.name.lexeme} = useCallback('); // TODO(ported): ideally we'd gather deps here but it would require // restructuring the rule code. Note we're // not adding [] because would that changes semantics. @@ -1267,38 +1302,44 @@ class ExhaustiveDeps extends DiagnosticContributor { final usedDep = dependencies[missingDep]; if (usedDep == null) return false; - return usedDep.references.any((ref) { - final element = ref.staticElement; - if (element != null && isPropVariable(element)) { - // foo() - if (ref.parent.tryCast()?.function == ref) { - return true; - } - // foo.call() - final inv = PropertyInvocation.detectClosest(ref); - if (inv != null) { - return inv.target == ref && inv.functionName.name == 'call'; - } - } - - if (element != null && isProps(element)) { - final inv = PropertyInvocation.detectClosest(ref); - if (inv != null) { - final target = inv.target; - // props.foo() - if (target == ref) { - return true; + return usedDep.references.any((union) { + return union.switchCase( + (ref) { + final element = ref.staticElement; + if (element != null && isPropVariable(element)) { + // foo() + if (ref.parent.tryCast()?.function == ref) { + return true; + } + // foo.call() + final inv = PropertyInvocation.detectClosest(ref); + if (inv != null) { + return inv.target == ref && inv.functionName.name == 'call'; + } } - // props.foo.call() - if (inv.functionName.name == 'call' && - target != null && - getSimpleTargetAndPropertyName(target, allowMethodInvocation: false)?.item1 == ref) { - return true; + + if (element != null && isProps(element)) { + final inv = PropertyInvocation.detectClosest(ref); + if (inv != null) { + final target = inv.target; + // props.foo() + if (target == ref) { + return true; + } + // props.foo.call() + if (inv.functionName.name == 'call' && + target != null && + getSimpleTargetAndPropertyName(target, allowMethodInvocation: false)?.item1 == ref) { + return true; + } + } } - } - } - return false; + return false; + }, + // This check isn't relevant for NamedType references. + (_) => false, + ); }); }).toList(); if (missingCallbackDeps.isNotEmpty) { @@ -1319,7 +1360,12 @@ class ExhaustiveDeps extends DiagnosticContributor { break; } final usedDep = dependencies[missingDep]!; - for (final reference in usedDep.references) { + for (final referenceUnion in usedDep.references) { + // Filter out NamedType (generic type parameters) since this check isn't relevant to them. + // Use a LHS type to ensure we're picking the correct type from the union. + // ignore: omit_local_variable_types + final Identifier? reference = referenceUnion.a; + if (reference == null) continue; // Try to see if we have setState(someExpr(missingDep)). for (var maybeCall = reference.parent; maybeCall != null && maybeCall != componentOrCustomHookFunction?.body; @@ -1335,7 +1381,7 @@ class ExhaustiveDeps extends DiagnosticContributor { // This case is necessary for `stateVar.set` calls, since key for setStateCallSites is the `stateVar` identifier, not `set`. setStateCallSites.getNullableKey(maybeCall.tryCast()?.target.tryCast()); if (correspondingStateVariable != null) { - if ('${correspondingStateVariable.name.name}.value' == missingDep) { + if ('${correspondingStateVariable.name.lexeme}.value' == missingDep) { // setCount(count + 1) setStateRecommendation = _SetStateRecommendation( missingDep: missingDep, @@ -1516,7 +1562,7 @@ class ReactiveHookCallbackInfo { class _Dependency { final bool isStable; - final List references; + final List> references; bool isUsedSomewhereAsCascadeTarget = false; @@ -1525,7 +1571,7 @@ class _Dependency { @override String toString() => prettyPrint({ 'isStable': isStable, - 'references': references, + 'references': references.map((r) => r.either).toList(), 'isUsedSomewhereAsCascadeTarget': isUsedSomewhereAsCascadeTarget, }); } @@ -1544,8 +1590,25 @@ class _RefInEffectCleanup { }); } -Iterable resolvedReferencesWithin(AstNode node) => - allDescendantsOfType(node).where((e) => e.staticElement != null); +Iterable> resolvedReferencesWithin(AstNode node) sync* { + for (final descendant in allDescendants(node)) { + if (descendant is Identifier) { + if (descendant.staticElement != null) { + yield Union.a(descendant); + } + } else if (descendant is NamedType) { + if (descendant.element != null) { + yield Union.b(descendant); + } + } + } +} + +extension on NamedType { + // NamedType.element is added in analyzer 5.11.0; we can't resolve to that version in Dart 2.18, + // so we'll add it here so we don't have to go back through and change it later. + Element? get element => name.staticElement; +} enum HookTypeWithStableMethods { stateHook, reducerHook, transitionHook } @@ -1658,7 +1721,7 @@ bool _hasDeclarationWithName(FunctionBody body, String childName) { // Use a visitor so we properly handle declarations inside blocks. var hasMatch = false; body.visitChildren(_ChildLocalVariableOrFunctionDeclarationVisitor((_, name) { - if (name.name == childName) { + if (name.lexeme == childName) { hasMatch = true; } })); @@ -1666,7 +1729,7 @@ bool _hasDeclarationWithName(FunctionBody body, String childName) { } class _ChildLocalVariableOrFunctionDeclarationVisitor extends RecursiveAstVisitor { - final void Function(Declaration, SimpleIdentifier name) onChildLocalDeclaration; + final void Function(Declaration, Token name) onChildLocalDeclaration; _ChildLocalVariableOrFunctionDeclarationVisitor(this.onChildLocalDeclaration); @@ -1922,7 +1985,7 @@ _Recommendations collectRecommendations({ String? getConstructionExpressionType(Expression node) { if (node is InstanceCreationExpression) { if (node.isConst) return null; - return node.constructorName.type2.name.name; + return node.constructorName.type.name.name; } else if (node is ListLiteral) { return _DepType.list; } else if (node is SetOrMapLiteral) { diff --git a/tools/analyzer_plugin/lib/src/diagnostic/missing_required_prop.dart b/tools/analyzer_plugin/lib/src/diagnostic/missing_required_prop.dart index 8b9aa94e7..ba1a6841a 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/missing_required_prop.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/missing_required_prop.dart @@ -104,7 +104,7 @@ class MissingRequiredPropDiagnostic extends ComponentUsageDiagnosticContributor } // ignore: unnecessary_parenthesis - final accessorClass = (_cachedAccessorClass ??= typeLibrary.getType('Accessor')!); + final accessorClass = (_cachedAccessorClass ??= typeLibrary.getClass('Accessor')!); if (!result.typeSystem.isAssignableTo(type, accessorClass.thisType)) { return false; } diff --git a/tools/analyzer_plugin/lib/src/diagnostic/non_defaulted_prop.dart b/tools/analyzer_plugin/lib/src/diagnostic/non_defaulted_prop.dart index 0be2d94a7..892ef5a5d 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/non_defaulted_prop.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/non_defaulted_prop.dart @@ -91,7 +91,7 @@ class NonDefaultedPropDiagnostic extends DiagnosticContributor { final access = tuple.item1; final variable = tuple.item2; - final variableName = variable.name.name; + final variableName = variable.name.lexeme; await collector.addErrorWithFix( code, diff --git a/tools/analyzer_plugin/lib/src/diagnostic/pseudo_static_lifecycle.dart b/tools/analyzer_plugin/lib/src/diagnostic/pseudo_static_lifecycle.dart index 3c94d4b9c..ec42b4f0e 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/pseudo_static_lifecycle.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/pseudo_static_lifecycle.dart @@ -73,7 +73,7 @@ class PseudoStaticLifecycleDiagnostic extends DiagnosticContributor { if (reference is SuperExpression || reference is ThisExpression) { final parent = reference.parent; if (parent is MethodInvocation) { - if (parent.methodName.name == enclosingMethodName.name) { + if (parent.methodName.name == enclosingMethodName.lexeme) { // Ignore super-calls to same method continue; } else { @@ -82,7 +82,7 @@ class PseudoStaticLifecycleDiagnostic extends DiagnosticContributor { end = parent.methodName.end; } } else if (parent is PropertyAccess) { - if (parent.propertyName.name == enclosingMethodName.name) { + if (parent.propertyName.name == enclosingMethodName.lexeme) { // Ignore super-calls to same getter continue; } else { @@ -99,7 +99,7 @@ class PseudoStaticLifecycleDiagnostic extends DiagnosticContributor { collector.addError( code, result.location(offset: offset, end: end), - errorMessageArgs: [enclosingMethodName.name], + errorMessageArgs: [enclosingMethodName.lexeme], ); } } @@ -115,17 +115,17 @@ class LifecycleMethodVisitor extends GeneralizingAstVisitor { @override void visitMixinDeclaration(MixinDeclaration node) { - visitClassOrMixinDeclaration(node); + visitClassOrMixinDeclaration(node, node.members); } @override void visitClassDeclaration(ClassDeclaration node) { - visitClassOrMixinDeclaration(node); + visitClassOrMixinDeclaration(node, node.members); } - void visitClassOrMixinDeclaration(ClassOrMixinDeclaration node) { - for (final member in node.members) { - if (member is MethodDeclaration && staticMethodNames.contains(member.name.name)) { + void visitClassOrMixinDeclaration(NamedCompilationUnitMember node, List members) { + for (final member in members) { + if (member is MethodDeclaration && staticMethodNames.contains(member.name.lexeme)) { final visitor = ReferenceVisitor(); member.body.accept(visitor); nonStaticReferences.addAll(visitor.nonStaticReferences); diff --git a/tools/analyzer_plugin/lib/src/diagnostic/render_return_value.dart b/tools/analyzer_plugin/lib/src/diagnostic/render_return_value.dart index 690753842..86c900c4e 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/render_return_value.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/render_return_value.dart @@ -183,7 +183,7 @@ class ClassComponentRenderVisitor extends SimpleAstVisitor { @override void visitMethodDeclaration(MethodDeclaration node) { - if (node.name.name == 'render') { + if (node.name.lexeme == 'render') { renderReturnExpressions.addAll(node.body.returnExpressions); } } diff --git a/tools/analyzer_plugin/lib/src/diagnostic/variadic_children_with_keys.dart b/tools/analyzer_plugin/lib/src/diagnostic/variadic_children_with_keys.dart index ac4fbfd10..440158055 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/variadic_children_with_keys.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/variadic_children_with_keys.dart @@ -73,7 +73,7 @@ class VariadicChildrenWithKeys extends ComponentUsageDiagnosticContributor { @override computeErrorsForUsage(result, collector, usage) async { String? reasonWhyUnnecessary; - final parentMethodName = usage.node.thisOrAncestorOfType()?.name.name; + final parentMethodName = usage.node.thisOrAncestorOfType()?.name.lexeme; final parent = usage.node.parent; 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 fb2e028b8..b3bc4e564 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 @@ -69,7 +69,7 @@ bool referencesImplicitThis(SimpleIdentifier identifier) { } // not a class member final Element? enclosingElement = element.enclosingElement; - if (enclosingElement is! ClassElement) { + if (enclosingElement is! InterfaceOrAugmentationElement) { return false; } // comment diff --git a/tools/analyzer_plugin/lib/src/diagnostic/visitors/proptypes_visitors.dart b/tools/analyzer_plugin/lib/src/diagnostic/visitors/proptypes_visitors.dart index 60c501817..58f1b76e4 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/visitors/proptypes_visitors.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/visitors/proptypes_visitors.dart @@ -20,7 +20,7 @@ class PropTypesVisitor extends SimpleAstVisitor { @override void visitMethodDeclaration(MethodDeclaration node) { - if (node.name.name == 'propTypes') { + if (node.name.lexeme == 'propTypes') { node.body.accept(mapVisitor); } } diff --git a/tools/analyzer_plugin/lib/src/plugin.dart b/tools/analyzer_plugin/lib/src/plugin.dart index 33af9615e..f67d28b85 100644 --- a/tools/analyzer_plugin/lib/src/plugin.dart +++ b/tools/analyzer_plugin/lib/src/plugin.dart @@ -31,31 +31,24 @@ import 'dart:async'; -import 'package:analyzer/dart/analysis/context_builder.dart'; -import 'package:analyzer/dart/analysis/context_locator.dart'; -import 'package:analyzer/dart/analysis/context_root.dart' as analyzer; +import 'package:analyzer/dart/analysis/analysis_context.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/file_system/file_system.dart'; -// ignore: implementation_imports -import 'package:analyzer/src/dart/analysis/context_builder.dart' show ContextBuilderImpl; -// ignore: implementation_imports -import 'package:analyzer/src/dart/analysis/driver.dart' show AnalysisDriver, AnalysisDriverGeneric; -import 'package:analyzer_plugin/plugin/navigation_mixin.dart'; import 'package:analyzer_plugin/plugin/plugin.dart'; import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin; -import 'package:analyzer_plugin/utilities/navigation/navigation.dart'; -import 'package:over_react_analyzer_plugin/src/analysis_options/plugin_analysis_options.dart'; import 'package:over_react_analyzer_plugin/src/analysis_options/reader.dart'; import 'package:over_react_analyzer_plugin/src/assist/add_props.dart'; import 'package:over_react_analyzer_plugin/src/assist/convert_class_or_function_component.dart'; import 'package:over_react_analyzer_plugin/src/assist/extract_component.dart'; import 'package:over_react_analyzer_plugin/src/assist/refs/add_create_ref_assist.dart'; + // Can't import this until it stops importing non-null-safe code, otherwise the plugin won't start. //import 'package:over_react_analyzer_plugin/src/assist/toggle_stateful.dart'; import 'package:over_react_analyzer_plugin/src/async_plugin_apis/assist.dart'; import 'package:over_react_analyzer_plugin/src/async_plugin_apis/diagnostic.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic/arrow_function_prop.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic/bad_key.dart'; + // Can't import this until it stops importing non-null-safe code, otherwise the plugin won't start. //import 'package:over_react_analyzer_plugin/src/diagnostic/boilerplate_validator.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic/callback_ref.dart'; @@ -65,6 +58,7 @@ import 'package:over_react_analyzer_plugin/src/diagnostic/dom_prop_types.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic/duplicate_prop_cascade.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic/exhaustive_deps.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic/forward_only_dom_props_to_dom_builders.dart'; + // Can't import this until it stops importing non-null-safe code, otherwise the plugin won't start. //import 'package:over_react_analyzer_plugin/src/diagnostic/incorrect_doc_comment_location.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic/invalid_child.dart'; @@ -81,18 +75,14 @@ import 'package:over_react_analyzer_plugin/src/diagnostic/style_value_diagnostic import 'package:over_react_analyzer_plugin/src/diagnostic/variadic_children.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic/variadic_children_with_keys.dart'; import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart'; -import 'package:over_react_analyzer_plugin/src/util/util.dart'; -abstract class OverReactAnalyzerPluginBase extends ServerPlugin - with +mixin OverReactAnalyzerPluginBase + on + ServerPlugin, // OutlineMixin, DartOutlineMixin, DiagnosticMixin, - NavigationMixin, - DartNavigationMixin, AsyncAssistsMixin, AsyncDartAssistsMixin { - OverReactAnalyzerPluginBase(ResourceProvider provider) : super(provider); - @override final pluginOptionsReader = PluginOptionsReader(); @@ -118,30 +108,10 @@ abstract class OverReactAnalyzerPluginBase extends ServerPlugin ]; @override - List getNavigationContributors(String path) => []; - - // TODO(greg) is there a better way to do this? - analyzer.ContextRoot? _analyzerContextRootForPath(String path) { - final driver = driverForPath(path); - if (driver == null) return null; - - // TODO should this throw? - if (driver is! AnalysisDriver) return null; - - return driver.analysisContext?.contextRoot; - } - - PluginAnalysisOptions? optionsForPath(String path) { - // Do not use protocol.ContextRoot's optionsFile, since it's null at least in tests. - // We'll use te driver's context instead. - final contextRoot = _analyzerContextRootForPath(path); - if (contextRoot == null) return null; - return pluginOptionsReader.getOptionsForContextRoot(contextRoot); - } - - @override - List getDiagnosticContributors(String path) { - final options = optionsForPath(path); + List getDiagnosticContributors(AnalysisContext analysisContext, String path) { + // Do not use protocol.ContextRoot's optionsFile, since it's null at least in tests; + // get it from analysisContext instead. + final options = pluginOptionsReader.getOptionsForContextRoot(analysisContext.contextRoot); return [ PropTypesReturnValueDiagnostic(), DuplicatePropCascadeDiagnostic(), @@ -183,103 +153,34 @@ abstract class OverReactAnalyzerPluginBase extends ServerPlugin // // Disabled for now since it doesn't seem to work consistently // new ReactElementOutlineContributor(), // ]; -} - -/// Analyzer plugin for over_react. -class OverReactAnalyzerPlugin extends OverReactAnalyzerPluginBase { - OverReactAnalyzerPlugin(ResourceProvider provider) : super(provider); @override - String get name => 'over_react'; - - @override - String get contactInfo => 'Workiva Slack channel: #react-analyzer-plugin'; + Future analyzeFile({required AnalysisContext analysisContext, required String path}) async { + if (!path.endsWith('.dart')) return; - @override - AnalysisDriverGeneric createAnalysisDriver(plugin.ContextRoot contextRoot) { - final root = ContextLocator(resourceProvider: resourceProvider).locateRoots( - includedPaths: [contextRoot.root], - excludedPaths: contextRoot.exclude, - optionsFile: contextRoot.optionsFile, - ).single; - final contextBuilder = ContextBuilder(resourceProvider: resourceProvider) as ContextBuilderImpl; - final context = contextBuilder.createContext( - contextRoot: root, - scheduler: analysisDriverScheduler, - byteStore: byteStore, - performanceLog: performanceLog, - ); - final driver = context.driver; - try { - driver.currentSession.analysisContext; - } catch (_) { - channel.sendNotification( - plugin.PluginErrorParams(false, 'Error fetching analysis context; assists may be unavailable', '') - .toNotification()); - } - runZonedGuarded(() { - driver.results.whereType().listen(processDiagnosticsForResult); + await runZonedGuarded(() async { + final result = await analysisContext.currentSession.getResolvedUnit(path); + if (result is ResolvedUnitResult) { + await getAllErrors(result); + } }, (e, stackTrace) { channel.sendNotification(plugin.PluginErrorParams(false, e.toString(), stackTrace.toString()).toNotification()); }); - return driver; } +} - @override - void contentChanged(String path) { - super.driverForPath(path)!.addFile(path); - } +/// Analyzer plugin for over_react. +class OverReactAnalyzerPlugin extends ServerPlugin + with + DiagnosticMixin, + AsyncAssistsMixin, + AsyncDartAssistsMixin, + OverReactAnalyzerPluginBase { + OverReactAnalyzerPlugin(ResourceProvider provider) : super(resourceProvider: provider); @override - Future handleAnalysisSetContextRoots( - plugin.AnalysisSetContextRootsParams parameters) async { - final result = await super.handleAnalysisSetContextRoots(parameters); - // The super-call adds files to the driver, so we need to prioritize them so they get analyzed. - _updatePriorityFiles(); - return result; - } - - List _filesFromSetPriorityFilesRequest = []; + String get name => 'over_react'; @override - Future handleAnalysisSetPriorityFiles( - plugin.AnalysisSetPriorityFilesParams parameters) async { - _filesFromSetPriorityFilesRequest = parameters.files; - _updatePriorityFiles(); - return plugin.AnalysisSetPriorityFilesResult(); - } - - /// AnalysisDriver doesn't fully resolve files that are added via `addFile`; they need to be either explicitly requested - /// via `getResult`/etc, or added to `priorityFiles`. - /// - /// This method updates `priorityFiles` on the driver to include: - /// - /// - Any files prioritized by the analysis server via [handleAnalysisSetPriorityFiles] - /// - All other files the driver has been told to analyze via addFile (in [ServerPlugin.handleAnalysisSetContextRoots]) - /// - /// As a result, [processDiagnosticsForResult] will get called with resolved units, and thus all of our diagnostics - /// will get run on all files in the repo instead of only the currently open/edited ones! - void _updatePriorityFiles() { - final filesToFullyResolve = { - // Ensure these go first, since they're actually considered priority; ... - ..._filesFromSetPriorityFilesRequest, - - /// ... all other files need to be analyzed, but don't trump priority/ - for (var driver2 in driverMap.values) ...(driver2 as AnalysisDriver).addedFiles, - }; - - // From ServerPlugin.handleAnalysisSetPriorityFiles - final filesByDriver = >{}; - for (final file in filesToFullyResolve) { - var contextRoot = contextRootContaining(file); - if (contextRoot != null) { - // TODO(brianwilkerson) Which driver should we use if there is no context root? - var driver = driverMap[contextRoot]!; - filesByDriver.putIfAbsent(driver, () => []).add(file); - } - } - filesByDriver.forEach((driver, files) { - driver.priorityFiles = files; - }); - } + String get contactInfo => 'Workiva Slack channel: #react-analyzer-plugin'; } diff --git a/tools/analyzer_plugin/lib/src/util/analyzer_util.dart b/tools/analyzer_plugin/lib/src/util/analyzer_util.dart index a3157f7ff..4afc5cb28 100644 --- a/tools/analyzer_plugin/lib/src/util/analyzer_util.dart +++ b/tools/analyzer_plugin/lib/src/util/analyzer_util.dart @@ -1,5 +1,5 @@ -// From analyzer 1.7.2 src/dart/ast/utilities.dart -// Permalink: https://github.com/dart-lang/sdk/blob/d97bd979570c4aacde322d8b04256ce477aa9729/pkg/analyzer/lib/src/dart/ast/utilities.dart +// Adapted from analyzer 5.13.0 src/dart/ast/utilities.dart +// Permalink: https://github.com/dart-lang/sdk/blob/efe0ca193f1c1485efc5467fb8dc9dfca6085d39/pkg/analyzer/lib/src/dart/ast/utilities.dart#L1687 // // Copyright 2013, the Dart project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without @@ -29,13 +29,16 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/dart/ast/visitor.dart'; /// An object used to locate the [AstNode] associated with a source range, given /// the AST structure built from the source. More specifically, they will return /// the [AstNode] with the shortest length whose source range completely -/// encompasses the specified range. +/// encompasses the specified range with some exceptions: +/// +/// - Offsets that fall between the name and type/formal parameter list of a +/// declaration will return the declaration node and not the parameter list +/// node. class NodeLocator extends UnifyingAstVisitor { /// The start offset of the range used to identify the node. final int _startOffset; @@ -70,9 +73,47 @@ class NodeLocator extends UnifyingAstVisitor { } catch (_) { return null; } + return _foundNode; } + @override + void visitConstructorDeclaration(ConstructorDeclaration node) { + // Names do not have AstNodes but offsets at the end should be treated as + // part of the declaration (not parameter list). + if (_startOffset == _endOffset && + _startOffset == (node.name ?? node.returnType).end) { + _foundNode = node; + return; + } + + super.visitConstructorDeclaration(node); + } + + @override + void visitFunctionDeclaration(FunctionDeclaration node) { + // Names do not have AstNodes but offsets at the end should be treated as + // part of the declaration (not parameter list). + if (_startOffset == _endOffset && _startOffset == node.name.end) { + _foundNode = node; + return; + } + + super.visitFunctionDeclaration(node); + } + + @override + void visitMethodDeclaration(MethodDeclaration node) { + // Names do not have AstNodes but offsets at the end should be treated as + // part of the declaration (not parameter list). + if (_startOffset == _endOffset && _startOffset == node.name.end) { + _foundNode = node; + return; + } + + super.visitMethodDeclaration(node); + } + @override void visitNode(AstNode node) { // Don't visit a new tree if the result has been already found. @@ -87,7 +128,7 @@ class NodeLocator extends UnifyingAstVisitor { // Fasta scanner reports unterminated string literal errors // and generates a synthetic string token with non-zero length. // Because of this, check for length > 0 rather than !isSynthetic. - if (endToken.type == TokenType.EOF || endToken.length > 0) { + if (endToken.isEof || endToken.length > 0) { break; } endToken = endToken.previous!; @@ -117,7 +158,11 @@ class NodeLocator extends UnifyingAstVisitor { /// An object used to locate the [AstNode] associated with a source range. /// More specifically, they will return the deepest [AstNode] which completely -/// encompasses the specified range. +/// encompasses the specified range with some exceptions: +/// +/// - Offsets that fall between the name and type/formal parameter list of a +/// declaration will return the declaration node and not the parameter list +/// node. class NodeLocator2 extends UnifyingAstVisitor { /// The inclusive start offset of the range used to identify the node. final int _startOffset; @@ -151,6 +196,43 @@ class NodeLocator2 extends UnifyingAstVisitor { return _foundNode; } + @override + void visitConstructorDeclaration(ConstructorDeclaration node) { + // Names do not have AstNodes but offsets at the end should be treated as + // part of the declaration (not parameter list). + if (_startOffset == _endOffset && + _startOffset == (node.name ?? node.returnType).end) { + _foundNode = node; + return; + } + + super.visitConstructorDeclaration(node); + } + + @override + void visitFunctionDeclaration(FunctionDeclaration node) { + // Names do not have AstNodes but offsets at the end should be treated as + // part of the declaration (not parameter list). + if (_startOffset == _endOffset && _startOffset == node.name.end) { + _foundNode = node; + return; + } + + super.visitFunctionDeclaration(node); + } + + @override + void visitMethodDeclaration(MethodDeclaration node) { + // Names do not have AstNodes but offsets at the end should be treated as + // part of the declaration (not parameter list). + if (_startOffset == _endOffset && _startOffset == node.name.end) { + _foundNode = node; + return; + } + + super.visitMethodDeclaration(node); + } + @override void visitNode(AstNode node) { // Don't visit a new tree if the result has been already found. @@ -165,7 +247,7 @@ class NodeLocator2 extends UnifyingAstVisitor { // Fasta scanner reports unterminated string literal errors // and generates a synthetic string token with non-zero length. // Because of this, check for length > 0 rather than !isSynthetic. - if (endToken.type == TokenType.EOF || endToken.length > 0) { + if (endToken.isEof || endToken.length > 0) { break; } endToken = endToken.previous!; diff --git a/tools/analyzer_plugin/lib/src/util/ast_util.dart b/tools/analyzer_plugin/lib/src/util/ast_util.dart index eea0ad18c..b8803f405 100644 --- a/tools/analyzer_plugin/lib/src/util/ast_util.dart +++ b/tools/analyzer_plugin/lib/src/util/ast_util.dart @@ -17,8 +17,8 @@ import 'util.dart'; /// or null if the [element] doesn't correspond to a variable declaration, or if it can't be found in [root]. VariableDeclaration? lookUpVariable(Element element, AstNode root) { final node = NodeLocator2(element.nameOffset).searchWithin(root); - if (node is Identifier && node.staticElement == element) { - return node.parent.tryCast(); + if (node is VariableDeclaration && node.declaredElement == element) { + return node; } return null; @@ -31,10 +31,11 @@ VariableDeclaration? lookUpVariable(Element element, AstNode root) { /// Returns null if the [element] doesn't correspond to one of these cases, or if it can't be found in [root]. FunctionExpression? lookUpFunction(Element element, AstNode root) { final node = NodeLocator2(element.nameOffset).searchWithin(root); - if (node is Identifier && node.staticElement == element) { - final parent = node.parent; - return parent.tryCast()?.functionExpression ?? - parent.tryCast()?.initializer?.tryCast(); + if (node is FunctionDeclaration && node.declaredElement == element) { + return node.functionExpression; + } + if (node is VariableDeclaration && node.declaredElement == element) { + return node.initializer?.tryCast(); } return null; @@ -132,11 +133,6 @@ extension TypeOrBound on DartType { } } -extension ClassOrMixinDeclarationUtils on ClassOrMixinDeclaration { - /// Similar to [getField], but returns the entire declaration instead. - FieldDeclaration? getFieldDeclaration(String name) => getField(name)?.thisOrAncestorOfType(); -} - int prevLine(int offset, LineInfo lineInfo) { return lineInfo.getOffsetOfLine(lineInfo.getLocation(offset).lineNumber - 1); } @@ -333,7 +329,7 @@ extension FunctionBodyUtils on FunctionBody { MethodDeclaration? get parentMethod => parent?.tryCast(); String get functionNameOrDescription { - final name = parentExpression?.parentDeclaration?.name.name; + final name = parentExpression?.parentDeclaration?.name.lexeme; if (name != null) return name; // TODO come up with a better description in some cases diff --git a/tools/analyzer_plugin/lib/src/util/function_components.dart b/tools/analyzer_plugin/lib/src/util/function_components.dart index 9fbf400e9..ed14ad3d7 100644 --- a/tools/analyzer_plugin/lib/src/util/function_components.dart +++ b/tools/analyzer_plugin/lib/src/util/function_components.dart @@ -1,4 +1,5 @@ import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:over_react_analyzer_plugin/src/util/hooks.dart'; @@ -46,7 +47,7 @@ class FunctionComponent { /// The name of this function component's factory, if it exists and could be found. /// /// See [factoryVariable] for more info on when this may be null. - SimpleIdentifier? get factoryName => factoryVariable?.name; + Token? get factoryName => factoryVariable?.name; } FunctionComponent? getClosestFunctionComponent(AstNode node) { diff --git a/tools/analyzer_plugin/lib/src/util/hooks.dart b/tools/analyzer_plugin/lib/src/util/hooks.dart index 2ef2a4b8d..6f6824455 100644 --- a/tools/analyzer_plugin/lib/src/util/hooks.dart +++ b/tools/analyzer_plugin/lib/src/util/hooks.dart @@ -16,7 +16,7 @@ FunctionBody? getClosestCustomHookFunction(AstNode node) => bool isCustomHookFunction(FunctionBody body) { final declaration = body.parentDeclaration; - return declaration != null && isHookName(declaration.name.name); + return declaration != null && isHookName(declaration.name.lexeme); } /// A utility class that provides information about a usage of a React hook. diff --git a/tools/analyzer_plugin/lib/src/util/ignore_info.dart b/tools/analyzer_plugin/lib/src/util/ignore_info.dart index 3ce756a5b..5b1b3c04b 100644 --- a/tools/analyzer_plugin/lib/src/util/ignore_info.dart +++ b/tools/analyzer_plugin/lib/src/util/ignore_info.dart @@ -29,7 +29,8 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/src/dart/ast/token.dart'; // ignore: implementation_imports +// ignore: implementation_imports +import 'package:analyzer/src/dart/ast/token.dart' show CommentToken; /// The name and location of a diagnostic name in an ignore comment. class DiagnosticName implements IgnoredElement { @@ -103,13 +104,12 @@ class IgnoreInfo { /// Initialize a newly created instance of this class to represent the ignore /// comments in the given compilation [unit]. IgnoreInfo.forDart(CompilationUnit unit, String content) { - var lineInfo = unit.lineInfo!; for (final comment in unit.ignoreComments) { var lexeme = comment.lexeme; if (lexeme.contains('ignore:')) { - var location = lineInfo.getLocation(comment.offset); + var location = unit.lineInfo.getLocation(comment.offset); var lineNumber = location.lineNumber; - var offsetOfLine = lineInfo.getOffsetOfLine(lineNumber - 1); + var offsetOfLine = unit.lineInfo.getOffsetOfLine(lineNumber - 1); var beforeMatch = content.substring( offsetOfLine, offsetOfLine + location.columnNumber - 1); if (beforeMatch.trim().isEmpty) { diff --git a/tools/analyzer_plugin/lib/src/util/react_types.dart b/tools/analyzer_plugin/lib/src/util/react_types.dart index 83ea12981..0cc46b3c5 100644 --- a/tools/analyzer_plugin/lib/src/util/react_types.dart +++ b/tools/analyzer_plugin/lib/src/util/react_types.dart @@ -23,7 +23,7 @@ extension ReactTypes$Element on Element { bool isOrIsSubtypeOfElementFromPackage(String typeName, String packageName) { final element = this; - return element is ClassElement && + return element is InterfaceElement && (element.isElementFromPackage(typeName, packageName) || element.allSupertypes.any((type) => type.element.isElementFromPackage(typeName, packageName))); } diff --git a/tools/analyzer_plugin/lib/src/util/util.dart b/tools/analyzer_plugin/lib/src/util/util.dart index 270c64a15..68e3ebacd 100644 --- a/tools/analyzer_plugin/lib/src/util/util.dart +++ b/tools/analyzer_plugin/lib/src/util/util.dart @@ -50,3 +50,32 @@ class Tuple2 { Tuple2(this.item1, this.item2); } + +/// A wrapper around two classes that can be used to pass data when the possible +/// type is not limited to a single class. +/// +/// Subset of package:union functionality +class Union { + final A? a; + final B? b; + + Union.a(A this.a) + : b = null; + Union.b(B this.b) + : a = null; + + /// Executes a callback based upon which field is set. + T switchCase(T Function(A) onA, T Function(B) onB) { + final a = this.a; + return a != null ? onA(a) : onB(b!); + } +} + +/// Utilities that supplement that functionality of the [Union] class. +/// +/// C resolves statically to the closest common ancestor type of A and B. +extension UnionHelper on Union { + /// Access [a] or [b] while allowing the analyzer to provide type inference + /// when possible. + C get either => (a ?? b)!; +} diff --git a/tools/analyzer_plugin/playground/pubspec.yaml b/tools/analyzer_plugin/playground/pubspec.yaml index 4e5ba39f9..e1727b418 100644 --- a/tools/analyzer_plugin/playground/pubspec.yaml +++ b/tools/analyzer_plugin/playground/pubspec.yaml @@ -5,8 +5,8 @@ environment: dependencies: over_react: ^3.5.3 dev_dependencies: - build_runner: '>=1.0.0 <3.0.0' - build_web_compilers: '>=2.0.0 <4.0.0' + build_runner: ^2.0.0 + build_web_compilers: ^3.0.0 workiva_analysis_options: ^1.1.0 dependency_overrides: diff --git a/tools/analyzer_plugin/pubspec.yaml b/tools/analyzer_plugin/pubspec.yaml index 8f51ff5aa..86618599e 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -6,9 +6,8 @@ repository: https://github.com/Workiva/over_react/tree/master/tools/analyzer_plu environment: sdk: '>=2.12.0 <3.0.0' dependencies: - analyzer: ^2.4.0 - # Can't be 0.7.0 since its results in errors around JenkinsSmiHash not existing - analyzer_plugin: ^0.8.0 + analyzer: ^5.1.0 + analyzer_plugin: ^0.11.0 collection: ^1.15.0-nullsafety.4 meta: ^1.6.0 # Upon release, this should be pinned to the over_react version from ../../pubspec.yaml @@ -23,17 +22,16 @@ dev_dependencies: build_test: ^2.0.0 convert: ^3.0.0 crypto: ^3.0.0 - dart_dev: '>=3.8.5 <5.0.0' + dart_dev: ^4.0.0 dart_style: ^2.0.0 dependency_validator: ^3.0.0 glob: ^2.0.0 io: ^1.0.0 logging: ^1.0.1 markdown: ^4.0.0 - package_config: ^2.0.0 - pub_semver: ^2.0.0 test: ^1.14.0 test_reflective_loader: ^0.2.0 + uuid: '>3.0.0 <5.0.0' workiva_analysis_options: ^1.1.0 dependency_validator: diff --git a/tools/analyzer_plugin/test/integration/assists/add_create_ref_test.dart b/tools/analyzer_plugin/test/integration/assists/add_create_ref_test.dart index 24d80cacd..410c98646 100644 --- a/tools/analyzer_plugin/test/integration/assists/add_create_ref_test.dart +++ b/tools/analyzer_plugin/test/integration/assists/add_create_ref_test.dart @@ -57,13 +57,13 @@ final HasNoRefs = uiFunction( '''; Future test_noAssist() async { - final source = newSource('test.dart', 'var foo = true;'); + final source = newSource('var foo = true;'); final selection = createSelection(source, '#var foo = true;#'); await expectNoAssist(selection); } Future test_noAssistWithExistingRefInFnComponentDecl() async { - final source = newSource('test.dart', ''' + final source = newSource(''' import 'package:over_react/over_react.dart'; final HasRefs = uiFunction( @@ -79,13 +79,13 @@ final HasRefs = uiFunction( } Future test_noAssistWithSelectionOutsideReturnExpressionInFnComponent() async { - var source = newSource('test.dart', usageSourceWithinFnComponent(fixed: false)); + var source = newSource(usageSourceWithinFnComponent(fixed: false)); var selection = createSelection(source, '(##'); await expectNoAssist(selection); } Future test_noAssistWithExistingRefInClassComponentDecl() async { - final source = newSource('test.dart', ''' + final source = newSource(''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; @@ -108,13 +108,13 @@ class HasRefsComponent extends UiComponent2 { } Future test_noAssistWithSelectionOutsideReturnExpressionInClassComponent() async { - var source = newSource('test.dart', usageSourceWithinClassComponent(fixed: false)); + var source = newSource(usageSourceWithinClassComponent(fixed: false)); var selection = createSelection(source, ' {##'); await expectNoAssist(selection); } Future test_classComponentAssist_componentNameSelection() async { - var source = newSource('test.dart', usageSourceWithinClassComponent(fixed: false)); + var source = newSource(usageSourceWithinClassComponent(fixed: false)); var selection = createSelection(source, 'return (#Child#()'); final change = await expectAndGetSingleAssist(selection); source = applySourceChange(change, source); @@ -122,7 +122,7 @@ class HasRefsComponent extends UiComponent2 { } Future test_classComponentAssist_zeroWidthSelection() async { - var source = newSource('test.dart', usageSourceWithinClassComponent(fixed: false)); + var source = newSource(usageSourceWithinClassComponent(fixed: false)); var selection = createSelection(source, 'return (##Child()'); final change = await expectAndGetSingleAssist(selection); source = applySourceChange(change, source); @@ -130,7 +130,7 @@ class HasRefsComponent extends UiComponent2 { } Future test_classComponentAssist_propCascadeSelection() async { - var source = newSource('test.dart', usageSourceWithinClassComponent(fixed: false)); + var source = newSource(usageSourceWithinClassComponent(fixed: false)); var selection = createSelection(source, '..id ##= \'foo\''); final change = await expectAndGetSingleAssist(selection); source = applySourceChange(change, source); @@ -138,7 +138,7 @@ class HasRefsComponent extends UiComponent2 { } Future test_fnComponentAssist_componentNameSelection() async { - var source = newSource('test.dart', usageSourceWithinFnComponent(fixed: false)); + var source = newSource(usageSourceWithinFnComponent(fixed: false)); var selection = createSelection(source, 'return (#Child#()'); final change = await expectAndGetSingleAssist(selection); source = applySourceChange(change, source); @@ -146,7 +146,7 @@ class HasRefsComponent extends UiComponent2 { } Future test_fnComponentAssist_zeroWidthSelection() async { - var source = newSource('test.dart', usageSourceWithinFnComponent(fixed: false)); + var source = newSource(usageSourceWithinFnComponent(fixed: false)); var selection = createSelection(source, 'return (##Child()'); final change = await expectAndGetSingleAssist(selection); source = applySourceChange(change, source); @@ -154,7 +154,7 @@ class HasRefsComponent extends UiComponent2 { } Future test_fnComponentAssist_propCascadeSelection() async { - var source = newSource('test.dart', usageSourceWithinFnComponent(fixed: false)); + var source = newSource(usageSourceWithinFnComponent(fixed: false)); var selection = createSelection(source, '..id ##= \'foo\''); final change = await expectAndGetSingleAssist(selection); source = applySourceChange(change, source); diff --git a/tools/analyzer_plugin/test/integration/assists/add_props_test.dart b/tools/analyzer_plugin/test/integration/assists/add_props_test.dart index c049e9118..364afe4b5 100644 --- a/tools/analyzer_plugin/test/integration/assists/add_props_test.dart +++ b/tools/analyzer_plugin/test/integration/assists/add_props_test.dart @@ -27,13 +27,13 @@ var foo = Dom.div()('hello'); '''; Future test_noAssist() async { - final source = newSource('test.dart', 'var foo = true;'); + final source = newSource('var foo = true;'); final selection = createSelection(source, '#var foo = true;#'); await expectNoAssist(selection); } Future test_noAssistForSelection() async { - final source = newSource('test.dart', simpleSource); + final source = newSource(simpleSource); final selection = createSelection(source, '#var foo#'); await expectNoAssist(selection); } @@ -48,7 +48,7 @@ var foo = Dom.div()('hello'); ]); Future test_addsParensAndPropsCascade() async { - var source = newSource('test.dart', simpleSource); + var source = newSource(simpleSource); var selection = createSelection(source, '#Dom.div#'); final change = await expectAndGetSingleAssist(selection); source = applySourceChange(change, source); @@ -62,7 +62,7 @@ var foo = (Dom.div()..)('hello'); } Future test_alreadyHasParens() async { - var source = newSource('test.dart', ''' + var source = newSource(''' import 'package:over_react/over_react.dart'; var foo = (Dom.div())('hello'); '''); diff --git a/tools/analyzer_plugin/test/integration/assists/toggle_statefulness_test.dart b/tools/analyzer_plugin/test/integration/assists/toggle_statefulness_test.dart index 99e9e2e4a..99364d6ab 100644 --- a/tools/analyzer_plugin/test/integration/assists/toggle_statefulness_test.dart +++ b/tools/analyzer_plugin/test/integration/assists/toggle_statefulness_test.dart @@ -27,7 +27,7 @@ class AddStatefulnessAssist extends AssistTestBase with BoilerplateAssistTestStr AssistKind get assistKindUnderTest => ToggleComponentStatefulness.makeStateful; Future test_noAssist() async { - final source = newSource('test.dart', 'var foo = true;'); + final source = newSource('var foo = true;'); final selection = createSelection(source, '#var foo = true;#'); await expectNoAssist(selection); } @@ -35,7 +35,7 @@ class AddStatefulnessAssist extends AssistTestBase with BoilerplateAssistTestStr Future test_noAssistOnFactory() async { final generatedSource = simpleUiComponentSource(); - var source = newSource(fileName, generatedSource); + var source = newSource(generatedSource, path: fileName); var selection = createSelection(source, r'UiFactory #Foo# = _$Foo;'); await expectNoAssist(selection); } @@ -43,7 +43,7 @@ class AddStatefulnessAssist extends AssistTestBase with BoilerplateAssistTestStr Future test_noAssistOnProps() async { final generatedSource = simpleUiComponentSource(); - var source = newSource(fileName, generatedSource); + var source = newSource(generatedSource, path: fileName); var selection = createSelection(source, 'mixin #FooProps# on UiProps {}'); await expectNoAssist(selection); } @@ -66,7 +66,7 @@ class TestComponent extends UiComponent { } '''; - var source = newSource(fileName, oldBoilerplate); + var source = newSource(oldBoilerplate, path: fileName); var selection = createSelection(source, 'class #TestComponent# extends'); await expectNoAssist(selection); } @@ -86,13 +86,13 @@ class TestComponent extends AbstractBazComponent { } '''; - var source = newSource(fileName, unknownBase); + var source = newSource(unknownBase, path: fileName); var selection = createSelection(source, 'class #TestComponent# extends'); await expectNoAssist(selection); } Future test_addsStatefulness() async { - var retrievedSource = newSource(fileName, simpleUiComponentSource()); + var retrievedSource = newSource(simpleUiComponentSource(), path: fileName); var selection = createSelection(retrievedSource, componentNameSelector); final change = await expectAndGetSingleAssist(selection); retrievedSource = applySourceChange(change, retrievedSource); @@ -100,7 +100,7 @@ class TestComponent extends AbstractBazComponent { } Future test_addsStatefulnessWithoutDefaultProps() async { - var retrievedSource = newSource(fileName, simpleUiComponentSource(includeDefaultProps: false)); + var retrievedSource = newSource(simpleUiComponentSource(includeDefaultProps: false), path: fileName); var selection = createSelection(retrievedSource, componentNameSelector); final change = await expectAndGetSingleAssist(selection); retrievedSource = applySourceChange(change, retrievedSource); @@ -108,7 +108,7 @@ class TestComponent extends AbstractBazComponent { } Future test_addsStatefulnessToFlux() async { - var retrievedSource = newSource(fileName, fluxUiComponentSource()); + var retrievedSource = newSource(fluxUiComponentSource(), path: fileName); var selection = createSelection(retrievedSource, componentNameSelector); final change = await expectAndGetSingleAssist(selection); retrievedSource = applySourceChange(change, retrievedSource); @@ -116,7 +116,7 @@ class TestComponent extends AbstractBazComponent { } Future test_addsStatefulnessToFluxWithoutDefaultProps() async { - var retrievedSource = newSource(fileName, fluxUiComponentSource(includeDefaultProps: false)); + var retrievedSource = newSource(fluxUiComponentSource(includeDefaultProps: false), path: fileName); var selection = createSelection(retrievedSource, componentNameSelector); final change = await expectAndGetSingleAssist(selection); retrievedSource = applySourceChange(change, retrievedSource); @@ -132,7 +132,7 @@ class RemoveStatefulnessAssist extends AssistTestBase with BoilerplateAssistTest AssistKind get assistKindUnderTest => ToggleComponentStatefulness.makeStateless; Future test_noAssist() async { - final source = newSource('test.dart', 'var foo = true;'); + final source = newSource('var foo = true;'); final selection = createSelection(source, '#var foo = true;#'); await expectNoAssist(selection); } @@ -140,7 +140,7 @@ class RemoveStatefulnessAssist extends AssistTestBase with BoilerplateAssistTest Future test_noAssistOnFactory() async { final generatedSource = simpleUiComponentSource(isStateful: true); - var source = newSource(fileName, generatedSource); + var source = newSource(generatedSource, path: fileName); var selection = createSelection(source, r'UiFactory #Foo# = _$Foo;'); await expectNoAssist(selection); } @@ -148,7 +148,7 @@ class RemoveStatefulnessAssist extends AssistTestBase with BoilerplateAssistTest Future test_noAssistOnProps() async { final generatedSource = simpleUiComponentSource(isStateful: true); - var source = newSource(fileName, generatedSource); + var source = newSource(generatedSource, path: fileName); var selection = createSelection(source, 'mixin #FooProps# on UiProps {}'); await expectNoAssist(selection); } @@ -174,13 +174,13 @@ class TestComponent extends UiStatefulComponent { } '''; - var source = newSource(fileName, oldBoilerplate); + var source = newSource(oldBoilerplate, path: fileName); var selection = createSelection(source, 'class #TestComponent# extends'); await expectNoAssist(selection); } Future test_removesStatefulness() async { - var retrievedSource = newSource(fileName, simpleUiComponentSource(isStateful: true)); + var retrievedSource = newSource(simpleUiComponentSource(isStateful: true), path: fileName); var selection = createSelection(retrievedSource, componentNameSelector); final change = await expectAndGetSingleAssist(selection); retrievedSource = applySourceChange(change, retrievedSource); @@ -208,7 +208,7 @@ class FooComponent extends UiStatefulComponent2 { } '''; - var retrievedSource = newSource(fileName, statefulComponentWithNoInitialState); + var retrievedSource = newSource(statefulComponentWithNoInitialState, path: fileName); var selection = createSelection(retrievedSource, componentNameSelector); final change = await expectAndGetSingleAssist(selection); retrievedSource = applySourceChange(change, retrievedSource); @@ -216,7 +216,7 @@ class FooComponent extends UiStatefulComponent2 { } Future test_removesStatefulnessToFlux() async { - var retrievedSource = newSource(fileName, fluxUiComponentSource(isStateful: true)); + var retrievedSource = newSource(fluxUiComponentSource(isStateful: true), path: fileName); var selection = createSelection(retrievedSource, componentNameSelector); final change = await expectAndGetSingleAssist(selection); retrievedSource = applySourceChange(change, retrievedSource); diff --git a/tools/analyzer_plugin/test/integration/diagnostics/analysis_options_configuration_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/analysis_options_configuration_test.dart index ed1fff9c3..33a2223cf 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/analysis_options_configuration_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/analysis_options_configuration_test.dart @@ -21,7 +21,7 @@ void main() { } Future> computeErrors({String sourceCode = rulesOfHooksErrorSourceCode}) async { - final source = testBase.newSource('test.dart', sourceCode); + final source = testBase.newSource(sourceCode); final result = await testBase.testPlugin.getResolvedUnitResult(testBase.sourcePath(source)); final errors = await testBase.testPlugin.getAllErrors(result); diff --git a/tools/analyzer_plugin/test/integration/diagnostics/arrow_function_prop_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/arrow_function_prop_test.dart index c1d07b792..99a9efa5b 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/arrow_function_prop_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/arrow_function_prop_test.dart @@ -32,12 +32,12 @@ var foo = (Dom.div() '''; Future test_noError() async { - final source = newSource('test.dart', 'var foo = true;'); + final source = newSource('var foo = true;'); expect(await getAllErrors(source), isEmpty); } Future test_noErrorLastInCascade() async { - final source = newSource('test.dart', /*language=dart*/ r''' + final source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; var foo = (Dom.div() @@ -48,7 +48,7 @@ var foo = (Dom.div() } Future test_noErrorLastInCascadeWithDescendantCascade() async { - final source = newSource('test.dart', /*language=dart*/ r''' + final source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; @@ -70,7 +70,7 @@ var foo = (Dom.div() } Future test_noErrorForSelection() async { - final source = newSource('test.dart', simpleSource); + final source = newSource(simpleSource); final selection = createSelection(source, '#var foo#'); await expectNoErrorFix(selection); } @@ -85,7 +85,7 @@ var foo = (Dom.div() ]); Future test_errorFix() async { - var source = newSource('test.dart', simpleSource); + var source = newSource(simpleSource); final selection = createSelection(source, "#(_) => 'click'#"); final errorFix = await expectSingleErrorFix(selection); expect(errorFix.fixes.single.change.selection, isNull); @@ -100,7 +100,7 @@ var foo = (Dom.div() } Future test_multipleErrors() async { - final source = newSource('test.dart', ''' + final source = newSource(''' import 'package:over_react/over_react.dart'; var foo = (Dom.div()..onClick = (_) => null..key = 'foo')(''); var bar = (Dom.div()..onSubmit = (_) => null..key = 'bar')(''); diff --git a/tools/analyzer_plugin/test/integration/diagnostics/bad_key_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/bad_key_test.dart index 2cbacbd8f..d5c2974ea 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/bad_key_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/bad_key_test.dart @@ -23,7 +23,7 @@ abstract class BadKeyDiagnosticTest extends DiagnosticTestBase { @override get fixKindUnderTest => null; - Source newSourceWithPrefix(String sourceFragment) => newSource('test.dart', sourcePrefix + sourceFragment); + Source newSourceWithPrefix(String sourceFragment) => newSource(sourcePrefix + sourceFragment); static const sourcePrefix = /*language=dart*/ r''' import 'package:over_react/over_react.dart'; @@ -47,6 +47,8 @@ class MyModelWithCustomToString { @override toString() => '$id'; } + +enum AnEnum { foo } '''; } @@ -59,6 +61,7 @@ class BadKeyDiagnosticTest_NoErrors extends BadKeyDiagnosticTest { final source = newSourceWithPrefix(/*language=dart*/ r''' test() => [ (Dom.div()..key = 'a string')(), + (Dom.div()..key = AnEnum.foo)(), (Dom.div()..key = 122)(), (Dom.div()..key = modelVar.id)(), (Dom.div()..key = modelVarWithCustomToString)(), diff --git a/tools/analyzer_plugin/test/integration/diagnostics/boilerplate_validator_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/boilerplate_validator_test.dart index 278e4ea2c..4de371e3c 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/boilerplate_validator_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/boilerplate_validator_test.dart @@ -39,7 +39,7 @@ abstract class LegacyAbstractComponent extends UiComponent '''; Future test_noErrors() async { - final _source = newSource('test.dart', source); + final _source = newSource(source); expect(await getAllErrors(_source), isEmpty); } } @@ -59,7 +59,7 @@ ${BoilerplateValidatorDiagnosticTest.boilerplateThatRequiresGeneratedPart} '''; Future test_error() async { - final _source = newSource('test.dart', source); + final _source = newSource(source); final allErrors = await getAllErrors(_source); expect( allErrors, @@ -71,7 +71,7 @@ ${BoilerplateValidatorDiagnosticTest.boilerplateThatRequiresGeneratedPart} } Future test_errorFix() async { - var _source = newSource('test.dart', source); + var _source = newSource(source); final selection = createSelection(_source, "UiFactory #Foo# ="); final errorFix = await expectSingleErrorFix(selection); expect(errorFix.fixes.single.change.selection, isNull); @@ -101,7 +101,7 @@ part 'test.over_react.g.dart'; '''; Future test_error() async { - final _source = newSource('test.dart', source); + final _source = newSource(source); final allErrors = await getAllErrors(_source); expect( allErrors, @@ -115,7 +115,7 @@ part 'test.over_react.g.dart'; } Future test_errorFix() async { - var _source = newSource('test.dart', source); + var _source = newSource(source); final selection = createSelection(_source, "#part 'test.over_react.g.dart';#"); final errorFix = await expectSingleErrorFix(selection); expect(errorFix.fixes.single.change.selection, isNull); @@ -145,7 +145,7 @@ ${BoilerplateValidatorDiagnosticTest.boilerplateThatRequiresGeneratedPart} '''; Future test_error() async { - final _source = newSource('test.dart', source); + final _source = newSource(source); final allErrors = await getAllErrors(_source); expect( allErrors, @@ -161,7 +161,7 @@ ${BoilerplateValidatorDiagnosticTest.boilerplateThatRequiresGeneratedPart} } Future test_errorFix() async { - var _source = newSource('test.dart', source); + var _source = newSource(source); final selection = createSelection(_source, "#part 'invalid_generated_part_filename.over_react.g.dart';#"); final errorFix = await expectSingleErrorFix(selection); expect(errorFix.fixes.single.change.selection, isNull); diff --git a/tools/analyzer_plugin/test/integration/diagnostics/callback_ref_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/callback_ref_test.dart index c7b1b103a..03d4ab916 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/callback_ref_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/callback_ref_test.dart @@ -200,13 +200,13 @@ final UsesCallbackRef = uiFunction( '''; Future test_blockFnBodyRefAssignment() async { - final source = newSource('test.dart', usageSourceWithinFnComponent); + final source = newSource(usageSourceWithinFnComponent); await expectSingleErrorAt( createSelection(source, CallbackRefDiagnosticTest.selectionToFixBlockFnBodyRefAssignment)); } Future test_blockFnBodyRefAssignmentFix() async { - var source = newSource('test.dart', usageSourceWithinFnComponent); + var source = newSource(usageSourceWithinFnComponent); final errorFix = await expectSingleErrorFix( createSelection(source, CallbackRefDiagnosticTest.selectionToFixBlockFnBodyRefAssignment)); expect(errorFix.fixes.single.change.selection, isNull); @@ -215,12 +215,12 @@ final UsesCallbackRef = uiFunction( } Future test_arrowFnRefAssignmentError() async { - final source = newSource('test.dart', usageSourceWithinFnComponentFixedBlockFnBodyRefAssignment); + final source = newSource(usageSourceWithinFnComponentFixedBlockFnBodyRefAssignment); await expectSingleErrorAt(createSelection(source, CallbackRefDiagnosticTest.selectionToFixArrowFnRefAssignment)); } Future test_arrowFnRefAssignmentErrorFix() async { - var source = newSource('test.dart', usageSourceWithinFnComponentFixedBlockFnBodyRefAssignment); + var source = newSource(usageSourceWithinFnComponentFixedBlockFnBodyRefAssignment); final errorFix = await expectSingleErrorFix( createSelection(source, CallbackRefDiagnosticTest.selectionToFixArrowFnRefAssignment)); expect(errorFix.fixes.single.change.selection, isNull); @@ -235,7 +235,7 @@ class CallbackRefDiagnosticFnComponentTestNoFix extends CallbackRefDiagnosticTes get fixKindUnderTest => null; Future test_tearoffFnRefAssignment() async { - final source = newSource('test.dart', CallbackRefDiagnosticFnComponentTest.usageSourceWithinFnComponent); + final source = newSource(CallbackRefDiagnosticFnComponentTest.usageSourceWithinFnComponent); final selection = createSelection(source, CallbackRefDiagnosticTest.selectionForTearoffRefAssignmentError); await expectSingleErrorAt(selection); // We intentionally do not want the diagnostic to suggest a fix since @@ -316,13 +316,13 @@ class UsesCallbackRefComponent extends UiComponent2 { '''; Future test_blockFnBodyRefAssignment() async { - final source = newSource('test.dart', usageSourceWithinClassComponent); + final source = newSource(usageSourceWithinClassComponent); await expectSingleErrorAt( createSelection(source, CallbackRefDiagnosticTest.selectionToFixBlockFnBodyRefAssignment)); } Future test_blockFnBodyRefAssignmentFix() async { - var source = newSource('test.dart', usageSourceWithinClassComponent); + var source = newSource(usageSourceWithinClassComponent); final errorFix = await expectSingleErrorFix( createSelection(source, CallbackRefDiagnosticTest.selectionToFixBlockFnBodyRefAssignment)); expect(errorFix.fixes.single.change.selection, isNull); @@ -331,12 +331,12 @@ class UsesCallbackRefComponent extends UiComponent2 { } Future test_arrowFnRefAssignmentError() async { - final source = newSource('test.dart', usageSourceWithinClassComponentFixedBlockFnBodyRefAssignment); + final source = newSource(usageSourceWithinClassComponentFixedBlockFnBodyRefAssignment); await expectSingleErrorAt(createSelection(source, CallbackRefDiagnosticTest.selectionToFixArrowFnRefAssignment)); } Future test_arrowFnRefAssignmentErrorFix() async { - var source = newSource('test.dart', usageSourceWithinClassComponentFixedBlockFnBodyRefAssignment); + var source = newSource(usageSourceWithinClassComponentFixedBlockFnBodyRefAssignment); final errorFix = await expectSingleErrorFix( createSelection(source, CallbackRefDiagnosticTest.selectionToFixArrowFnRefAssignment)); expect(errorFix.fixes.single.change.selection, isNull); @@ -351,7 +351,7 @@ class CallbackRefDiagnosticClassComponentTestNoFix extends CallbackRefDiagnostic get fixKindUnderTest => null; Future test_tearoffFnRefAssignment() async { - final source = newSource('test.dart', CallbackRefDiagnosticClassComponentTest.usageSourceWithinClassComponent); + final source = newSource(CallbackRefDiagnosticClassComponentTest.usageSourceWithinClassComponent); final selection = createSelection(source, CallbackRefDiagnosticTest.selectionForTearoffRefAssignmentError); await expectSingleErrorAt(selection); // We intentionally do not want the diagnostic to suggest a fix since diff --git a/tools/analyzer_plugin/test/integration/diagnostics/create_ref_usage_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/create_ref_usage_test.dart index 16979acaa..a63cb975d 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/create_ref_usage_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/create_ref_usage_test.dart @@ -24,7 +24,7 @@ class CreateRefUsageDiagnosticTest extends DiagnosticTestBase { get fixKindUnderTest => CreateRefUsageDiagnostic.fixKind; Future test_noErrorFunction() async { - final source = newSource('test.dart', /*language=dart*/ r''' + final source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; ReactElement someFunction(Map props) { @@ -37,7 +37,7 @@ ReactElement someFunction(Map props) { } Future test_noErrorClassComponent() async { - final source = newSource('test.dart', /*language=dart*/ r''' + final source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; @@ -61,7 +61,7 @@ class FooComponent extends UiComponent2 { } Future test_noErrorUseRef() async { - final source = newSource('test.dart', /*language=dart*/ r''' + final source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart' hide createRef; part 'test.over_react.g.dart'; @@ -81,7 +81,7 @@ final Foo = uiFunction( } Future test_noErrorNotFromOverReact() async { - final source = newSource('test.dart', /*language=dart*/ r''' + final source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart' hide createRef; part 'test.over_react.g.dart'; @@ -103,7 +103,7 @@ final Foo = uiFunction( /// Helper function to expect that the `createRef` usage error exists and verify that /// the fix will replace it with `useRef`. Future _expectErrorAndFix(String input, String expectedOutput) async { - var source = newSource('test.dart', input); + var source = newSource(input); final selection = createSelection(source, "#createRef#"); // Verify error. diff --git a/tools/analyzer_plugin/test/integration/diagnostics/duplicate_prop_cascade_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/duplicate_prop_cascade_test.dart index 3e4de147c..1dc98cdc6 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/duplicate_prop_cascade_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/duplicate_prop_cascade_test.dart @@ -42,7 +42,7 @@ final Custom = uiFunction( '''; Future test_noFalsePositives() async { - final source = newSource('test.dart', /*language=dart*/ ''' + final source = newSource(/*language=dart*/ ''' $customComponentDefinition final customComponentUsage = (Custom() @@ -68,7 +68,7 @@ final domComponentUsage = (Dom.div() } Future test_dupeDomPropFix() async { - var source = newSource('test.dart', /*language=dart*/ r''' + var source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; final domComponentUsage = (Dom.div() @@ -94,7 +94,7 @@ final domComponentUsage = (Dom.div() } Future test_dupeCustomPropFix() async { - var source = newSource('test.dart', /*language=dart*/ ''' + var source = newSource(/*language=dart*/ ''' $customComponentDefinition final componentUsage = (Custom() @@ -125,7 +125,7 @@ final componentUsage = (Custom() // ******************************************************** Future test_dupeDomPropWithOnePrefixedKey() async { - var source = newSource('test.dart', /*language=dart*/ ''' + var source = newSource(/*language=dart*/ ''' $customComponentDefinition final customComponentUsage = (Custom() @@ -150,7 +150,7 @@ final domComponentUsage = (Dom.div() } Future test_dupeDomPropWithMultiplePrefixedKeys() async { - final source = newSource('test.dart', /*language=dart*/ ''' + final source = newSource(/*language=dart*/ ''' $customComponentDefinition final domComponentUsage = (Dom.div() @@ -179,7 +179,7 @@ final customComponentUsage = (Custom() } Future test_dupeDomPropWithAllPrefixedKeys() async { - final source = newSource('test.dart', /*language=dart*/ ''' + final source = newSource(/*language=dart*/ ''' $customComponentDefinition final domComponentUsage = (Dom.div() @@ -208,7 +208,7 @@ final customComponentUsage = (Custom() } Future test_dupeDomPropWithNoPrefixedKeys() async { - final source = newSource('test.dart', /*language=dart*/ ''' + final source = newSource(/*language=dart*/ ''' $customComponentDefinition final domComponentUsage = (Dom.div() @@ -239,7 +239,7 @@ final customComponentUsage = (Custom() // We currently don't support flagging dupes when a key is added using `..addProps(domProps()..key)`, but // we run a test here to verify that no lint is shown, and that the plugin doesn't crash. Future test_noDupeAndNoPluginCrashWhenUsingAddPropsDomPropsAndPrefixedKey() async { - final source = newSource('test.dart', /*language=dart*/ ''' + final source = newSource(/*language=dart*/ ''' $customComponentDefinition final domComponentUsage = (Dom.div() @@ -270,7 +270,7 @@ final customComponentUsagePrefixed = (Custom() // ******************************************************** Future test_dupeAriaPropWithAllPrefixedKeys() async { - final source = newSource('test.dart', /*language=dart*/ ''' + final source = newSource(/*language=dart*/ ''' $customComponentDefinition final domComponentUsage = (Dom.div() @@ -297,7 +297,7 @@ final customComponentUsage = (Custom() // We currently don't support flagging dupes when a key is added using `..addProps(ariaProps()..key)`, but // we run a test here to verify that no lint is shown, and that the plugin doesn't crash. Future test_noDupeAndNoPluginCrashWhenUsingAddPropsAriaProps() async { - final source = newSource('test.dart', /*language=dart*/ ''' + final source = newSource(/*language=dart*/ ''' $customComponentDefinition final domComponentUsage = (Dom.div() @@ -320,7 +320,7 @@ final customComponentUsage = (Custom() // ******************************************************** Future test_dupeCustomProp() async { - final source = newSource('test.dart', /*language=dart*/ ''' + final source = newSource(/*language=dart*/ ''' $customComponentDefinition final usage = (Custom() diff --git a/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart index 7e6b1b37a..54ac6ba78 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart @@ -25,7 +25,7 @@ import 'exhaustive_deps_test_cases.dart' as test_cases; void main() { group('ExhaustiveDeps', () { const preamble = r''' -// ignore_for_file: unused_import +// ignore_for_file: unused_import, unused_local_variable import 'dart:html'; @@ -151,7 +151,7 @@ class ObjectWithWritableField { printOnFailure('Test case source (before adding preamble): ```\n${testCase.code}\n```'); final testBase = await setUpTestBase(testCase); - final source = testBase.newSource('test.dart', preamble + testCase.code); + final source = testBase.newSource(preamble + testCase.code); await testBase.expectNoErrors(source, errorFilter: errorFilter); }); }); @@ -170,7 +170,7 @@ class ObjectWithWritableField { final expectedErrors = testCase.errors; expect(expectedErrors, isNotEmpty); - final source = testBase.newSource('test.dart', preamble + testCase.code); + final source = testBase.newSource(preamble + testCase.code); final errors = await testBase.getAllErrors(source, includeOtherCodes: true, errorFilter: errorFilter); expect(errors.dartErrors, isEmpty, reason: 'Expected there to be no errors coming from the analyzer and not the plugin.' @@ -310,7 +310,7 @@ class ObjectWithWritableField { // This means that later iterations in the loop will have unexpected changes, and also their // fixes won't always end up in the right places since their offsets are stale. // Revert the changes to the file so that other iterations can test their fixes without interference. - testBase.resourceProvider.updateFile(p.normalize(source.uri.toFilePath()), sourceBeforeFixes); + testBase.modifyFile(p.normalize(source.uri.toFilePath()), sourceBeforeFixes); } } } diff --git a/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test_cases.dart b/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test_cases.dart index f5d653c22..f59862863 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test_cases.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test_cases.dart @@ -620,8 +620,8 @@ final Map>> tests = { }, { 'code': r''' - StateHook useFunnyState(T initialState) {} - ReducerHook useFunnyReducer(dynamic reducer, T initialState) {} + StateHook useFunnyState(T initialState) => useState(initialState); + ReducerHook useFunnyReducer(dynamic reducer, T initialState) => useReducer(reducer, initialState); dynamic useSomeOtherRefyThing() => null; final MyComponent = uiFunction((props) { var maybeRef2 = props.maybeRef2; @@ -687,8 +687,8 @@ final Map>> tests = { }, { 'code': r''' - StateHook useFunnyState(T initialState) {} - ReducerHook useFunnyReducer(dynamic reducer, T initialState) {} + StateHook useFunnyState(T initialState) => useState(initialState); + ReducerHook useFunnyReducer(dynamic reducer, T initialState) => useReducer(reducer, initialState); dynamic useSomeOtherRefyThing() => null; final MyComponent = uiFunction((props) { var maybeRef2 = props.maybeRef2; @@ -1382,8 +1382,8 @@ final Map>> tests = { }, { 'code': r''' - final MyComponent = uiFunction((_) { - final foo = true ? "fine" : "also fine"; + final MyComponent = uiFunction((props) { + final foo = props.isEditMode ? "fine" : "also fine"; return useMemo(() => foo, [foo]); }, null); ''', @@ -2351,7 +2351,7 @@ final Map>> tests = { final local = someFunc(); useEffect(() { print(local); - // ignore: undefined_identifier + // ignore: undefined_identifier, not_iterable_spread }, [local, ...dependencies]); }, null); ''', @@ -5737,6 +5737,7 @@ final Map>> tests = { }, { 'code': r''' + // ignore_for_file: dead_code final MyComponent = uiFunction((props) { final handleNext1 = () { print('hello'); @@ -5762,11 +5763,12 @@ final Map>> tests = { 'errors': [ { 'message': - 'The \'handleNext1\' function makes the dependencies of useEffect Hook (at line 11) change on every render. To fix this, wrap the definition of \'handleNext1\' in its own useCallback() Hook.', + 'The \'handleNext1\' function makes the dependencies of useEffect Hook (at line 12) change on every render. To fix this, wrap the definition of \'handleNext1\' in its own useCallback() Hook.', 'suggestions': [ { 'desc': 'Wrap the definition of \'handleNext1\' in its own useCallback() Hook.', 'output': r''' + // ignore_for_file: dead_code final MyComponent = uiFunction((props) { final handleNext1 = useCallback(() { print('hello'); @@ -5789,11 +5791,12 @@ final Map>> tests = { }, { 'message': - 'The \'handleNext1\' function makes the dependencies of useEffect Hook (at line 15) change on every render. To fix this, wrap the definition of \'handleNext1\' in its own useCallback() Hook.', + 'The \'handleNext1\' function makes the dependencies of useEffect Hook (at line 16) change on every render. To fix this, wrap the definition of \'handleNext1\' in its own useCallback() Hook.', 'suggestions': [ { 'desc': 'Wrap the definition of \'handleNext1\' in its own useCallback() Hook.', 'output': r''' + // ignore_for_file: dead_code final MyComponent = uiFunction((props) { final handleNext1 = useCallback(() { print('hello'); @@ -5816,11 +5819,12 @@ final Map>> tests = { }, { 'message': - 'The \'handleNext2\' function makes the dependencies of useEffect Hook (at line 11) change on every render. To fix this, wrap the definition of \'handleNext2\' in its own useCallback() Hook.', + 'The \'handleNext2\' function makes the dependencies of useEffect Hook (at line 12) change on every render. To fix this, wrap the definition of \'handleNext2\' in its own useCallback() Hook.', 'suggestions': [ { 'desc': 'Wrap the definition of \'handleNext2\' in its own useCallback() Hook.', 'output': r''' + // ignore_for_file: dead_code final MyComponent = uiFunction((props) { final handleNext1 = () { print('hello'); @@ -5843,11 +5847,12 @@ final Map>> tests = { }, { 'message': - 'The \'handleNext2\' function makes the dependencies of useEffect Hook (at line 15) change on every render. To fix this, wrap the definition of \'handleNext2\' in its own useCallback() Hook.', + 'The \'handleNext2\' function makes the dependencies of useEffect Hook (at line 16) change on every render. To fix this, wrap the definition of \'handleNext2\' in its own useCallback() Hook.', 'suggestions': [ { 'desc': 'Wrap the definition of \'handleNext2\' in its own useCallback() Hook.', 'output': r''' + // ignore_for_file: dead_code final MyComponent = uiFunction((props) { final handleNext1 = () { print('hello'); @@ -7030,8 +7035,8 @@ final Map>> tests = { /* (1 case previously here involving class expressions was removed, since there is no equivalent in Dart) */ { 'code': r''' - final Component = uiFunction((_) { - final foo = true ? {} : "fine"; + final Component = uiFunction((props) { + final foo = props.isEditMode ? {} : "fine"; useMemo(() => foo, [foo]); }, null); ''', diff --git a/tools/analyzer_plugin/test/integration/diagnostics/forward_only_dom_props_to_dom_builders_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/forward_only_dom_props_to_dom_builders_test.dart index 546e7ba39..91ba320ea 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/forward_only_dom_props_to_dom_builders_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/forward_only_dom_props_to_dom_builders_test.dart @@ -59,7 +59,7 @@ final DomWrapperFn = uiFunction( '''; Future test_classComponentUsageErrorAndFix() async { - var source = newSource('test.dart', usageSourceWithinClassComponent(fixed: false)); + var source = newSource(usageSourceWithinClassComponent(fixed: false)); final errorFix = await expectSingleErrorFix(createSelection(source, "..modifyProps(#addUnconsumedProps#)")); expect(errorFix.fixes.single.change.selection, isNull); source = applyErrorFixes(errorFix, source); @@ -67,7 +67,7 @@ final DomWrapperFn = uiFunction( } Future test_fnComponentUsageErrorAndFix() async { - var source = newSource('test.dart', usageSourceWithinFnComponent(fixed: false)); + var source = newSource(usageSourceWithinFnComponent(fixed: false)); final errorFix = await expectSingleErrorFix(createSelection(source, "..#addUnconsumedProps#(props, const [])")); expect(errorFix.fixes.single.change.selection, isNull); source = applyErrorFixes(errorFix, source); diff --git a/tools/analyzer_plugin/test/integration/diagnostics/non_defaulted_prop_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/non_defaulted_prop_test.dart index fd25a9aef..edd1f6bfe 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/non_defaulted_prop_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/non_defaulted_prop_test.dart @@ -24,7 +24,7 @@ class NonDefaultedPropDiagnosticTest extends DiagnosticTestBase { get fixKindUnderTest => NonDefaultedPropDiagnostic.fixKind; Future test_noErrorDefaultUsed() async { - final source = newSource('test.dart', /*language=dart*/ r''' + final source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; @@ -49,7 +49,7 @@ final Foo = uiFunction( } Future test_noErrorSameNameButNotDefault() async { - final source = newSource('test.dart', /*language=dart*/ r''' + final source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; @@ -61,6 +61,7 @@ mixin FooProps on UiProps { final Foo = uiFunction( (props) { + // ignore: unused_local_variable final content = 'abc'; return (Dom.div() @@ -92,7 +93,7 @@ final Foo = uiFunction( _\$FooConfig, // ignore: undefined_identifier ); '''; - var source = newSource('test.dart', contents('props.content')); + var source = newSource(contents('props.content')); final selection = createSelection(source, "(#props.content#)"); // Verify error. @@ -124,7 +125,7 @@ final Foo = uiFunction( _\$FooConfig, // ignore: undefined_identifier ); '''; - var source = newSource('test.dart', contents('props.content')); + var source = newSource(contents('props.content')); final selection = createSelection(source, "(#props.content#)"); // Verify error. @@ -138,7 +139,7 @@ final Foo = uiFunction( } Future test_multipleErrorsAndFixes() async { - var source = newSource('test.dart', /*language=dart*/ r''' + var source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; @@ -152,11 +153,14 @@ mixin FooProps on UiProps { final Foo = uiFunction( (props) { + // ignore: unused_local_variable final fooId = props.fooId ?? 'abc'; + // ignore: unused_local_variable final name = props.name ?? 'Jane'; // Some other logic void helperFunction() => print('hello'); + // ignore: unused_local_variable const a = 25; // Some other logic @@ -202,11 +206,14 @@ mixin FooProps on UiProps { final Foo = uiFunction( (props) { + // ignore: unused_local_variable final fooId = props.fooId ?? 'abc'; + // ignore: unused_local_variable final name = props.name ?? 'Jane'; // Some other logic void helperFunction() => print('hello'); + // ignore: unused_local_variable const a = 25; // Some other logic diff --git a/tools/analyzer_plugin/test/integration/stubs.dart b/tools/analyzer_plugin/test/integration/stubs.dart index 8b15333b7..6af6f0050 100644 --- a/tools/analyzer_plugin/test/integration/stubs.dart +++ b/tools/analyzer_plugin/test/integration/stubs.dart @@ -1,9 +1,11 @@ -import 'package:analyzer/file_system/file_system.dart'; -import 'package:analyzer/src/dart/analysis/driver.dart'; +import 'package:analyzer/dart/analysis/analysis_context.dart'; +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/file_system/overlay_file_system.dart'; import 'package:analyzer_plugin/channel/channel.dart'; import 'package:analyzer_plugin/plugin/plugin.dart'; import 'package:analyzer_plugin/protocol/protocol.dart'; -import 'package:analyzer_plugin/protocol/protocol_generated.dart'; +import 'package:over_react_analyzer_plugin/src/async_plugin_apis/assist.dart'; +import 'package:over_react_analyzer_plugin/src/async_plugin_apis/diagnostic.dart'; import 'package:over_react_analyzer_plugin/src/plugin.dart'; import 'test_bases/assist_test_base.dart'; @@ -20,19 +22,173 @@ class StubChannel implements PluginCommunicationChannel { dynamic noSuchMethod(Invocation invocation) {} } +/// A stubbed ServerPlugin base class to use for testing, so that we can ensure its AnalysisContextCollection isn't +/// being used instead of the one we've set up in tests. +class StubServerPlugin implements ServerPlugin { + @override + get channel => throw UnimplementedError(); + + @override + get priorityPaths => throw UnimplementedError(); + + @override + set priorityPaths(value) => throw UnimplementedError(); + + @override + afterNewContextCollection({required contextCollection}) => throw UnimplementedError(); + + @override + analyzeFile({required analysisContext, required path}) => throw UnimplementedError(); + + @override + analyzeFiles({required analysisContext, required paths}) => throw UnimplementedError(); + + @override + beforeContextCollectionDispose({required contextCollection}) => throw UnimplementedError(); + + @override + get contactInfo => throw UnimplementedError(); + + @override + contentChanged(paths) => throw UnimplementedError(); + + @override + createByteStore() => throw UnimplementedError(); + + @override + get fileGlobsToAnalyze => throw UnimplementedError(); + + @override + flushAnalysisState({elementModels = true}) => throw UnimplementedError(); + + @override + getResolvedUnitResult(path) => throw UnimplementedError(); + + @override + handleAffectedFiles({required analysisContext, required paths}) => throw UnimplementedError(); + + @override + handleAnalysisGetNavigation(parameters) => throw UnimplementedError(); + + @override + handleAnalysisHandleWatchEvents(parameters) => throw UnimplementedError(); + + @override + handleAnalysisSetContextRoots(parameters) => throw UnimplementedError(); + + @override + handleAnalysisSetPriorityFiles(parameters) => throw UnimplementedError(); + + @override + handleAnalysisSetSubscriptions(parameters) => throw UnimplementedError(); + + @override + handleAnalysisUpdateContent(parameters) => throw UnimplementedError(); + + @override + handleCompletionGetSuggestions(parameters) => throw UnimplementedError(); + + @override + handleEditGetAssists(parameters) => throw UnimplementedError(); + + @override + handleEditGetAvailableRefactorings(parameters) => throw UnimplementedError(); + + @override + handleEditGetFixes(parameters) => throw UnimplementedError(); + + @override + handleEditGetRefactoring(parameters) => throw UnimplementedError(); + + @override + handleKytheGetKytheEntries(parameters) => throw UnimplementedError(); + + @override + handlePluginShutdown(parameters) => throw UnimplementedError(); + + @override + handlePluginVersionCheck(parameters) => throw UnimplementedError(); + + @override + isCompatibleWith(serverVersion) => throw UnimplementedError(); + + @override + get name => throw UnimplementedError(); + + @override + void onDone() {} + + @override + void onError(exception, stackTrace) {} + + @override + get resourceProvider => throw UnimplementedError(); + + @override + sendFoldingNotification(path) => throw UnimplementedError(); + + @override + sendHighlightsNotification(path) => throw UnimplementedError(); + + @override + sendNavigationNotification(path) => throw UnimplementedError(); + + @override + void sendNotificationsForFile(path) {} + + @override + void sendNotificationsForSubscriptions(subscriptions) {} + + @override + sendOccurrencesNotification(path) => throw UnimplementedError(); + + @override + sendOutlineNotification(path) => throw UnimplementedError(); + + @override + void start(channel) => throw UnimplementedError(); + + @override + get subscriptionManager => throw UnimplementedError(); + + @override + get version => throw UnimplementedError(); +} + /// A concrete [ServerPlugin] implementation designed for use in testing a -/// single assist or diagnostic contributor against a real-ish [AnalysisDriver]. +/// single assist or diagnostic contributor against real-ish AnalysisContexts. /// /// Tests should not use, extend, or implement this class directly. Instead, /// extend the contributor-specific test base class, like [AssistTestBase]. -class PluginForTest extends OverReactAnalyzerPluginBase { - final AnalysisDriver testDriver; +class PluginForTest extends StubServerPlugin + with + DiagnosticMixin, + AsyncAssistsMixin, + AsyncDartAssistsMixin, + OverReactAnalyzerPluginBase { + @override + late PluginCommunicationChannel channel; + + @override + late OverlayResourceProvider resourceProvider; - PluginForTest(this.testDriver, ResourceProvider resourceProvider) : super(resourceProvider); + late Future Function(String path)? handleGetResolvedUnitResult; @override - String get name => 'over_react (for test)'; + Future getResolvedUnitResult(String path) => + handleGetResolvedUnitResult?.call(path) ?? (throw UnimplementedError()); + + @override + Future analyzeFile({required AnalysisContext analysisContext, required String path}) => + throw UnimplementedError(); + + @override + Future analyzeFiles({required AnalysisContext analysisContext, required List paths}) => + throw UnimplementedError(); @override - AnalysisDriverGeneric createAnalysisDriver(ContextRoot contextRoot) => testDriver; + Future contentChanged(List paths) => throw UnimplementedError(); + + @override + String get name => 'over_react (for test)'; } diff --git a/tools/analyzer_plugin/test/integration/test_bases/analysis_driver_test_base.dart b/tools/analyzer_plugin/test/integration/test_bases/analysis_driver_test_base.dart index 2ac904583..c183cb91f 100644 --- a/tools/analyzer_plugin/test/integration/test_bases/analysis_driver_test_base.dart +++ b/tools/analyzer_plugin/test/integration/test_bases/analysis_driver_test_base.dart @@ -1,25 +1,12 @@ import 'dart:io'; -import 'package:analyzer/dart/analysis/features.dart'; -import 'package:analyzer/file_system/file_system.dart'; -import 'package:analyzer/file_system/memory_file_system.dart'; -import 'package:analyzer/file_system/physical_file_system.dart'; -import 'package:analyzer/src/context/packages.dart'; -import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart'; -import 'package:analyzer/src/dart/analysis/byte_store.dart'; -import 'package:analyzer/src/dart/analysis/driver.dart' show AnalysisDriver, AnalysisDriverScheduler; -import 'package:analyzer/src/dart/analysis/performance_logger.dart'; -import 'package:analyzer/src/generated/engine.dart'; -import 'package:analyzer/src/generated/source.dart'; -import 'package:analyzer/src/source/package_map_resolver.dart'; -import 'package:analyzer/src/test_utilities/mock_sdk.dart'; -import 'package:collection/collection.dart' show IterableExtension; +import 'package:analyzer/file_system/file_system.dart' show ResourceProvider; +// ignore: implementation_imports +import 'package:analyzer/src/generated/source.dart' show Source; import 'package:meta/meta.dart'; -import 'package:package_config/package_config.dart'; import 'package:path/path.dart' as p; -import 'package:pub_semver/pub_semver.dart'; -import 'package:test/test.dart'; +import '../../util/shared_analysis_context.dart'; import 'assist_test_base.dart'; /// Test base that handles constructing an analysis server plugin designed for @@ -31,134 +18,71 @@ import 'assist_test_base.dart'; /// extend a test base for the specific type of contributor, like /// [AssistTestBase]. abstract class AnalysisDriverTestBase { - /// The list of packages that are dependencies of this - /// `over_react_analyzer_plugin` package and are required for contributor - /// integration test sources. - /// - /// Any package in this list will be copied from the physical pub-cache into - /// the [MemoryResourceProvider] and the [AnalysisDriver] will be configured - /// with a [PackageMapUriResolver] that knows where these packages are so that - /// test sources can import from them. - static const _realPackagesToCopyIntoResourceProvider = [ - // FIXME add all transitive deps of over_react (or do that in the package config logic) - 'over_react', - 'react', - 'collection', - ]; - - /// The analysis driver that computes analysis results for the test sources - /// created via [newSource] that are then used by the plugin contributors - /// under test. - AnalysisDriver get analysisDriver => _analysisDriver!; - AnalysisDriver? _analysisDriver; - /// Provider of all resources created during and needed for tests. - MemoryResourceProvider get resourceProvider => _resourceProvider!; - MemoryResourceProvider? _resourceProvider; - - /// Absolute path to the in-memory folder within which all test sources are - /// created. - String get testPath => _testPath!; - String? _testPath; + ResourceProvider get resourceProvider => _resourceProvider!; + ResourceProvider? _resourceProvider; /// Contents of analysis_options.yaml file. /// /// This is optional; if `null`, there will be no analysis_options.yaml file. String? get analysisOptionsYamlContents => null; - /// Creates and returns a new source file at [path] with optional file - /// [contents]. + /// Creates and returns a new source file with the provided [contents] + /// within [sharedContext]. + /// + /// Creates the file at [path] if it's non-null, and otherwise uses a new, unique path. /// - /// [path] must be relative; the returned source will be created within - /// [testPath]. - Source newSource(String path, [String? contents]) { - expect(p.isAbsolute(path), isFalse, reason: 'newSource() must be called with a relative path'); - final absolutePath = p.join(testPath, path); - final file = resourceProvider.newFile(absolutePath, contents ?? ''); - return file.createSource(); + /// If provided, [path] must be relative, and also must be unique so that it + /// doesn't conflict with paths created by other tests using the same [sharedContext] + Source newSource(String contents, {String? path}) { + if (path != null && p.isAbsolute(path)) { + throw ArgumentError.value(path, 'path', 'must be a relative path'); + } + final testFilePath = sharedContext.createTestFile(contents, filename: path); + return resourceProvider.getFile(testFilePath).createSource(); } - /// Returns the absolute path for [source]. - String sourcePath(Source source) => resourceProvider.convertPath(source.uri.path); - - @mustCallSuper - Future setUp() async { - // Based on https://github.com/dart-lang/angular/blob/832c8df2c54415e8b6e68886cd3d8c293ef9874c/angular_analyzer_plugin/test/analyzer_base.dart#L62-L89 + void modifyFile(String path, String contents) { + resourceProvider.getFile(path).writeAsStringSync(contents); + } - _resourceProvider = MemoryResourceProvider(); + /// Returns the absolute path for [source]. + String sourcePath(Source source) => source.uri.toFilePath(); - final sdk = MockSdk(resourceProvider: resourceProvider); - final packageMap = { - for (final packageName in _realPackagesToCopyIntoResourceProvider) - packageName: [await _loadRealPackage(packageName, resourceProvider)], - }; + SharedAnalysisContext? _sharedContext; - // Setup a testing directory. All calls to [newSource] will create files - // within this directory. - _testPath = resourceProvider.newFolder('/test').path; + SharedAnalysisContext get sharedContext => _sharedContext!; - // Add a analysis_options.yaml file to the [resourceProvider] if the contents are specified. - // If this isn't set up here, `AnalysisContext.optionsFile` will be null even if the file is added later. - final contents = analysisOptionsYamlContents; - if (contents != null) { - final absolutePath = p.join(testPath, 'analysis_options.yaml'); - resourceProvider.newFile(absolutePath, contents); + @mustCallSuper + Future setUp() async { + // TODO once we're running tests optionally on null-safe code? Or maybe language version comments in source files instead? + // final defaultContext = isNullSafe ? SharedAnalysisContext.overReactNullSafe : SharedAnalysisContext.overReactNonNullSafe; + final defaultContext = SharedAnalysisContext.overReact; + if (analysisOptionsYamlContents == null) { + _sharedContext = defaultContext; + } else { + // Create a copy since modifying the original context would interfere with other test. + // + // Make sure the copy is on the same level as the original so that we don't have to mess with relative paths + // in the pubspec (e.g., to the over_react dependency). + // Original: test/test_fixtures/over_react_project/ + // Copy: test/temporary_test_fixtures// + final parentDir = p.canonicalize(p.join(defaultContext.contextRootPath, '../../temporary_test_fixtures')); + _sharedContext = SharedAnalysisContext.createTemporaryCopy(defaultContext, parentDir); + File(p.join(sharedContext.contextRootPath, 'analysis_options.yaml')) + .writeAsStringSync(analysisOptionsYamlContents!); } - // Disable null safety - final languageVersion = Version.parse('2.7.0'); - final languageConstraint = VersionConstraint.compatibleWith(languageVersion); - final featureSet = FeatureSet.fromEnableFlags2(sdkLanguageVersion: languageVersion, flags: []); - - final logger = PerformanceLog(StringBuffer()); - final analysisScheduler = AnalysisDriverScheduler(logger)..start(); - _analysisDriver = AnalysisDriver.tmp1( - scheduler: analysisScheduler, - logger: logger, - resourceProvider: resourceProvider, - byteStore: MemoryByteStore(), - sourceFactory: SourceFactory([ - DartUriResolver(sdk), - PackageMapUriResolver(resourceProvider, packageMap), - ResourceUriResolver(resourceProvider), - ]), - analysisOptions: AnalysisOptionsImpl() - ..sdkVersionConstraint = languageConstraint - ..contextFeatures = featureSet - ..nonPackageLanguageVersion = languageVersion - ..nonPackageFeatureSet = featureSet, - packages: Packages.empty, - ); - - final contextCollection = AnalysisContextCollectionImpl( - includedPaths: [testPath], resourceProvider: resourceProvider, sdkPath: resourceProvider.convertPath('/sdk')); - _analysisDriver!.analysisContext = contextCollection.contextFor(testPath); + await sharedContext.warmUpAnalysis(); + _resourceProvider = sharedContext.collection.contexts.single.currentSession.resourceProvider; } @mustCallSuper void tearDown() { - _analysisDriver?.dispose(); - _analysisDriver = null; _resourceProvider = null; - _testPath = null; - } - - /// Returns the parsed package config from the - /// `.dart_tool/package_config.json` file for the - /// `over_react_analyzer_plugin` package. - static Future _getRootPackageConfig() async => - (_realPackageConfig ??= await findPackageConfig(Directory.current, recurse: false))!; - static PackageConfig? _realPackageConfig; - - /// Finds the source of [packageName] and copies the physical resources into - /// the [memory] resoure provider at the `/packages/$packageName` location. - static Future _loadRealPackage(String packageName, MemoryResourceProvider memory) async { - final package = (await _getRootPackageConfig()).packages.firstWhereOrNull((pkg) => pkg.name == packageName)!; - expect(package, isNotNull, - reason: 'Could not load "$packageName" into MemoryResourceProvider because it is not a dependency.'); - final physicalRoot = p.normalize(package.packageUriRoot.toFilePath()); - final memoryRoot = p.normalize('/packages/$packageName'); - PhysicalResourceProvider.INSTANCE.getFolder(physicalRoot).copyTo(memory.getFolder(memoryRoot)); - return memory.getFolder(p.join(memoryRoot, 'lib')); + if (_sharedContext != null && sharedContext.isTemporaryCopy) { + Directory(sharedContext.contextRootPath).deleteSync(recursive: true); + } + _sharedContext = null; } } diff --git a/tools/analyzer_plugin/test/integration/test_bases/assist_test_base.dart b/tools/analyzer_plugin/test/integration/test_bases/assist_test_base.dart index a637fc75b..61a1391ee 100644 --- a/tools/analyzer_plugin/test/integration/test_bases/assist_test_base.dart +++ b/tools/analyzer_plugin/test/integration/test_bases/assist_test_base.dart @@ -26,7 +26,7 @@ abstract class AssistTestBase extends ServerPluginContributorTestBase { /// [selectionTargets] does not produce a single assist matching /// [assistKindUnderTest]. Future expectAllSelectionsProduceSingleAssist(String sourceContents, List selectionTargets) async { - final source = newSource('test.dart', sourceContents); + final source = newSource(sourceContents); for (final target in selectionTargets) { final selection = createSelection(source, target); final assists = await _getAllAssists(selection); @@ -56,7 +56,7 @@ abstract class AssistTestBase extends ServerPluginContributorTestBase { /// Returns all assists produced at [selection]. Future> _getAllAssists(SourceSelection selection) async { final parameters = EditGetAssistsParams( - resourceProvider.convertPath(selection.source.uri.path), selection.offset, selection.length); + selection.source.uri.toFilePath(), selection.offset, selection.length); return (await testPlugin.handleEditGetAssists(parameters)) .assists .where((psc) => psc.change.id == assistKindUnderTest.id) diff --git a/tools/analyzer_plugin/test/integration/test_bases/diagnostic_test_base.dart b/tools/analyzer_plugin/test/integration/test_bases/diagnostic_test_base.dart index 3c7593fb3..33647808f 100644 --- a/tools/analyzer_plugin/test/integration/test_bases/diagnostic_test_base.dart +++ b/tools/analyzer_plugin/test/integration/test_bases/diagnostic_test_base.dart @@ -8,6 +8,7 @@ import 'package:test/test.dart'; import '../matchers.dart'; import 'server_plugin_contributor_test_base.dart'; +// ignore: implementation_imports export 'package:analyzer/src/generated/source.dart' show Source; /// Test base for integration tests that exercise a single diagnostic @@ -74,7 +75,7 @@ abstract class DiagnosticTestBase extends ServerPluginContributorTestBase { /// Fails the test if any selection of [sourceContents] from /// [selectionTargets] does not produce a single assist. Future expectAllSelectionsProduceAtLeastOneError(String sourceContents, List selectionTargets) async { - final source = newSource('test.dart', sourceContents); + final source = newSource(sourceContents); for (final target in selectionTargets) { final selection = createSelection(source, target); await expectSingleErrorFix(selection); diff --git a/tools/analyzer_plugin/test/integration/test_bases/server_plugin_contributor_test_base.dart b/tools/analyzer_plugin/test/integration/test_bases/server_plugin_contributor_test_base.dart index aa73bd29d..9922a39d9 100644 --- a/tools/analyzer_plugin/test/integration/test_bases/server_plugin_contributor_test_base.dart +++ b/tools/analyzer_plugin/test/integration/test_bases/server_plugin_contributor_test_base.dart @@ -1,6 +1,8 @@ -import 'package:analyzer/src/generated/source.dart'; +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/file_system/overlay_file_system.dart'; +// ignore: implementation_imports +import 'package:analyzer/src/generated/source.dart' show Source, SourceRange; import 'package:analyzer_plugin/protocol/protocol_common.dart'; -import 'package:analyzer_plugin/protocol/protocol_generated.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; @@ -74,7 +76,8 @@ abstract class ServerPluginContributorTestBase extends AnalysisDriverTestBase { resourceProvider.getFile(path).readAsStringSync(), [for (final fileEdit in applicableFileEdits) ...fileEdit.edits], ); - final file = resourceProvider.updateFile(path, updated); + modifyFile(path, updated); + final file = resourceProvider.getFile(path); return file.createSource(); } @@ -149,18 +152,21 @@ abstract class ServerPluginContributorTestBase extends AnalysisDriverTestBase { await super.setUp(); _channel = StubChannel(); - _plugin = PluginForTest(analysisDriver, resourceProvider)..start(_channel!); - - // ignore: missing_required_param - final contextRoot = ContextRoot(testPath, []); - await testPlugin.handleAnalysisSetContextRoots(AnalysisSetContextRootsParams([contextRoot])); + _plugin = PluginForTest() + ..channel = _channel! + ..resourceProvider = OverlayResourceProvider(sharedContext.collection.contexts.single.currentSession.resourceProvider) + ..handleGetResolvedUnitResult = (path) async { + final result = await sharedContext.collection.contextFor(path).currentSession.getResolvedUnit(path); + if (result is ResolvedUnitResult) return result; + + throw Exception('Could not resolve path $path: $result'); + }; } @override @mustCallSuper Future tearDown() async { expectNoPluginErrors(); - await _plugin?.handlePluginShutdown(PluginShutdownParams()); _channel = null; _plugin = null; super.tearDown(); diff --git a/tools/analyzer_plugin/test/test_fixtures/over_react_project/README.md b/tools/analyzer_plugin/test/test_fixtures/over_react_project/README.md new file mode 100644 index 000000000..e8e9cb721 --- /dev/null +++ b/tools/analyzer_plugin/test/test_fixtures/over_react_project/README.md @@ -0,0 +1,3 @@ +A package that depends on over_react, and can be used as a context root for tests that require a resolved analysis context with access to over_react APIs. + +To use, see `SharedAnalysisContext.over_react`. diff --git a/tools/analyzer_plugin/test/test_fixtures/over_react_project/analysis_options.yaml b/tools/analyzer_plugin/test/test_fixtures/over_react_project/analysis_options.yaml new file mode 100644 index 000000000..190747144 --- /dev/null +++ b/tools/analyzer_plugin/test/test_fixtures/over_react_project/analysis_options.yaml @@ -0,0 +1 @@ +# Without this file, this project will inherit analysis_options.yaml from parent directories. diff --git a/tools/analyzer_plugin/test/test_fixtures/over_react_project/lib/analysis_warmup.dart b/tools/analyzer_plugin/test/test_fixtures/over_react_project/lib/analysis_warmup.dart new file mode 100644 index 000000000..83a58ae5c --- /dev/null +++ b/tools/analyzer_plugin/test/test_fixtures/over_react_project/lib/analysis_warmup.dart @@ -0,0 +1,8 @@ +// This file imports WSD and over_react and can be analyzed to warm up an +// analysis context during testing. + +import 'package:over_react/over_react.dart'; + +main() { + Dom.div()(); +} diff --git a/tools/analyzer_plugin/test/test_fixtures/over_react_project/pubspec.yaml b/tools/analyzer_plugin/test/test_fixtures/over_react_project/pubspec.yaml new file mode 100644 index 000000000..90d438b1a --- /dev/null +++ b/tools/analyzer_plugin/test/test_fixtures/over_react_project/pubspec.yaml @@ -0,0 +1,6 @@ +name: over_react_project +environment: + sdk: '>=2.11.0 <3.0.0' +dependencies: + over_react: + path: ../../../../.. diff --git a/tools/analyzer_plugin/test/test_util.dart b/tools/analyzer_plugin/test/test_util.dart index e3b1bb386..6abcf2d45 100644 --- a/tools/analyzer_plugin/test/test_util.dart +++ b/tools/analyzer_plugin/test/test_util.dart @@ -1,19 +1,13 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/analysis/utilities.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/diagnostic/diagnostic.dart'; -import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart' show DriverBasedAnalysisContext; -import 'package:analyzer/file_system/overlay_file_system.dart'; -import 'package:analyzer/file_system/physical_file_system.dart'; import 'package:over_react_analyzer_plugin/src/component_usage.dart'; import 'package:over_react_analyzer_plugin/src/error_filtering.dart'; import 'package:over_react_analyzer_plugin/src/util/ast_util.dart'; import 'package:over_react_analyzer_plugin/src/util/ignore_info.dart'; -import 'package:path/path.dart' as p; + +import 'util/shared_analysis_context.dart'; /// Parses [dartSource] and returns the unresolved AST, throwing if there are any syntax errors. CompilationUnit parseAndGetUnit(String dartSource) { @@ -70,142 +64,23 @@ FluentComponentUsage parseAndGetComponentUsage(String dartSource) { } /// Parses [dartSource] and returns the resolved AST, throwing if there are any static analysis errors. -Future parseAndGetResolvedUnit(String dartSource, {String path = 'dartSource.dart'}) async { - final results = await parseAndGetResolvedUnits({path: dartSource}); - return results.values.single; -} - -// Reuse an analysis context across multiple calls, -// which is much faster than spinning up a new one each time -// when resolving files. -var _hasInitializedSharedCollection = false; -late AnalysisContextCollection _sharedCollection; -late OverlayResourceProvider _sharedResourceProvider; -var _dartSourcePathsAddedInPreviousCall = []; - -/// Parses a collection of Dart sources and returns the resolved ASTs, -/// throwing if there are any static analysis errors. -/// -/// Useful when you need multiple files that reference each other. -/// -/// Example: -/// ```dart -/// final results = await parseAndGetResolvedUnits({ -/// 'foo.dart': r''' -/// class Foo {} -/// ''', -/// -/// 'bar.dart': r''' -/// import 'foo.dart'; -/// class Bar extends Foo {} -/// ''', -/// }); -/// -/// final barResolveResult = results['bar.dart']; -/// final barElement = barResolveResult.unit.declaredElement.getType('Bar'); -/// print(barElement.allSupertypes); // [Foo, Object] -/// ``` -Future> parseAndGetResolvedUnits(Map dartSourcesByPath) async { - // Must be absolute. - const pathPrefix = '/fake_in_memory_path'; - - String transformPath(String path) => p.join(pathPrefix, path); - - final dartSourcesByAbsolutePath = dartSourcesByPath.map((key, value) => MapEntry(transformPath(key), value)); - - if (!_hasInitializedSharedCollection) { - _sharedResourceProvider = OverlayResourceProvider(PhysicalResourceProvider.INSTANCE); - - // Create a fake in memory package that opts out of null safety - // TODO clean this up, perhaps use SharedAnalysisContextCollection from over_react_codemod - const packageName = 'package_depending_on_over_react'; - _sharedResourceProvider.setOverlay(transformPath('pubspec.yaml'), - content: ''' - name: $packageName - publish_to: none - environment: - # Opt out of null safety since it gets in our way for these tests - sdk: '>=2.11.0 <3.0.0' - dependencies: - over_react: ^4.3.1 - ''', - modificationStamp: 0); - // Steal the packages file from this project, which depends on over_react - final currentPackageConfig = File('.dart_tool/package_config.json').readAsStringSync(); - final updatedConfig = jsonDecode(currentPackageConfig) as Map; - var updatedPackages = (updatedConfig['packages'] as List).cast() - // Need to get rid of this config so its rootUri doesn't cause it to get used for this package. - ..removeSingleWhere((package) => package['name'] == 'over_react_analyzer_plugin'); - updatedPackages = updatedPackages.map((e) { - // Transform any relative paths to absolute ones, since they're relative to - // over_react_analyzer_plugin and not this new fake package. - var rootUri = e['rootUri'] as String; - if (!Uri.parse(rootUri).hasScheme) { - // fixme verify relative to pubspec.yaml - rootUri = Uri.file(p.canonicalize(p.absolute('pubspec.yaml', rootUri))).toString(); - } - return { - ...e, - 'rootUri': rootUri, - }; - }).toList() - ..add({ - "name": packageName, - "rootUri": "../", - "packageUri": "lib/", - // Opt out of null safety since it gets in our way for these tests - "languageVersion": "2.7", - }); - updatedConfig['packages'] = updatedPackages; - _sharedResourceProvider.setOverlay(transformPath('.dart_tool/package_config.json'), - content: jsonEncode(updatedConfig), modificationStamp: 0); - - _sharedCollection = AnalysisContextCollection( - includedPaths: [pathPrefix], - resourceProvider: _sharedResourceProvider, - ); - _hasInitializedSharedCollection = true; - } - - // Clean up overlays for previous calls, and let the driver know the file has changed so it doesn't cache the result - // if this call uses files of the same name. - // Do this before each call instead of after getting the resolved unit - // so that the session associate with the result remains valid after this function returns. - for (final absolutePath in _dartSourcePathsAddedInPreviousCall) { - _sharedResourceProvider.removeOverlay(absolutePath); - (_sharedCollection.contextFor(absolutePath) as DriverBasedAnalysisContext).driver.removeFile(absolutePath); - } - _dartSourcePathsAddedInPreviousCall = []; - - dartSourcesByAbsolutePath.forEach((absolutePath, source) { - _dartSourcePathsAddedInPreviousCall.add(absolutePath); - _sharedResourceProvider.setOverlay( - absolutePath, - content: source, - modificationStamp: 0, - ); - }); - - final results = {}; - for (final path in dartSourcesByPath.keys) { - final absolutePath = transformPath(path); - final context = _sharedCollection.contextFor(absolutePath); - final result = await context.currentSession.getResolvedUnit(absolutePath) as ResolvedUnitResult; - final lineInfo = result.lineInfo; - final filteredErrors = - filterIgnoresForErrors(result.errors, lineInfo, () => IgnoreInfo.forDart(result.unit, result.content)) - // only fail for error severity errors. - .where((error) => error.severity == Severity.error); - if (filteredErrors.isNotEmpty) { - final source = dartSourcesByPath[path]; - throw ArgumentError('Parse errors in source "$path":\n${filteredErrors.join('\n')}\nFull source:\n$source'); - } - results[path] = result; +Future parseAndGetResolvedUnit(String dartSource, {String? path}) async { + final sharedContext = SharedAnalysisContext.overReact; + final testFilePath = SharedAnalysisContext.overReact.createTestFile(dartSource, filename: path); + final result = await sharedContext.collection.contextFor(testFilePath).currentSession.getResolvedUnit(testFilePath) + as ResolvedUnitResult; + + final lineInfo = result.lineInfo; + final filteredErrors = + filterIgnoresForErrors(result.errors, lineInfo, () => IgnoreInfo.forDart(result.unit, result.content)) + // only fail for error severity errors. + .where((error) => error.severity == Severity.error); + if (filteredErrors.isNotEmpty) { + throw ArgumentError('Parse errors in source "$path":\n${filteredErrors.join('\n')}\nFull source:\n$dartSource'); } - return results; + return result; } - /// Returns [expression] parsed as AST. /// /// This is accomplished it by including the [expression] as a statement within a wrapper function @@ -245,15 +120,3 @@ Future parseExpression( // Unwrap the expression. Don't use `.unparenthesized` since it unwraps all sets of parentheses. return (statement.expression as ParenthesizedExpression).expression; } - -extension on List { - E removeSingleWhere(bool Function(E) test) { - final firstIndex = indexWhere(test); - if (firstIndex == -1) throw StateError('No matching item found'); - - final lastIndex = lastIndexWhere(test); - if (lastIndex != firstIndex) throw StateError('More than one matching item found'); - - return removeAt(firstIndex); - } -} diff --git a/tools/analyzer_plugin/test/unit/util/analyzer_util_test.dart b/tools/analyzer_plugin/test/unit/util/analyzer_util_test.dart index 46bd6f94f..ff586e60d 100644 --- a/tools/analyzer_plugin/test/unit/util/analyzer_util_test.dart +++ b/tools/analyzer_plugin/test/unit/util/analyzer_util_test.dart @@ -1,4 +1,5 @@ -// Adapted from: https://github.com/dart-lang/sdk/blob/1601f6fcd2a7aa39bdd71f1f988fe7acfaecc418/pkg/analyzer/test/src/dart/ast/utilities_test.dart +// Adapted from analyzer 5.13.0 src/dart/ast/utilities.dart +// Permalink: https://github.com/dart-lang/sdk/blob/efe0ca193f1c1485efc5467fb8dc9dfca6085d39/pkg/analyzer/test/src/dart/ast/utilities_test.dart // // Copyright 2013, the Dart project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without @@ -42,51 +43,66 @@ void main() { } @reflectiveTest -class NodeLocator2Test { +class NodeLocator2Test extends _SharedNodeLocatorTests { + @override + AstNode? locate( + CompilationUnit unit, + int start, [ + int? end, + ]) { + final locator = NodeLocator2(start, end); + final node = locator.searchWithin(unit)!; + return node; + } + void test_onlyStartOffset() { - const code = ' int vv; '; - // 012345678 + var code = ' f() {} '; + // 01234567 final unit = parseAndGetUnit(code); - final declaration = unit.declarations[0] as TopLevelVariableDeclaration; - final variableList = declaration.variables; - final typeName = (variableList.type as NamedType).name; - final varName = variableList.variables[0].name; + final function = unit.declarations.single as FunctionDeclaration; + final expression = function.functionExpression; + final body = expression.body as BlockFunctionBody; expect(NodeLocator2(0).searchWithin(unit), same(unit)); - expect(NodeLocator2(1).searchWithin(unit), same(typeName)); - expect(NodeLocator2(2).searchWithin(unit), same(typeName)); - expect(NodeLocator2(3).searchWithin(unit), same(typeName)); - expect(NodeLocator2(4).searchWithin(unit), same(variableList)); - expect(NodeLocator2(5).searchWithin(unit), same(varName)); - expect(NodeLocator2(6).searchWithin(unit), same(varName)); - expect(NodeLocator2(7).searchWithin(unit), same(declaration)); - expect(NodeLocator2(8).searchWithin(unit), same(unit)); - expect(NodeLocator2(9).searchWithin(unit), isNull); + expect(NodeLocator2(1).searchWithin(unit), same(function)); + expect(NodeLocator2(2).searchWithin(unit), same(function)); + expect(NodeLocator2(3).searchWithin(unit), same(expression.parameters)); + expect(NodeLocator2(4).searchWithin(unit), same(expression)); + expect(NodeLocator2(5).searchWithin(unit), same(body.block)); + expect(NodeLocator2(6).searchWithin(unit), same(body.block)); + expect(NodeLocator2(7).searchWithin(unit), same(unit)); expect(NodeLocator2(100).searchWithin(unit), isNull); } void test_startEndOffset() { - const code = ' int vv; '; - // 012345678 + var code = ' f() {} '; + // 01234567 final unit = parseAndGetUnit(code); - final declaration = unit.declarations[0] as TopLevelVariableDeclaration; - final variableList = declaration.variables; - final typeName = (variableList.type as NamedType).name; - final varName = variableList.variables[0].name; + final function = unit.declarations.single as FunctionDeclaration; expect(NodeLocator2(-1, 2).searchWithin(unit), isNull); expect(NodeLocator2(0, 2).searchWithin(unit), same(unit)); - expect(NodeLocator2(1, 2).searchWithin(unit), same(typeName)); - expect(NodeLocator2(1, 3).searchWithin(unit), same(typeName)); - expect(NodeLocator2(1, 4).searchWithin(unit), same(variableList)); - expect(NodeLocator2(5, 6).searchWithin(unit), same(varName)); - expect(NodeLocator2(5, 7).searchWithin(unit), same(declaration)); - expect(NodeLocator2(5, 8).searchWithin(unit), same(unit)); + expect(NodeLocator2(1, 2).searchWithin(unit), same(function)); + expect(NodeLocator2(1, 3).searchWithin(unit), same(function)); + expect(NodeLocator2(1, 4).searchWithin(unit), same(function)); + expect(NodeLocator2(5, 7).searchWithin(unit), same(unit)); expect(NodeLocator2(5, 100).searchWithin(unit), isNull); expect(NodeLocator2(100, 200).searchWithin(unit), isNull); } } @reflectiveTest -class NodeLocatorTest { +class NodeLocatorTest extends _SharedNodeLocatorTests { + @override + AstNode? locate( + CompilationUnit unit, + int start, [ + int? end, + ]) { + final locator = NodeLocator(start, end); + final node = locator.searchWithin(unit)!; + expect(locator.foundNode, same(node)); + return node; + } + void test_range() { final unit = parseAndGetUnit("library myLib;"); final node = _assertLocate(unit, 4, 10); @@ -121,17 +137,97 @@ class B {}'''); final node = locator.searchWithin(unit.declarations[1]); expect(node, isNull); } +} + +abstract class _SharedNodeLocatorTests { + AstNode? locate( + CompilationUnit unit, + int start, [ + int? end, + ]); + + void test_searchWithin_constructor_afterName_beforeParameters() { + var source = r''' +class A { + A() {} +} +'''; + var unit = parseAndGetUnit(source); + // TODO(dantup): Update these tests to use markers. + final node = _assertLocate(unit, source.indexOf('() {}')); + expect(node, isA()); + } + + void test_searchWithin_function_afterName_beforeParameters() { + var source = r''' +void f() {} +'''; + var unit = parseAndGetUnit(source); + final node = _assertLocate(unit, source.indexOf('() {}')); + expect(node, isA()); + } + + void test_searchWithin_function_afterName_beforeTypeParameters() { + var source = r''' +void f() {} +'''; + var unit = parseAndGetUnit(source); + final node = _assertLocate(unit, source.indexOf('() {}')); + expect(node, isA()); + } + + void test_searchWithin_method_afterName_beforeParameters() { + var source = r''' +class A { + void m() {} +} +'''; + var unit = parseAndGetUnit(source); + final node = _assertLocate(unit, source.indexOf('() {}')); + expect(node, isA()); + } + + void test_searchWithin_method_afterName_beforeTypeParameters() { + var source = r''' +class A { + void m() {} +} +'''; + var unit = parseAndGetUnit(source); + final node = _assertLocate(unit, source.indexOf('() {}')); + expect(node, isA()); + } + + void test_searchWithin_namedConstructor_afterName_beforeParameters() { + var source = r''' +class A { + A.c() {} +} +'''; + var unit = parseAndGetUnit(source); + final node = _assertLocate(unit, source.indexOf('() {}')); + expect(node, isA()); + } + + void test_searchWithin_setter_afterName_beforeParameters() { + var source = r''' +set s(int i) {} +'''; + var unit = parseAndGetUnit(source); + final node = _assertLocate(unit, source.indexOf('(int i)')); + expect(node, isA()); + } AstNode _assertLocate( CompilationUnit unit, - int start, - int end, - ) { - final locator = NodeLocator(start, end); - final node = locator.searchWithin(unit)!; - expect(locator.foundNode, same(node)); + int start, [ + int? end, + ]) { + end ??= start; + final node = locate(unit, start, end)!; expect(node.offset <= start, isTrue, reason: "Node starts after range"); - expect(node.offset + node.length > end, isTrue, reason: "Node ends before range"); + expect(node.offset + node.length > end, isTrue, + reason: "Node ends before range"); return node; } } diff --git a/tools/analyzer_plugin/test/unit/util/ast_util_test.dart b/tools/analyzer_plugin/test/unit/util/ast_util_test.dart index 07b698611..9e3494d87 100644 --- a/tools/analyzer_plugin/test/unit/util/ast_util_test.dart +++ b/tools/analyzer_plugin/test/unit/util/ast_util_test.dart @@ -9,14 +9,17 @@ void main() { group('ast_util', () { test('allDescendants returns all descendants in breadth-first order', () { final unit = parseAndGetUnit(/*language=dart*/ r''' - class A {} + class A { + var field; + } class B {} '''); expect(allDescendants(unit), [ isA(), // class A {} isA(), // class B {} - isA(), // A - isA(), // B + isA(), // var field; + isA(), // field + isA(), // field ]); }); @@ -81,7 +84,7 @@ void main() { final usage = getAllPrintedExpressions(unit).single as Identifier; expect(usage.name, 'foo', reason: 'test setup check'); expect(usage.staticElement, isNotNull, reason: 'test setup check'); - expect(lookUpVariable(usage.staticElement!, unit)?.name.name, 'foo'); + expect(lookUpVariable(usage.staticElement!, unit)?.name.lexeme, 'foo'); }); group('returns null when', () { @@ -180,7 +183,7 @@ void main() { final usage = getAllPrintedExpressions(unit).single as Identifier; expect(usage.name, 'foo', reason: 'test setup check'); expect(usage.staticElement, isNotNull, reason: 'test setup check'); - expect((lookUpDeclaration(usage.staticElement!, unit) as FunctionDeclaration).name.name, 'foo'); + expect((lookUpDeclaration(usage.staticElement!, unit) as FunctionDeclaration).name.lexeme, 'foo'); }); test('looks up a variable', () async { @@ -193,7 +196,7 @@ void main() { final usage = getAllPrintedExpressions(unit).single as Identifier; expect(usage.name, 'foo', reason: 'test setup check'); expect(usage.staticElement, isNotNull, reason: 'test setup check'); - expect((lookUpDeclaration(usage.staticElement!, unit) as VariableDeclaration).name.name, 'foo'); + expect((lookUpDeclaration(usage.staticElement!, unit) as VariableDeclaration).name.lexeme, 'foo'); }); test('looks up a class', () async { @@ -207,7 +210,7 @@ void main() { final usage = getAllPrintedExpressions(unit).single as Identifier; expect(usage.name, 'Foo', reason: 'test setup check'); expect(usage.staticElement, isNotNull, reason: 'test setup check'); - expect((lookUpDeclaration(usage.staticElement!, unit) as ClassDeclaration).name.name, 'Foo'); + expect((lookUpDeclaration(usage.staticElement!, unit) as ClassDeclaration).name.lexeme, 'Foo'); }); group('returns null when', () { @@ -245,7 +248,7 @@ void main() { final usage = getAllPrintedExpressions(unit).single as Identifier; expect(usage.name, 'foo', reason: 'test setup check'); expect(usage.staticElement, isNotNull, reason: 'test setup check'); - expect(lookUpParameter(usage.staticElement!, unit)?.identifier?.name, 'foo'); + expect(lookUpParameter(usage.staticElement!, unit)?.name?.lexeme, 'foo'); }); test('looks up a named parameter', () async { @@ -257,7 +260,7 @@ void main() { final usage = getAllPrintedExpressions(unit).single as Identifier; expect(usage.name, 'foo', reason: 'test setup check'); expect(usage.staticElement, isNotNull, reason: 'test setup check'); - expect(lookUpParameter(usage.staticElement!, unit)?.identifier?.name, 'foo'); + expect(lookUpParameter(usage.staticElement!, unit)?.name?.lexeme, 'foo'); }); group('returns null when', () { diff --git a/tools/analyzer_plugin/test/unit/util/boilerplate_utils_test.dart b/tools/analyzer_plugin/test/unit/util/boilerplate_utils_test.dart index 659549492..50c16ba0e 100644 --- a/tools/analyzer_plugin/test/unit/util/boilerplate_utils_test.dart +++ b/tools/analyzer_plugin/test/unit/util/boilerplate_utils_test.dart @@ -4,6 +4,8 @@ import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart'; +import 'package:meta/meta.dart'; +import 'package:path/path.dart' as p; import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart'; import 'package:over_react_analyzer_plugin/src/util/ast_util.dart'; import 'package:over_react_analyzer_plugin/src/util/boilerplate_utils.dart'; @@ -12,59 +14,94 @@ import 'package:test/test.dart'; import '../../test_util.dart'; -const sourceWithOverReactPart = /*language=dart*/ r''' - import 'package:over_react/over_react.dart'; - - // ignore: uri_has_not_been_generated - part 'foo.over_react.g.dart'; - - UiFactory Foo = _$Foo; // ignore: undefined_identifier, invalid_assignment - - mixin FooProps on UiProps {} - - class FooComponent extends UiComponent2 { - @override - void render() {} - } -'''; - -const sourceWithNonOverReactPart = /*language=dart*/ r''' - import 'package:over_react/over_react.dart'; - - // ignore: uri_has_not_been_generated - part 'foo.not_over_react.g.dart'; - - UiFactory Foo = _$Foo; // ignore: undefined_identifier, invalid_assignment - - mixin FooProps on UiProps {} - - class FooComponent extends UiComponent2 { - @override - void render() {} - } -'''; - -const sourceWithNoPart = /*language=dart*/ r''' - import 'package:over_react/over_react.dart'; - - UiFactory Foo = _$Foo; // ignore: undefined_identifier, invalid_assignment - - mixin FooProps on UiProps {} - - class FooComponent extends UiComponent2 { - @override - void render() {} - } -'''; +String basenameWithoutExtensionOrSuffixes(String filenameOrPath) => + p.basenameWithoutExtension(filenameOrPath).split('.').first; void main() { group('boilerplate_util', () { + // Use different names each time so they don't conflict in the SharedAnalysisContext. + // Usually we don't have to worry about this since names are auto-generated, but + // these tests involve need to explicit specifying names to properly test part handling. + var nameCounter = 0; + String testName; + String testDifferentName; + + String sourceWithOverReactPart; + String sourceWithNonOverReactPart; + String sourceWithNoPart; + + setUp(() { + nameCounter++; + testName = 'test_name_$nameCounter'; + testDifferentName = 'test_different_name_$nameCounter'; + + sourceWithOverReactPart = ''' + import 'package:over_react/over_react.dart'; + + // ignore: uri_has_not_been_generated + part '$testName.over_react.g.dart'; + + UiFactory Foo = _\$Foo; // ignore: undefined_identifier, invalid_assignment + + mixin FooProps on UiProps {} + + class FooComponent extends UiComponent2 { + @override + void render() {} + } + '''; + + sourceWithNonOverReactPart = ''' + import 'package:over_react/over_react.dart'; + + // ignore: uri_has_not_been_generated + part '$testName.not_over_react.g.dart'; + + UiFactory Foo = _\$Foo; // ignore: undefined_identifier, invalid_assignment + + mixin FooProps on UiProps {} + + class FooComponent extends UiComponent2 { + @override + void render() {} + } + '''; + + sourceWithNoPart = ''' + import 'package:over_react/over_react.dart'; + + UiFactory Foo = _\$Foo; // ignore: undefined_identifier, invalid_assignment + + mixin FooProps on UiProps {} + + class FooComponent extends UiComponent2 { + @override + void render() {} + } + '''; + }); + + Future checkPartValidity({ + bool shouldBeValid = true, + bool isPartOverReact = true, + @required String path, + }) async { + final result = await parseAndGetResolvedUnit( + isPartOverReact ? sourceWithOverReactPart : sourceWithNonOverReactPart, + path: path, + ); + final part = result.unit.directives.whereType().firstOrNull; + expect(part, isNotNull); + expect(result.uri.path, endsWith('/$path')); + expect(overReactGeneratedPartDirectiveIsValid(part, result.uri), shouldBeValid); + } + group('getOverReactGeneratedPartDirective', () { test('returns correct over_react part directive', () { final unit = parseAndGetUnit(sourceWithOverReactPart); final result = getOverReactGeneratedPartDirective(unit); expect(result, isA()); - expect(result.uri.stringValue, 'foo.over_react.g.dart'); + expect(result.uri.stringValue, '$testName.over_react.g.dart'); }); group('returns null when', () { @@ -82,12 +119,13 @@ void main() { group('overReactGeneratedPartDirectiveIsValid', () { test('returns true when valid part directive matches the file uri', () async { - await checkPartValidity(); + await checkPartValidity(path: '$testName.dart'); }); group('returns false when', () { test('part directive is not over_react', () async { await checkPartValidity( + path: '$testName.dart', isPartOverReact: false, shouldBeValid: false, ); @@ -95,7 +133,7 @@ void main() { test('part directive does not match the file uri', () async { await checkPartValidity( - path: 'different_file_path.dart', + path: '$testDifferentName.dart', shouldBeValid: false, ); }); @@ -107,14 +145,14 @@ void main() { final sourceFileEdits = await getSourceFileEdits( sourceWithOverReactPart, (builder, result) => addOverReactGeneratedPartDirective(builder, result.unit, result.lineInfo, result.uri), - path: 'foo.dart', + path: '$testName.dart', ); expect(sourceFileEdits, isEmpty); }); group('adds a valid part directive', () { test('when there are no part directives in the file', () async { - final result = await parseAndGetResolvedUnit(sourceWithNoPart, path: 'foo.dart'); + final result = await parseAndGetResolvedUnit(sourceWithNoPart, path: '$testName.dart'); final sourceChange = await buildFileEdit(result, (builder) { addOverReactGeneratedPartDirective(builder, result.unit, result.lineInfo, result.uri); }); @@ -127,11 +165,11 @@ void main() { result.lineInfo.getOffsetOfLineAfter(nextLine(result.unit.directives.last.end, result.lineInfo)); expect(editList.first.offset, offset, reason: 'should be new line between existing directives and new part'); expect(editList.first.length, 0, reason: 'nothing is replaced'); - expect(editList.first.replacement, 'part \'foo.over_react.g.dart\';\n\n'); + expect(editList.first.replacement, 'part \'$testName.over_react.g.dart\';\n\n'); }); test('when there is an existing part directive in the file', () async { - final result = await parseAndGetResolvedUnit(sourceWithNonOverReactPart, path: 'foo.dart'); + final result = await parseAndGetResolvedUnit(sourceWithNonOverReactPart, path: '$testName.dart'); final sourceChange = await buildFileEdit(result, (builder) { addOverReactGeneratedPartDirective(builder, result.unit, result.lineInfo, result.uri); }); @@ -143,7 +181,7 @@ void main() { final offset = result.lineInfo.getOffsetOfLineAfter(result.unit.directives.last.end); expect(editList.first.offset, offset, reason: 'new part should be on the line after existing part'); expect(editList.first.length, 0, reason: 'nothing is replaced'); - expect(editList.first.replacement, 'part \'foo.over_react.g.dart\';\n\n'); + expect(editList.first.replacement, 'part \'$testName.over_react.g.dart\';\n\n'); }); }); @@ -170,7 +208,7 @@ void main() { final sourceFileEdits = await getSourceFileEdits( sourceWithNoPart, (builder, result) => removeOverReactGeneratedPartDirective(builder, result.unit), - path: 'foo.dart', + path: '$testName.dart', ); expect(sourceFileEdits, isEmpty); }); @@ -179,7 +217,7 @@ void main() { final sourceFileEdits = await getSourceFileEdits( sourceWithNonOverReactPart, (builder, result) => removeOverReactGeneratedPartDirective(builder, result.unit), - path: 'foo.dart', + path: '$testName.dart', ); expect(sourceFileEdits, isEmpty); }); @@ -187,7 +225,7 @@ void main() { group('removes over_react part directive if it exists', () { test('', () async { - final result = await parseAndGetResolvedUnit(sourceWithOverReactPart, path: 'foo.dart'); + final result = await parseAndGetResolvedUnit(sourceWithOverReactPart, path: '$testName.dart'); final sourceChange = await buildFileEdit(result, (builder) { removeOverReactGeneratedPartDirective(builder, result.unit); }); @@ -203,17 +241,17 @@ void main() { }); test('and there are other part directives in file', () async { - final result = await parseAndGetResolvedUnit(/*language=dart*/ r''' + final result = await parseAndGetResolvedUnit(''' import 'package:over_react/over_react.dart'; // ignore: uri_has_not_been_generated - part 'foo.not_over_react.g.dart'; + part '$testName.not_over_react.g.dart'; // ignore: uri_has_not_been_generated - part 'foo.over_react.g.dart'; + part '$testName.over_react.g.dart'; // ignore: uri_has_not_been_generated part 'other.not_over_react.g.dart'; - UiFactory Foo = _$Foo; // ignore: undefined_identifier, invalid_assignment + UiFactory Foo = _\$Foo; // ignore: undefined_identifier, invalid_assignment mixin FooProps on UiProps {} @@ -221,7 +259,7 @@ void main() { @override void render() {} } - ''', path: 'foo.dart'); + ''', path: '$testName.dart'); final sourceChange = await buildFileEdit(result, (builder) { removeOverReactGeneratedPartDirective(builder, result.unit); }); @@ -244,7 +282,7 @@ void main() { final sourceFileEdits = await getSourceFileEdits( sourceWithNoPart, (builder, result) => fixOverReactGeneratedPartDirective(builder, result.unit, result.uri), - path: 'foo.dart', + path: '$testName.dart', ); expect(sourceFileEdits, isEmpty); }); @@ -253,7 +291,7 @@ void main() { final sourceFileEdits = await getSourceFileEdits( sourceWithOverReactPart, (builder, result) => fixOverReactGeneratedPartDirective(builder, result.unit, result.uri), - path: 'foo.dart', + path: '$testName.dart', ); expect(sourceFileEdits, isEmpty); }); @@ -262,14 +300,14 @@ void main() { final sourceFileEdits = await getSourceFileEdits( sourceWithNonOverReactPart, (builder, result) => fixOverReactGeneratedPartDirective(builder, result.unit, result.uri), - path: 'foo.dart', + path: '$testName.dart', ); expect(sourceFileEdits, isEmpty); }); }); test('replaces over_react part directive that does not match file uri', () async { - final result = await parseAndGetResolvedUnit(sourceWithOverReactPart, path: 'different_file_name.dart'); + final result = await parseAndGetResolvedUnit(sourceWithOverReactPart, path: '$testDifferentName.dart'); final sourceChange = await buildFileEdit(result, (builder) { fixOverReactGeneratedPartDirective(builder, result.unit, result.uri); }); @@ -281,31 +319,17 @@ void main() { final part = getOverReactGeneratedPartDirective(result.unit); expect(editList.first.offset, part.offset); expect(editList.first.length, part.length); - expect(editList.first.replacement, 'part \'different_file_name.over_react.g.dart\';'); + expect(editList.first.replacement, 'part \'$testDifferentName.over_react.g.dart\';'); }); }); }); } -Future checkPartValidity({ - bool shouldBeValid = true, - bool isPartOverReact = true, - String path = 'foo.dart', -}) async { - final result = await parseAndGetResolvedUnit( - isPartOverReact ? sourceWithOverReactPart : sourceWithNonOverReactPart, - path: path, - ); - final part = result.unit.directives.whereType().firstOrNull; - expect(part, isNotNull); - expect(result.uri.toFilePath(), endsWith('/$path')); - expect(overReactGeneratedPartDirectiveIsValid(part, result.uri), shouldBeValid); -} Future> getSourceFileEdits( String dartSource, void Function(DartFileEditBuilder builder, ResolvedUnitResult result) callUtilityFunction, { - String path = 'foo.dart', + @required String path, }) async { final result = await parseAndGetResolvedUnit(dartSource, path: path); final sourceChange = await buildFileEdit(result, (builder) { diff --git a/tools/analyzer_plugin/test/unit/util/component_usage_test.dart b/tools/analyzer_plugin/test/unit/util/component_usage_test.dart index aa484b8ea..3897e1876 100644 --- a/tools/analyzer_plugin/test/unit/util/component_usage_test.dart +++ b/tools/analyzer_plugin/test/unit/util/component_usage_test.dart @@ -423,7 +423,7 @@ void main() { expect( componentUsage.propsClassElement, builderSource.canBuilderResolve - ? isA().having((c) => c.name, 'name', builderSource.propsName) + ? isA().having((c) => c.name, 'name', builderSource.propsName) : isNull, ); expect( diff --git a/tools/analyzer_plugin/test/util/package_util.dart b/tools/analyzer_plugin/test/util/package_util.dart new file mode 100644 index 000000000..2369f8ce2 --- /dev/null +++ b/tools/analyzer_plugin/test/util/package_util.dart @@ -0,0 +1,136 @@ +// From https://github.com/Workiva/over_react_codemod/blob/a14a4ef372a1b2c2a625755cec7a6f956a074650/lib/src/util/package_util.dart + +// Copyright 2021 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. + +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as p; + +final _logger = Logger('orcm.pubspec'); + +bool _isPubGetNecessary(String packageRoot) { + final packageConfig = + File(p.join(packageRoot, '.dart_tool', 'package_config.json')); + final pubspec = File(p.join(packageRoot, 'pubspec.yaml')); + final pubspecLock = File(p.join(packageRoot, 'pubspec.lock')); + + if (!pubspec.existsSync()) { + throw ArgumentError('pubspec.yaml not found in directory: $packageRoot'); + } + + if (packageConfig.existsSync() && pubspecLock.existsSync()) { + return !pubspecLock.lastModifiedSync().isAfter(pubspec.lastModifiedSync()); + } + + return true; +} + +/// Runs `pub get` in [packageRoot] unless running `pub get` would have no effect. +void runPubGetIfNeeded(String packageRoot) { + if (_isPubGetNecessary(packageRoot)) { + runPubGet(packageRoot); + } else { + _logger.info( + 'Skipping `pub get`, which has already been run, in `$packageRoot`'); + } +} + +/// Runs `pub get` in [workingDirectory], and throws if the command completed +/// with a non-zero exit code. +/// +/// For convenience, tries running with `pub get --offline` if `pub get` fails, +/// for a better experience when not authenticated to private pub servers. +void runPubGet(String workingDirectory) { + _logger.info('Running `pub get` in `$workingDirectory`...'); + + final result = Process.runSync('dart', ['pub', 'get'], + workingDirectory: workingDirectory, + runInShell: true + ); + final exitCode = result.exitCode; + + if (exitCode == 69) { + _logger.info( + 'Re-running `pub get` but with `--offline`, to hopefully fix the above error.'); + final retryResult = Process.runSync('pub', ['get', '--offline'], + workingDirectory: workingDirectory, + runInShell: true); + final exitCode = retryResult.exitCode; + if (exitCode != 0) { + throw Exception('pub get failed with exit code $exitCode in $workingDirectory'); + } + } else if (exitCode != 0) { + throw Exception('pub get failed with exit code $exitCode in $workingDirectory'); + } +} + +/// Returns a path to the closest Dart package root (i.e., a directory with a +/// pubspec.yaml file) to [path], throwing if no package root can be found. +/// +/// If [path] is itself a package root, it will be returned. +/// +/// Example: +/// +/// ```dart +/// // All of these return a path to 'some_package' +/// findPackageRootFor('some_package/lib/src/file.dart'); +/// findPackageRootFor('some_package/lib/'); +/// findPackageRootFor('some_package'); +/// +/// // Returns a path to 'some_package/subpackages/some_nested_package' +/// findPackageRootFor('some_package/some_nested_package/lib/file.dart'); +/// ``` +String findPackageRootFor(String path) { + final packageRoot = [ + path, + ...ancestorsOfPath(path) + ].firstWhereOrNull((path) => File(p.join(path, 'pubspec.yaml')).existsSync()); + + if (packageRoot == null) { + throw Exception('Could not find package root for file `$path`'); + } + + return packageRoot; +} + +/// Returns canonicalized paths for all the the ancestor directories of [path], +/// starting with its parent and working upwards. +Iterable ancestorsOfPath(String path) sync* { + path = p.canonicalize(path); + + // p.dirname of the root directory is the root directory, so if they're the same, stop. + final parent = p.dirname(path); + if (p.equals(path, parent)) return; + + yield parent; + yield* ancestorsOfPath(parent); +} + +/// Returns whether [file] is within a top-level `build` directory of a package root. +bool isNotWithinTopLevelBuildOutputDir(File file) => + !isWithinTopLevelDir(file, 'build'); + +/// Returns whether [file] is within a top-level `tool` directory of a package root. +bool isNotWithinTopLevelToolDir(File file) => + !isWithinTopLevelDir(file, 'tool'); + +/// Returns whether [file] is within a top-level [topLevelDir] directory +/// (e.g., `bin`, `lib`, `web`) of a package root. +bool isWithinTopLevelDir(File file, String topLevelDir) => + ancestorsOfPath(file.path).any((ancestor) => + p.basename(ancestor) == topLevelDir && + File(p.join(p.dirname(ancestor), 'pubspec.yaml')).existsSync()); diff --git a/tools/analyzer_plugin/test/util/package_util_test.dart b/tools/analyzer_plugin/test/util/package_util_test.dart new file mode 100644 index 000000000..c53ef8948 --- /dev/null +++ b/tools/analyzer_plugin/test/util/package_util_test.dart @@ -0,0 +1,223 @@ +// From https://github.com/Workiva/over_react_codemod/blob/a14a4ef372a1b2c2a625755cec7a6f956a074650/test/util/package_util_test.dart +// +// Copyright 2021 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. + +import 'dart:io'; + + +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; + +import './package_util.dart'; + +void main() { + group('Package Utilities', () { + // Use a utility to determine the path to the root directory since it will + // be different when run locally or in CI. + final currentPathFromRoot = p.canonicalize('.'); + + group('findPackageRootFor', () { + test('on the current directory', () { + expect(findPackageRootFor('.'), equals('.')); + }); + + test('on a nested file', () { + expect( + findPackageRootFor('./test/util/package_util_test.dart'), + equals(currentPathFromRoot), + ); + }); + + test('with a nested pubspec', () { + expect( + findPackageRootFor( + 'test/test_fixtures/over_react_project/lib/analysis_warmup.dart'), + equals('$currentPathFromRoot/test/test_fixtures/over_react_project'), + ); + }); + + test('on a pubspec file', () { + expect( + findPackageRootFor('$currentPathFromRoot/pubspec.yaml'), + equals(currentPathFromRoot), + ); + }); + + test('when a pubspec cannot be found', () { + final tmpDir = Directory.systemTemp.createTempSync(); + addTearDown(tmpDir.deleteSync); + expect( + () => findPackageRootFor(tmpDir.path), + throwsA(isA() + .havingToStringValue(contains('Could not find package root'))), + ); + }); + }); + + group('ancestorsOfPath', () { + final ancestorsOfPathFromRoot = []; + + setUpAll(() { + // Determine the expected ancestors of the current path from root. + final segments = p.split(currentPathFromRoot); + for (var i = 1; i < segments.length; i++) { + ancestorsOfPathFromRoot + .add(p.joinAll(segments.sublist(0, segments.length - i))); + } + }); + + test('for a nested file', () { + expect( + ancestorsOfPath( + 'test/test_fixtures/over_react_project/lib/analysis_warmup.dart'), + orderedEquals([ + '$currentPathFromRoot/test/test_fixtures/over_react_project/lib', + '$currentPathFromRoot/test/test_fixtures/over_react_project', + '$currentPathFromRoot/test/test_fixtures', + '$currentPathFromRoot/test', + currentPathFromRoot, + ...ancestorsOfPathFromRoot, + ]), + ); + }); + + test('for a nested directory', () { + expect( + ancestorsOfPath('lib/src/util'), + orderedEquals([ + '$currentPathFromRoot/lib/src', + '$currentPathFromRoot/lib', + currentPathFromRoot, + ...ancestorsOfPathFromRoot, + ]), + ); + }); + + test('on the current directory', () { + expect( + ancestorsOfPath('.'), + orderedEquals(ancestorsOfPathFromRoot), + ); + }); + }); + + group('top-level directory checking utilities', () { + List? filesInTopLevelDir; + List? filesNotInTopLevelBuildDir; + + // Set the top level directory that the test cases use for verifying output. + void _initializeTestCases(String directory) { + filesInTopLevelDir = [ + File('$directory/some_place/some_file.dart'), + File( + 'test/test_fixtures/over_react_project/$directory/lib/analysis_warmup.dart'), + ]; + filesNotInTopLevelBuildDir = [ + File('test/$directory/some_place/some_file.dart'), + File('some_file.dart'), + File( + 'test/test_fixtures/$directory/over_react_project/lib/analysis_warmup.dart'), + File( + 'test/test_fixtures/over_react_project/lib/$directory/analysis_warmup.dart'), + ]; + } + + tearDown(() { + filesInTopLevelDir = null; + filesNotInTopLevelBuildDir = null; + }); + + group('isWithinTopLevelDir', () { + const testDirectory = 'some_directory'; + + test('for files in top-level dir input', () { + _initializeTestCases(testDirectory); + for (final file in filesInTopLevelDir!) { + expect( + isWithinTopLevelDir(file, testDirectory), + isTrue, + reason: '${file.path} is in a top-level $testDirectory directory', + ); + } + }); + + test('for files not in top-level dir input', () { + _initializeTestCases(testDirectory); + for (final file in filesNotInTopLevelBuildDir!) { + expect( + isWithinTopLevelDir(file, testDirectory), + isFalse, + reason: + '${file.path} is not in a top-level $testDirectory directory', + ); + } + }); + }); + + group('isNotWithinTopLevelBuildOutputDir', () { + test('for files in top-level build dir', () { + _initializeTestCases('build'); + for (final file in filesInTopLevelDir!) { + expect( + isNotWithinTopLevelBuildOutputDir(file), + isFalse, + reason: '${file.path} is in a top-level build directory', + ); + } + }); + + test('for files not in top-level build dir', () { + _initializeTestCases('build'); + for (final file in filesNotInTopLevelBuildDir!) { + expect( + isNotWithinTopLevelBuildOutputDir(file), + isTrue, + reason: '${file.path} is not in a top-level build directory', + ); + } + }); + }); + + group('isNotWithinTopLevelToolDir', () { + test('for files in top-level tool dir', () { + _initializeTestCases('tool'); + for (final file in filesInTopLevelDir!) { + expect( + isNotWithinTopLevelToolDir(file), + isFalse, + reason: '${file.path} is in a top-level tool directory', + ); + } + }); + + test('for files not in top-level tool dir', () { + _initializeTestCases('tool'); + for (final file in filesNotInTopLevelBuildDir!) { + expect( + isNotWithinTopLevelToolDir(file), + isTrue, + reason: '${file.path} is not in a top-level tool directory', + ); + } + }); + }); + }); + }); +} + +extension ObjectMatchers on TypeMatcher { + Matcher havingToStringValue(dynamic matcher) => + having((p) => p.toString(), 'toString() value', matcher); +} diff --git a/tools/analyzer_plugin/test/util/shared_analysis_context.dart b/tools/analyzer_plugin/test/util/shared_analysis_context.dart new file mode 100644 index 000000000..dfd5a1cb6 --- /dev/null +++ b/tools/analyzer_plugin/test/util/shared_analysis_context.dart @@ -0,0 +1,290 @@ +// From https://github.com/Workiva/over_react_codemod/blob/a14a4ef372a1b2c2a625755cec7a6f956a074650/test/resolved_file_context.dart + +// ignore_for_file: comment_references + +// Copyright 2021 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. + +import 'dart:async'; +import 'dart:io'; + +import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/diagnostic/diagnostic.dart'; +import 'package:analyzer/error/error.dart'; +import 'package:io/io.dart'; +import 'package:path/path.dart' as p; +import 'package:uuid/uuid.dart'; + +import 'package_util.dart'; +import 'util.dart'; + +/// Provides a mechanism for getting resolved codemod [TestFile]s for test cases +/// using a shared context root, allowing: +/// +/// - the resolution of package imports (provided they're depended on in the context root's pubspec.yaml) +/// - multiple tests to run without `pub get`-ing in a new package or spinning up +/// a new same [AnalysisContextCollection], which dramatically improves test run times +/// if there are many tests that rely on a resolved context. +/// +/// Also, re-using a fixed directory instead of a new temporary directory each time +/// means that `pub get`s from the previous run as well as any analysis cached +/// within `~/.dartServer` can be reused between test runs, which means faster +/// test runs during local development. +class SharedAnalysisContext { + /// A context root located at `test/test_fixtures/over_react_project` + /// that depends on the `over_react` package. + static final overReact = + SharedAnalysisContext(p.join(findPackageRootFor(p.current), 'test/test_fixtures/over_react_project')); + + /// Creates a temporary copy of the files in this context. + /// + /// Useful when you want mostly the same setup as an existing context, + /// but need additional changes to pubspec.yaml or analysis_options.yaml. + static SharedAnalysisContext createTemporaryCopy(SharedAnalysisContext other, String newParentDirectory) { + final copyParentDir = Directory(p.join(findPackageRootFor(p.current), newParentDirectory)); + copyParentDir.createSync(recursive: true); + final copyDir = copyParentDir.createTempSync().path; + // Adapted from package:io 1.0.4 `copyPathSync`. + // Permalink https://github.com/dart-lang/io/blob/0c6fa36867b64748639515d5ea37b99176772756/lib/src/copy_path.dart#L54 + // + // Copyright 2017, the Dart project authors. + // + // Redistribution and use in source and binary forms, with or without + // modification, are permitted provided that the following conditions are + // met: + // + // * Redistributions of source code must retain the above copyright + // notice, this list of conditions and the following disclaimer. + // * Redistributions in binary form must reproduce the above + // copyright notice, this list of conditions and the following + // disclaimer in the documentation and/or other materials provided + // with the distribution. + // * Neither the name of Google LLC nor the names of its + // contributors may be used to endorse or promote products derived + // from this software without specific prior written permission. + // + // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + for (final file in Directory(other.contextRootPath).listSync(recursive: false)) { + if (const {'pubspec.lock', '.dart_tool'}.contains(p.basename(file.path))) { + continue; + } + + final copyTo = p.join(copyDir, p.relative(file.path, from: other.contextRootPath)); + if (file is Directory) { + copyPathSync(file.path, copyTo); + } else if (file is File) { + File(file.path).copySync(copyTo); + } else if (file is Link) { + Link(copyTo).createSync(file.targetSync(), recursive: true); + } + } + return SharedAnalysisContext._temporaryCopy(copyDir, customPubGetErrorMessage: other.customPubGetErrorMessage); + } + + /// The path to the package root in which test files will be created + /// and resolved. + final String contextRootPath; + + /// Whether this is a temporary copy of another shared context that should be deleted after use. + final bool isTemporaryCopy; + + /// The analysis context collection for files within [contextRootPath], initialized + /// lazily. + late final AnalysisContextCollection collection = _initCollection(); + + /// A custom error message to display if `pub get` fails. + final String? customPubGetErrorMessage; + + // Namespace the test path using a UUID so that concurrent runs + // don't try to output the same filename, making it so that we can + // easily create new filenames by counting synchronously [nextFilename] + // without coordinating with other test processes. + // + // This also allows us to keep using the same project directory among concurrent tests + // and across test runs, which means the Dart analysis server can use cached + // analysis results (meaning faster test runs). + final _testFileSubpath = 'lib/dynamic_test_files/${Uuid().v4()}'; + + SharedAnalysisContext._( + {required this.contextRootPath, required this.isTemporaryCopy, this.customPubGetErrorMessage}) { + if (!p.isAbsolute(contextRootPath)) { + throw ArgumentError.value(contextRootPath, 'projectRoot', 'must be absolute'); + } + } + + SharedAnalysisContext._temporaryCopy(String contextRootPath, {String? customPubGetErrorMessage}) + : this._( + contextRootPath: contextRootPath, + customPubGetErrorMessage: customPubGetErrorMessage, + isTemporaryCopy: true, + ); + + SharedAnalysisContext(String contextRootPath, {String? customPubGetErrorMessage}) + : this._( + contextRootPath: contextRootPath, + customPubGetErrorMessage: customPubGetErrorMessage, + isTemporaryCopy: false, + ); + + AnalysisContextCollection _initCollection() { + // Note that if tests are run concurrently, then concurrent pub gets will be run. + // This is hard to avoid (trying to avoid it using a filesystem lock in + // macOS/Linux doesn't work due to advisory lock behavior), but intermittently + // causes issues, so message referencing exit code 66 and workaround below. + try { + runPubGetIfNeeded(contextRootPath); + } catch (e, st) { + var message = [ + // ignore: no_adjacent_strings_in_list + 'If the exit code is 66, the issue is likely concurrent `pub get`s on' + ' this directory from concurrent test entrypoints.' + ' Regardless of the exit code, if in CI, try running `pub get`' + ' in this directory before running any tests.', + if (customPubGetErrorMessage != null) customPubGetErrorMessage, + ].join(' '); + throw Exception('$message\nOriginal exception: $e$st'); + } + + return AnalysisContextCollection( + includedPaths: [contextRootPath], + ); + } + + /// Warms up the AnalysisContextCollection by running `pub get` (if needed) and + /// initializing [collection] if that hasn't been done yet, and getting the + /// resolved library for `lib/analysis_warmup.dart` in the project. + /// + /// This is useful to run in a `setUpAll` so that the first test resolving a file + /// doesn't take abnormally long (e.g., if having consistent test times is + /// important, or if the first test might have a short timeout). + Future warmUpAnalysis() async { + final path = p.join(contextRootPath, 'lib/analysis_warmup.dart'); + await collection.contextFor(path).currentSession.getResolvedLibrary(path); + } + + /// Creates a new file within [_testFileSubpath] with the name [filename] + /// (or a generated filename if not specified) and the given [sourceText] + /// and returns a codemod FileContext for that file. + /// + /// Throws if [filename] has already been used before in this instance, + /// to prevent the wrong results from being returned. + /// Since there's no public analyzer API to get an updated result for a file + /// that has been modified, reusing a file name means that [collection] or + /// the returned context backed by [collection] could return results for a previous + /// call to this method, and not results containing the updated [sourceText]. + /// And, even if there were a way to update it, reusing file names would be prone + /// to race conditions, so this restriction will likely never be removed. + String createTestFile(String sourceText, {String? filename}) { + filename ??= nextFilename(); + final path = p.join(contextRootPath, _testFileSubpath, filename); + final file = File(path); + if (file.existsSync()) { + throw StateError('File already exists: $filename.' + ' Cannot use an existing file, since there is no public API' + ' to update a file within a AnalysisContextCollection.' + ' Make sure you\'re using a unique filename each time.' + ' This error can also occur if there are concurrent test runs' + ' and `_testFileSubpath` is not namespaced.'); + } + file.parent.createSync(recursive: true); + file.writeAsStringSync(sourceText); + + // Assert that this doesn't throw a StateError due to this file not + // existing in the context we've set up (which shouldn't ever happen). + collection.contextFor(path); + + return path; + } + + int _fileNameCounter = 0; + + /// Returns a filename that hasn't been used before. + String nextFilename() => 'test_${_fileNameCounter++}.dart'; +} + +/// A function that returns whether an error is expected and thus should be ignored +typedef IsExpectedError = bool Function(AnalysisError); + +/// Checks [result] (usually the return value of a call to `AnalysisSession.getResolvedLibrary2`) +/// and throws if: +/// +/// - there were any issues getting the resolved result +/// - there are analysis errors (other than those for which [isExpectedError] returns `true`) +/// that are either have an `error` severity or are otherwise potentially problematic +/// (e.g., unused members, which may mean there's a typo in the test) +void checkResolvedResultForErrors( + SomeResolvedLibraryResult result, { + IsExpectedError? isExpectedError, +}) { + isExpectedError ??= (_) => false; + + const sharedMessage = 'If analysis errors are expected for this test, either:' + '\n1. specify `isExpectedError` with a function that returns true' + ' only for your expected error' + '\n2. use an `ignore:` comment to silence them' + '\n3. set `throwOnAnalysisErrors: false`,' + ' and use `checkResolvedResultForErrors` with `isExpectedError`' + ' to verify that only the expected errors are present.'; + + if (result is! ResolvedLibraryResult) { + throw ArgumentError(['Error resolving file; result was $result.', sharedMessage].join(' ')); + } + + final unexpectedErrors = result.units + .expand((unit) => unit.errors) + .where((error) => + error.severity == Severity.error || + const { + 'unused_element', + 'unused_local_variable', + }.contains(error.errorCode.name.toLowerCase())) + // We need a non-null-assertion here due to https://github.com/dart-lang/sdk/issues/40790 + .where((error) => !isExpectedError!(error)) + .toList(); + if (unexpectedErrors.isNotEmpty) { + throw ArgumentError([ + // ignore: no_adjacent_strings_in_list + 'File had analysis errors or unused element hints,' + ' which likely indicate that the test file is set up improperly,' + ' potentially resulting in false positives in your test.', + sharedMessage, + 'Errors:\n${prettyPrintErrors(unexpectedErrors)}.' + ].join(' ')); + } +} + +extension FileSystemDeleteIfExistExtension on FileSystemEntity { + void deleteSyncIfExists({bool recursive = false}) { + if (existsSync()) { + deleteSync(recursive: recursive); + } + } + + Future deleteIfExists({bool recursive = false}) async { + if (existsSync()) { + await delete(recursive: recursive); + } + } +} diff --git a/tools/analyzer_plugin/test/util/util.dart b/tools/analyzer_plugin/test/util/util.dart new file mode 100644 index 000000000..32426c367 --- /dev/null +++ b/tools/analyzer_plugin/test/util/util.dart @@ -0,0 +1,21 @@ +// From https://github.com/Workiva/over_react_codemod/blob/a14a4ef372a1b2c2a625755cec7a6f956a074650/lib/src/util.dart +import 'package:analyzer/error/error.dart'; +import 'package:analyzer/source/line_info.dart'; + +String prettyPrintErrors(Iterable errors, + {LineInfo? lineInfo, bool includeFilename = true}) { + return errors.map((e) { + final severity = e.errorCode.errorSeverity.name.toLowerCase(); + final errorCode = e.errorCode.name.toLowerCase(); + final location = + lineInfo?.getLocation(e.offset).toString() ?? 'offset ${e.offset}'; + final filename = e.source.shortName; + + return " - [$severity] ${e.message} ($errorCode at${includeFilename ? ' $filename' : ''} $location)"; + }).join('\n'); +} + +String blockComment(String contents) => '/*$contents*/'; + +String lineComment(String contents) => + contents.split('\n').map((line) => '// $line\n').join('');