From 18d6877491ebe9859b25bc04607d3958ccf20f1f Mon Sep 17 00:00:00 2001 From: Sourcegraph Date: Wed, 30 Aug 2023 22:10:22 +0000 Subject: [PATCH 01/45] "Update dev dependencies for analyzer 2" --- app/over_react_redux/todo_client/pubspec.yaml | 2 +- pubspec.yaml | 16 ++++++++-------- tools/analyzer_plugin/playground/pubspec.yaml | 4 ++-- tools/analyzer_plugin/pubspec.yaml | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) 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/pubspec.yaml b/pubspec.yaml index b73bbf060..9e0ebf7ab 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: ^2.0.0 build: '>=1.0.0 <3.0.0' built_redux: ^8.0.0 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,12 +28,12 @@ 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 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..fd373853e 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -23,7 +23,7 @@ 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 From bdc3467e695bf3ed23baac1da6d09bbd2bcdd36c Mon Sep 17 00:00:00 2001 From: Rob Becker Date: Wed, 30 Aug 2023 22:15:44 -0600 Subject: [PATCH 02/45] pub get and format --- lib/src/builder/codegen/accessors_generator.dart | 4 +++- lib/src/builder/codegen/component_factory_generator.dart | 4 +++- lib/src/builder/codegen/typed_map_impl_generator.dart | 8 ++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index 2849191e7..2beb935e4 100644 --- a/lib/src/builder/codegen/accessors_generator.dart +++ b/lib/src/builder/codegen/accessors_generator.dart @@ -363,7 +363,9 @@ class _TypedMapMixinAccessorsGenerator extends TypedMapAccessorsGenerator { @override void generate() { - outputContentsBuffer..write(_generateAccessorsMixin())..write(_generateMetaConstImpl()); + outputContentsBuffer + ..write(_generateAccessorsMixin()) + ..write(_generateMetaConstImpl()); } } 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..b7d2883fe 100644 --- a/lib/src/builder/codegen/typed_map_impl_generator.dart +++ b/lib/src/builder/codegen/typed_map_impl_generator.dart @@ -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 From a7a456f69783d8ae0b1fb33bc21b89c9b77773d1 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Tue, 5 Sep 2023 12:21:10 -0700 Subject: [PATCH 03/45] Upgrade main package to analyzer 5 --- .../builder/codegen/accessors_generator.dart | 11 ++-- .../codegen/typed_map_impl_generator.dart | 4 +- lib/src/builder/codegen/util.dart | 5 +- lib/src/builder/parsing/ast_util.dart | 13 ++++- .../builder/parsing/ast_util/classish.dart | 54 +++++++++---------- lib/src/builder/parsing/declarations.dart | 12 ++--- lib/src/builder/parsing/members.dart | 4 +- .../builder/parsing/members/component.dart | 7 +-- lib/src/builder/parsing/members/factory.dart | 2 +- .../parsing/members/props_and_state.dart | 8 ++- .../members/props_and_state_mixins.dart | 18 +++---- .../parsing/members/props_and_state_util.dart | 2 +- lib/src/builder/parsing/members_from_ast.dart | 12 ++--- lib/src/builder/util.dart | 5 +- pubspec.yaml | 8 ++- .../builder/parsing/ast_util_test.dart | 14 ++--- .../builder/parsing/error_collection.dart | 2 +- .../builder/parsing/members_test.dart | 6 +-- 18 files changed, 97 insertions(+), 90 deletions(-) diff --git a/lib/src/builder/codegen/accessors_generator.dart b/lib/src/builder/codegen/accessors_generator.dart index 2beb935e4..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; @@ -424,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; @@ -464,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 @@ -495,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/typed_map_impl_generator.dart b/lib/src/builder/codegen/typed_map_impl_generator.dart index b7d2883fe..6b0c1ec2d 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) { @@ -354,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), 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/pubspec.yaml b/pubspec.yaml index 9e0ebf7ab..adf424636 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,9 +7,9 @@ environment: dependencies: collection: ^1.14.11 - analyzer: ^2.0.0 + analyzer: ^5.0.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: ^2.0.0 js: ^0.6.1+1 @@ -36,9 +36,7 @@ dev_dependencies: 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/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.dart index 2426293db..3fdadc6e0 100644 --- a/test/vm_tests/builder/parsing/error_collection.dart +++ b/test/vm_tests/builder/parsing/error_collection.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']), ]); From 35243a271ad57c35e9dfe75222a1c009818de5b3 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Tue, 5 Sep 2023 12:23:18 -0700 Subject: [PATCH 04/45] Upgrade main package to analyzer 5 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index adf424636..1df00e0cb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ environment: dependencies: collection: ^1.14.11 - analyzer: ^5.0.0 + analyzer: ^5.1.0 build: '>=1.0.0 <3.0.0' built_redux: ^8.0.6 built_value: ^8.0.0 From 474bb3ce064885f7b039a58c2343315f5f1d49f0 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Tue, 5 Sep 2023 12:27:58 -0700 Subject: [PATCH 05/45] Fix test file not getting run due to it missing the _test suffix --- .../parsing/{error_collection.dart => error_collection_test.dart} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/vm_tests/builder/parsing/{error_collection.dart => error_collection_test.dart} (100%) diff --git a/test/vm_tests/builder/parsing/error_collection.dart b/test/vm_tests/builder/parsing/error_collection_test.dart similarity index 100% rename from test/vm_tests/builder/parsing/error_collection.dart rename to test/vm_tests/builder/parsing/error_collection_test.dart From b366b4f24cb9787476c0d875486c844e4fda6af1 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Tue, 5 Sep 2023 12:41:26 -0700 Subject: [PATCH 06/45] Update generated code --- test/mockito.mocks.dart | 3 ++- .../redux_component_test/test_reducer.g.dart | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) 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_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 From e82647ad584253f601c4e50a040b4bbdafca80e2 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Tue, 5 Sep 2023 12:42:30 -0700 Subject: [PATCH 07/45] Update CI analyzer matrix, run on 2.19, stop running on 2.13.4 --- .github/workflows/dart_ci.yml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) 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 From 155e40dcd363bc7c9f12bde7fadaa5c9aa781c19 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Tue, 5 Sep 2023 13:35:53 -0700 Subject: [PATCH 08/45] Update generated comment placement to smooth over formatting differences in CI This comment used to go at the end of the line, b but that caused formatting difference between dart_style 2.2.5 and 2.3.2 when the name was prefixed, causing CI failures due to the output sometimes being different, so we'll put it on the previous line for now as an easy fix. --- lib/src/builder/codegen/typed_map_impl_generator.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/src/builder/codegen/typed_map_impl_generator.dart b/lib/src/builder/codegen/typed_map_impl_generator.dart index 6b0c1ec2d..f0691bed3 100644 --- a/lib/src/builder/codegen/typed_map_impl_generator.dart +++ b/lib/src/builder/codegen/typed_map_impl_generator.dart @@ -438,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' @@ -466,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)); } } From c8e8e5a91fcd0b36bfc06b31f1799c73d05d5ee8 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Tue, 5 Sep 2023 13:37:14 -0700 Subject: [PATCH 09/45] Update generated files with new comment placements --- ...bstract_transition_props.over_react.g.dart | 4 +- .../error_boundary.over_react.g.dart | 8 ++-- ...ror_boundary_recoverable.over_react.g.dart | 6 ++- .../component/resize_sensor.over_react.g.dart | 4 +- .../suspense_component.over_react.g.dart | 3 +- .../with_transition.over_react.g.dart | 10 +++-- ...fe_render_manager_helper.over_react.g.dart | 8 ++-- ...abstract_transition_test.over_react.g.dart | 9 +++-- .../element_type_test.over_react.g.dart | 8 ++-- .../shared_stack_tests.over_react.g.dart | 12 +++--- .../lazy_load_me_component.over_react.g.dart | 3 +- .../lazy_load_me_props.over_react.g.dart | 4 +- .../pure_test_components.over_react.g.dart | 13 +++--- .../component/memo_test.over_react.g.dart | 4 +- .../component/ref_util_test.over_react.g.dart | 11 ++--- .../with_transition_test.over_react.g.dart | 6 ++- .../components.over_react.g.dart | 40 +++++++++---------- .../cast_ui_factory_test.over_react.g.dart | 4 +- ...omponent_debug_name_test.over_react.g.dart | 4 +- .../util/js_component_test.over_react.g.dart | 6 ++- .../prop_conversion_test.over_react.g.dart | 30 +++++++------- .../fixtures/counter_fn.over_react.g.dart | 16 +++++--- .../hooks/use_dispatch_test.over_react.g.dart | 8 ++-- .../hooks/use_store_test.over_react.g.dart | 8 ++-- .../store_bindings_tests.over_react.g.dart | 10 +++-- 25 files changed, 132 insertions(+), 107 deletions(-) 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/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/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._(); From 780119a8b1dcb23b3ecc60e0b07b514b749059ce Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Tue, 5 Sep 2023 13:37:36 -0700 Subject: [PATCH 10/45] Update gold files with new comment placements --- .../basic.over_react.g.dart.goldFile | 4 +- .../basic_library.over_react.g.dart.goldFile | 18 ++++--- .../basic_two_nine.over_react.g.dart.goldFile | 4 +- ...tiple_factories.over_react.g.dart.goldfile | 6 ++- ...type_parameters.over_react.g.dart.goldFile | 51 ++++++++++--------- 5 files changed, 46 insertions(+), 37 deletions(-) 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._(); From f2ffd7dd0c89248531918129f40f6f87cbf19627 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Tue, 5 Sep 2023 13:44:46 -0700 Subject: [PATCH 11/45] Update Dockerfile Dart version --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 1dce39aee6cd4e3994d43d878dc68e19499f7a7a Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Wed, 6 Sep 2023 08:00:00 -0700 Subject: [PATCH 12/45] Upgrade analyzer and analyzer_plugin, first stab at fixing ServerPlugin breakages --- .../lib/src/async_plugin_apis/diagnostic.dart | 18 ++- tools/analyzer_plugin/lib/src/plugin.dart | 135 +++--------------- tools/analyzer_plugin/pubspec.yaml | 13 +- 3 files changed, 34 insertions(+), 132 deletions(-) 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..08f0d1f2b 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'; @@ -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/plugin.dart b/tools/analyzer_plugin/lib/src/plugin.dart index 33af9615e..316444ad7 100644 --- a/tools/analyzer_plugin/lib/src/plugin.dart +++ b/tools/analyzer_plugin/lib/src/plugin.dart @@ -31,20 +31,13 @@ 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'; @@ -81,7 +74,6 @@ 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 @@ -91,7 +83,7 @@ abstract class OverReactAnalyzerPluginBase extends ServerPlugin DartNavigationMixin, AsyncAssistsMixin, AsyncDartAssistsMixin { - OverReactAnalyzerPluginBase(ResourceProvider provider) : super(provider); + OverReactAnalyzerPluginBase(ResourceProvider provider) : super(resourceProvider: provider); @override final pluginOptionsReader = PluginOptionsReader(); @@ -120,28 +112,11 @@ 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 +158,29 @@ 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'; @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); + Future analyzeFile({required AnalysisContext analysisContext, required String path}) async { + 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 OverReactAnalyzerPluginBase { + OverReactAnalyzerPlugin(ResourceProvider provider) : super(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/pubspec.yaml b/tools/analyzer_plugin/pubspec.yaml index fd373853e..24e479513 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -6,15 +6,18 @@ 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 + analyzer: ^5.1.0 # Can't be 0.7.0 since its results in errors around JenkinsSmiHash not existing - analyzer_plugin: ^0.8.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 # so that it always resolves to the same version of over_react that the user has pulled in, # and thus has the same boilerplate parsing code that's running in the builder. - over_react: 4.10.0 +# over_react: 4.10.0 + + over_react: + path: /Users/greglittlefield/workspaces/over_react2 path: ^1.5.1 source_span: ^1.7.0 yaml: ^3.0.0 @@ -52,6 +55,6 @@ workiva: # # In CI, this should also be the path to the over_react checkout # -# dependency_overrides: +#dependency_overrides: # over_react: -# path: /Users/me/workspaces/over_react +# path: /Users/greglittlefield/workspaces/over_react From 6a852c4f8b2999447dd7512677d934bdc8e26417 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Wed, 6 Sep 2023 11:03:28 -0700 Subject: [PATCH 13/45] Address analyzer breakages --- .../convert_class_or_function_component.dart | 12 +++--- .../lib/src/assist/refs/add_create_ref.dart | 2 +- .../lib/src/component_usage.dart | 2 +- .../bool_prop_name_readability.dart | 42 ++++++++++--------- .../consumed_props_return_value.dart | 2 +- .../lib/src/diagnostic/exhaustive_deps.dart | 10 ++--- .../src/diagnostic/missing_required_prop.dart | 2 +- .../src/diagnostic/non_defaulted_prop.dart | 2 +- .../diagnostic/pseudo_static_lifecycle.dart | 16 +++---- .../src/diagnostic/render_return_value.dart | 2 +- .../variadic_children_with_keys.dart | 2 +- .../visitors/proptypes_visitors.dart | 2 +- .../lib/src/util/ast_util.dart | 7 +--- .../lib/src/util/function_components.dart | 3 +- tools/analyzer_plugin/lib/src/util/hooks.dart | 2 +- .../lib/src/util/ignore_info.dart | 5 +-- .../test/unit/util/ast_util_test.dart | 12 +++--- 17 files changed, 62 insertions(+), 63 deletions(-) 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/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/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..a31910b87 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart @@ -1080,7 +1080,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. @@ -1335,7 +1335,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, @@ -1658,7 +1658,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 +1666,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 +1922,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/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/util/ast_util.dart b/tools/analyzer_plugin/lib/src/util/ast_util.dart index eea0ad18c..907b69906 100644 --- a/tools/analyzer_plugin/lib/src/util/ast_util.dart +++ b/tools/analyzer_plugin/lib/src/util/ast_util.dart @@ -132,11 +132,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 +328,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..8e621d41a 100644 --- a/tools/analyzer_plugin/lib/src/util/ignore_info.dart +++ b/tools/analyzer_plugin/lib/src/util/ignore_info.dart @@ -103,13 +103,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/test/unit/util/ast_util_test.dart b/tools/analyzer_plugin/test/unit/util/ast_util_test.dart index 07b698611..0dbb183b4 100644 --- a/tools/analyzer_plugin/test/unit/util/ast_util_test.dart +++ b/tools/analyzer_plugin/test/unit/util/ast_util_test.dart @@ -81,7 +81,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 +180,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 +193,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 +207,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 +245,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 +257,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', () { From 946cf1ff775945ea7bffceed73ed8d7b26ddab46 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Wed, 6 Sep 2023 11:04:55 -0700 Subject: [PATCH 14/45] Address analyzer breakages in boilerplate_validator This code doesn't currently run, so this implementation may not be correct. --- .../src/diagnostic/boilerplate_validator.dart | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) 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({ From ea6bf32f4acc22f56f6a7fd411ab5c762d0d05c4 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Wed, 6 Sep 2023 14:41:48 -0700 Subject: [PATCH 15/45] Refactor test setup in response to analyzer_plugin changes --- .gitignore | 2 + tools/analyzer_plugin/analysis_options.yaml | 1 + tools/analyzer_plugin/pubspec.yaml | 1 + .../diagnostics/exhaustive_deps_test.dart | 2 +- .../test/integration/stubs.dart | 8 +- .../test_bases/analysis_driver_test_base.dart | 148 ++---- .../test_bases/assist_test_base.dart | 2 +- .../server_plugin_contributor_test_base.dart | 5 +- .../over_react_project/README.md | 3 + .../over_react_project/analysis_options.yaml | 1 + .../lib/analysis_warmup.dart | 8 + .../over_react_project/pubspec.yaml | 6 + tools/analyzer_plugin/test/test_util.dart | 8 +- .../test/util/package_util.dart | 137 ++++++ .../test/util/shared_analysis_context.dart | 453 ++++++++++++++++++ tools/analyzer_plugin/test/util/util.dart | 21 + 16 files changed, 685 insertions(+), 121 deletions(-) create mode 100644 tools/analyzer_plugin/test/test_fixtures/over_react_project/README.md create mode 100644 tools/analyzer_plugin/test/test_fixtures/over_react_project/analysis_options.yaml create mode 100644 tools/analyzer_plugin/test/test_fixtures/over_react_project/lib/analysis_warmup.dart create mode 100644 tools/analyzer_plugin/test/test_fixtures/over_react_project/pubspec.yaml create mode 100644 tools/analyzer_plugin/test/util/package_util.dart create mode 100644 tools/analyzer_plugin/test/util/shared_analysis_context.dart create mode 100644 tools/analyzer_plugin/test/util/util.dart diff --git a/.gitignore b/.gitignore index c70d3dfb0..47373ca07 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,5 @@ pubspec.lock # Should not track the output of code coverage for now coverage/ + +tools/analyzer_plugin/test/test_fixtures/over_react_project/lib/dynamic_test_files/ diff --git a/tools/analyzer_plugin/analysis_options.yaml b/tools/analyzer_plugin/analysis_options.yaml index c1040a326..ed45147b1 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 diff --git a/tools/analyzer_plugin/pubspec.yaml b/tools/analyzer_plugin/pubspec.yaml index 24e479513..ea454e566 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -37,6 +37,7 @@ dev_dependencies: pub_semver: ^2.0.0 test: ^1.14.0 test_reflective_loader: ^0.2.0 + uuid: ^3.0.5 workiva_analysis_options: ^1.1.0 dependency_validator: 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..5fac9832f 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart @@ -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/stubs.dart b/tools/analyzer_plugin/test/integration/stubs.dart index 8b15333b7..e7cda6262 100644 --- a/tools/analyzer_plugin/test/integration/stubs.dart +++ b/tools/analyzer_plugin/test/integration/stubs.dart @@ -3,7 +3,6 @@ import 'package:analyzer/src/dart/analysis/driver.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/plugin.dart'; import 'test_bases/assist_test_base.dart'; @@ -26,13 +25,8 @@ class StubChannel implements PluginCommunicationChannel { /// 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; - - PluginForTest(this.testDriver, ResourceProvider resourceProvider) : super(resourceProvider); + PluginForTest(ResourceProvider resourceProvider) : super(resourceProvider); @override String get name => 'over_react (for test)'; - - @override - AnalysisDriverGeneric createAnalysisDriver(ContextRoot contextRoot) => testDriver; } 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..14b1f9255 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,23 +1,9 @@ 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: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 'assist_test_base.dart'; @@ -31,30 +17,9 @@ 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; + PhysicalResourceProvider get resourceProvider => _resourceProvider!; + PhysicalResourceProvider? _resourceProvider; /// Absolute path to the in-memory folder within which all test sources are /// created. @@ -71,94 +36,63 @@ abstract class AnalysisDriverTestBase { /// /// [path] must be relative; the returned source will be created within /// [testPath]. - Source newSource(String path, [String? contents]) { + 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 ?? ''); + final file = resourceProvider.getFile(absolutePath); + file.writeAsStringSync(contents); return file.createSource(); } + void modifyFile(String path, String contents) { + resourceProvider.getFile(path).writeAsStringSync(contents); + } + /// Returns the absolute path for [source]. - String sourcePath(Source source) => resourceProvider.convertPath(source.uri.path); + String sourcePath(Source source) => source.uri.toFilePath(); + + static const testPathBase = '/Users/greglittlefield/workspaces/over_react2/tools/analyzer_plugin/'; @mustCallSuper Future setUp() async { - // Based on https://github.com/dart-lang/angular/blob/832c8df2c54415e8b6e68886cd3d8c293ef9874c/angular_analyzer_plugin/test/analyzer_base.dart#L62-L89 - - _resourceProvider = MemoryResourceProvider(); - - final sdk = MockSdk(resourceProvider: resourceProvider); - final packageMap = { - for (final packageName in _realPackagesToCopyIntoResourceProvider) - packageName: [await _loadRealPackage(packageName, resourceProvider)], - }; + _resourceProvider = PhysicalResourceProvider(); // Setup a testing directory. All calls to [newSource] will create files // within this directory. - _testPath = resourceProvider.newFolder('/test').path; - - // 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); + _testPath = Directory(testPathBase).createTempSync().path; + + // If analysis_options.yaml isn't set up here, `AnalysisContext.optionsFile` will be null even if the file is added later, + // and also Dart will try to load analysis_options.yaml from an ancestor directory. + resourceProvider + .getFile(p.join(testPath, 'analysis_options.yaml')) + .writeAsStringSync(analysisOptionsYamlContents ?? ''); + + final testPackageName = 'test_package_' + p.basename(_testPath!).replaceAll(RegExp(r'[^a-zA-Z0-9_]'), ''); + const nullSafety = false; + resourceProvider.getFile(p.join(testPath, 'pubspec.yaml')).writeAsStringSync(''' +name: $testPackageName +environment: + sdk: ${nullSafety ? '">=2.12.0 <3.0.0"' : '">=2.10.0 <3.0.0"'} +dependencies: + over_react: + path: /Users/greglittlefield/workspaces/over_react2 +'''); + + // TODO optimize this: running `pub get` before each test is pretty slow. + final pubGetResult = + await Process.run('dart', ['pub', 'get', '--offline'], runInShell: true, workingDirectory: testPath); + if (pubGetResult.exitCode != 0) { + throw Exception('pub get failed with exit code ${pubGetResult.exitCode}.' + '\n${pubGetResult.stdout}${pubGetResult.stderr}'); } - - // 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); } @mustCallSuper void tearDown() { - _analysisDriver?.dispose(); - _analysisDriver = null; _resourceProvider = null; + if (_testPath != null) { + Directory(_testPath!).deleteSync(recursive: true); + } _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')); - } } 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..9dacd8e38 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 @@ -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/server_plugin_contributor_test_base.dart b/tools/analyzer_plugin/test/integration/test_bases/server_plugin_contributor_test_base.dart index aa73bd29d..ad83a41cd 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 @@ -74,7 +74,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,7 +150,7 @@ abstract class ServerPluginContributorTestBase extends AnalysisDriverTestBase { await super.setUp(); _channel = StubChannel(); - _plugin = PluginForTest(analysisDriver, resourceProvider)..start(_channel!); + _plugin = PluginForTest(resourceProvider)..start(_channel!); // ignore: missing_required_param final contextRoot = ContextRoot(testPath, []); 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..4905f86d9 100644 --- a/tools/analyzer_plugin/test/test_util.dart +++ b/tools/analyzer_plugin/test/test_util.dart @@ -15,6 +15,8 @@ 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) { final result = parseString( @@ -70,9 +72,9 @@ 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; +Future parseAndGetResolvedUnit(String dartSource, {String? path}) async { + final context = await SharedAnalysisContext.overReact.resolvedFileContextForTest(dartSource, filename: path); + return await context.getResolvedUnit() as ResolvedUnitResult; } // Reuse an analysis context across multiple calls, 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..27bb841f6 --- /dev/null +++ b/tools/analyzer_plugin/test/util/package_util.dart @@ -0,0 +1,137 @@ +// 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. +Future runPubGetIfNeeded(String packageRoot) async { + if (_isPubGetNecessary(packageRoot)) { + await 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. +Future runPubGet(String workingDirectory) async { + _logger.info('Running `pub get` in `$workingDirectory`...'); + + final process = await Process.start('pub', ['get'], + workingDirectory: workingDirectory, + runInShell: true, + mode: ProcessStartMode.inheritStdio); + final exitCode = await process.exitCode; + + if (exitCode == 69) { + _logger.info( + 'Re-running `pub get` but with `--offline`, to hopefully fix the above error.'); + final process = await Process.start('pub', ['get', '--offline'], + workingDirectory: workingDirectory, + runInShell: true, + mode: ProcessStartMode.inheritStdio); + final exitCode = await process.exitCode; + if (exitCode != 0) { + throw Exception('pub get failed with exit code: $exitCode'); + } + } else if (exitCode != 0) { + throw Exception('pub get failed with exit code: $exitCode'); + } +} + +/// 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/shared_analysis_context.dart b/tools/analyzer_plugin/test/util/shared_analysis_context.dart new file mode 100644 index 000000000..2d717d220 --- /dev/null +++ b/tools/analyzer_plugin/test/util/shared_analysis_context.dart @@ -0,0 +1,453 @@ +// 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:async/async.dart'; +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/error/error.dart'; +import 'package:path/path.dart' as p; +import 'package:source_span/source_span.dart'; + +// This isn't strictly required for anything, so if the import becomes invalid, +// just comment it and related code out. +import 'package:test_api/src/backend/invoker.dart' show Invoker; +import 'package:uuid/uuid.dart'; + +import 'package_util.dart'; +import 'util.dart'; + +/// Provides a mechanism for getting resolved codemod [FileContext]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')); + + /// The path to the package root in which test files will be created + /// and resolved. + final String _path; + + /// The analysis context collection for files within [_path], initialized + /// lazily. + late final AnalysisContextCollection collection; + + /// 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(this._path, {this.customPubGetErrorMessage}) { + if (!p.isAbsolute(_path)) { + throw ArgumentError.value(_path, 'projectRoot', 'must be absolute'); + } + } + + // This ensures the _initIfNeeded logic is only ever run once, and prevents + // race conditions if there are concurrent calls to it. + final _initMemo = AsyncMemoizer(); + + Future _initIfNeeded() => _initMemo.runOnce(() async { + // 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 { + await runPubGetIfNeeded(_path); + } 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'); + } + + collection = AnalysisContextCollection( + includedPaths: [_path], + ); + }); + + /// 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 { + await _initIfNeeded(); + final path = p.join(_path, 'lib/analysis_warmup.dart'); + await collection.contextFor(path).currentSession.getResolvedLibrary(path); + _shouldPrintFirstFileWarning = false; + } + // + // /// A convenience method that creates a codemod [FileContext], + // /// run [suggestor] on it, and returns the patches yielded. + // /// + // /// Most arguments are forwarded to [resolvedFileContextForTest]; + // /// see that method for more details. + // Future> getPatches( + // Suggestor suggestor, + // String sourceText, { + // String? filename, + // bool preResolveLibrary = true, + // bool throwOnAnalysisErrors = true, + // }) async { + // final context = await resolvedFileContextForTest( + // sourceText, + // preResolveLibrary: preResolveLibrary, + // throwOnAnalysisErrors: throwOnAnalysisErrors, + // filename: filename, + // ); + // return await suggestor(context).toList(); + // } + + /// 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. + /// + /// If [preResolveLibrary] is `true`, then the file will be resolved as a library + /// and checked for errors (if [throwOnAnalysisErrors] is `true`) to help + /// validate that there are no issues resolving the test file, which are likely + /// the result of a bad test file that could result in false positives or + /// confusing errors in your test. + /// + /// If you expect analysis errors in your test file, provide a [isExpectedError] + /// so that those errors can be ignored while others can be filtered out. + Future resolvedFileContextForTest( + String sourceText, { + String? filename, + bool includeTestDescription = true, + bool preResolveLibrary = true, + bool throwOnAnalysisErrors = true, + IsExpectedError? isExpectedError, + }) async { + await _initIfNeeded(); + + filename ??= nextFilename(); + + if (includeTestDescription) { + // For convenience, include the current test description in the file as + // a comment, so that: + // - you can tell you're looking at the right file for a given test + // - you can search for the test description to easily find the right file + try { + final testName = Invoker.current!.liveTest.test.name; + sourceText = + lineComment('Created within test with name:\n> $testName') + + '\n' + + sourceText; + } catch (_) {} + } + + final path = p.join(_path, _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); + + final context = collection.contexts + .singleWhere((c) => c.contextRoot.root.path == _path); + + if (throwOnAnalysisErrors && !preResolveLibrary) { + throw ArgumentError( + 'If throwOnAnalysisErrors is true, preResolveFile must be false'); + } + if (isExpectedError != null && !throwOnAnalysisErrors) { + throw ArgumentError( + 'If isExpectedError is provided, throwOnAnalysisErrors must be true'); + } + if (preResolveLibrary) { + final result = await _printAboutFirstFile( + () => context.currentSession.getResolvedLibrary(path)); + if (throwOnAnalysisErrors) { + checkResolvedResultForErrors(result, isExpectedError: isExpectedError); + } + } + + // 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 FileContext(path, collection, root: _path); + } + + int _fileNameCounter = 0; + + /// Returns a filename that hasn't been used before. + String nextFilename() => 'test_${_fileNameCounter++}.dart'; + + bool _shouldPrintFirstFileWarning = true; + + /// We can't intelligently warn only when this is taking too long since + /// getResolvedLibrary2 blocks the main thread for a long period of time, + /// making it so that timers don't fire until it's done. + /// So, we'll just always print this. + Future _printAboutFirstFile(Future Function() callback) async { + var shouldPrint = false; + if (_shouldPrintFirstFileWarning) { + _shouldPrintFirstFileWarning = false; + shouldPrint = true; + } + + if (shouldPrint) { + final contextName = p.basename(_path); + print('Resolving a file for the first time in context "$contextName";' + ' this will take a few seconds...'); + } + final result = await callback(); + if (shouldPrint) { + print('Done resolving.'); + } + return result; + } +} + +/// 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); + } + } +} + +extension ParseHelpers on SharedAnalysisContext { + /// Returns [expression] parsed as AST. + /// + /// This is accomplished it by including the [expression] as a statement within a wrapper function + /// with any necessary [imports] at the top of the source. As a result, the offset of the + /// returned expression will not be 0. + /// + /// To return resolved AST, set [isResolved] to true. + Future parseExpression( + String expression, { + String imports = '', + String otherSource = '', + bool isResolved = false, + }) async { + CompilationUnit unit; + // Wrap the expression in parens to ensure this is interpreted as an expression + // for ambiguous cases (e.g, a map literal that could be interpreted as an empty block). + final source = ''' + $imports + void wrapperFunction() { + ($expression); + } + $otherSource + '''; + final fileContext = await resolvedFileContextForTest(source, + // We don't want to get the resolved unit if `isResolve = false`, + // since it may fail. + preResolveLibrary: false, + throwOnAnalysisErrors: false); + if (isResolved) { + final result = await fileContext.getResolvedUnit(); + unit = (result as ResolvedUnitResult).unit; + } else { + unit = fileContext.getUnresolvedUnit(); + } + final parsedFunction = unit.childEntities + .whereType() + .singleWhere((function) => function.name.lexeme == 'wrapperFunction'); + final body = parsedFunction.functionExpression.body as BlockFunctionBody; + final statement = body.block.statements.single as ExpressionStatement; + return (statement.expression as ParenthesizedExpression).expression; + } +} + + +/// A helper class for a file located at [path] that provides access to its +/// contents and analyzed formats like [CompilationUnit] and [LibraryElement]. +class FileContext { + final AnalysisContextCollection _analysisContextCollection; + + /// This file's absolute path. + final String path; + + /// This file's path relative to [root]. + final String relativePath; + + /// The path to the working directory from which this file was discovered. + /// + /// Defaults to current working directory. + final String root; + + FileContext(this.path, this._analysisContextCollection, {String? root}) + : root = root ?? p.current, + relativePath = p.relative(path, from: root) { + if (!p.isAbsolute(path)) { + throw ArgumentError.value(path, 'path', 'must be absolute.'); + } + } + + /// A representation of this file that makes it easy to reference spans of + /// text, which is useful for the creation of [SourcePatch]es. + late final SourceFile sourceFile = + SourceFile.fromString(sourceText, url: Uri.file(path)); + + /// The contents of this file. + late final String sourceText = File(path).readAsStringSync(); + + /// Uses the analyzer to resolve and return the library result for this file, + /// which includes the [LibraryElement]. + Future getResolvedLibrary() async { + final result = await _analysisContextCollection + .contextFor(path) + .currentSession + .getResolvedLibrary(path); + return result is ResolvedLibraryResult ? result : null; + } + + /// Uses the analyzer to resolve and return the AST result for this file, + /// which includes the [CompilationUnit]. + /// + /// If the fully resolved AST is not needed, use the much faster + /// [getUnresolvedUnit]. + Future getResolvedUnit() async { + return _analysisContextCollection + .contextFor(path) + .currentSession + .getResolvedUnit(path); + } + + /// Returns the unresolved AST for this file. + /// + /// If the fully resolved AST is needed, use [getResolvedUnit]. + CompilationUnit getUnresolvedUnit() { + final result = + parseString(content: sourceText, path: path, throwIfDiagnostics: false); + if (result.errors.isEmpty) return result.unit; + + // Errors thrown by parseString don't include the filename, and result in + // the codemod halting without indicating which file it failed on. + // To aid in debugging, we'll construct the error message the same way + // parseString does, but also include the path to the file. + var buffer = StringBuffer(); + for (final error in result.errors) { + var location = result.lineInfo.getLocation(error.offset); + buffer.writeln(' ${error.errorCode.name}: ${error.message} - ' + '${location.lineNumber}:${location.columnNumber}'); + } + throw ArgumentError( + 'File "$relativePath" produced diagnostics when parsed:\n$buffer'); + } +} 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(''); From 620db2f023abba1f416729c243f6e8cd0313fab0 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Wed, 6 Sep 2023 14:44:27 -0700 Subject: [PATCH 16/45] Fix bad usages of ClassElement now that mixins elements don't use it --- tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart | 4 ++-- .../src/diagnostic/visitors/non_static_reference_visitor.dart | 2 +- tools/analyzer_plugin/lib/src/util/react_types.dart | 2 +- .../analyzer_plugin/test/unit/util/component_usage_test.dart | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart b/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart index 43cc1f1f4..6ea263a76 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart @@ -186,9 +186,9 @@ class BadKeyDiagnostic extends ComponentUsageDiagnosticContributor { static bool inheritsToStringImplFromObject(Element element) => element - .tryCast() + .tryCast() ?.lookUpConcreteMethod('toString', element.library!) - ?.thisOrAncestorOfType() + ?.thisOrAncestorOfType() ?.thisType .isDartCoreObject ?? false; 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/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/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( From 2a299d9f75755378b636e2d078a041d23e136b5e Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Wed, 6 Sep 2023 16:14:00 -0700 Subject: [PATCH 17/45] Update plugin test setup to use shared analysis context Before this change, each test would spin up a new AnalysisContextCollection, which was much too slow --- tools/analyzer_plugin/lib/src/plugin.dart | 23 +- .../assists/add_create_ref_test.dart | 22 +- .../integration/assists/add_props_test.dart | 8 +- .../assists/toggle_statefulness_test.dart | 4 +- .../analysis_options_configuration_test.dart | 2 +- .../diagnostics/arrow_function_prop_test.dart | 12 +- .../integration/diagnostics/bad_key_test.dart | 2 +- .../boilerplate_validator_test.dart | 14 +- .../diagnostics/callback_ref_test.dart | 20 +- .../diagnostics/create_ref_usage_test.dart | 10 +- .../duplicate_prop_cascade_test.dart | 22 +- .../diagnostics/exhaustive_deps_test.dart | 5 +- ...d_only_dom_props_to_dom_builders_test.dart | 4 +- .../diagnostics/non_defaulted_prop_test.dart | 10 +- .../test/integration/stubs.dart | 204 +++++++++++++++++- .../test_bases/analysis_driver_test_base.dart | 69 ++---- .../test_bases/assist_test_base.dart | 2 +- .../test_bases/diagnostic_test_base.dart | 2 +- .../server_plugin_contributor_test_base.dart | 18 +- tools/analyzer_plugin/test/test_util.dart | 154 ------------- .../test/util/package_util.dart | 21 +- .../test/util/shared_analysis_context.dart | 139 ++++++------ 22 files changed, 389 insertions(+), 378 deletions(-) diff --git a/tools/analyzer_plugin/lib/src/plugin.dart b/tools/analyzer_plugin/lib/src/plugin.dart index 316444ad7..24906bab1 100644 --- a/tools/analyzer_plugin/lib/src/plugin.dart +++ b/tools/analyzer_plugin/lib/src/plugin.dart @@ -43,12 +43,14 @@ 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'; @@ -58,6 +60,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'; @@ -75,16 +78,15 @@ 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'; -abstract class OverReactAnalyzerPluginBase extends ServerPlugin - with +mixin OverReactAnalyzerPluginBase + on + ServerPlugin, // OutlineMixin, DartOutlineMixin, DiagnosticMixin, NavigationMixin, DartNavigationMixin, AsyncAssistsMixin, AsyncDartAssistsMixin { - OverReactAnalyzerPluginBase(ResourceProvider provider) : super(resourceProvider: provider); - @override final pluginOptionsReader = PluginOptionsReader(); @@ -159,8 +161,6 @@ abstract class OverReactAnalyzerPluginBase extends ServerPlugin // new ReactElementOutlineContributor(), // ]; - - @override Future analyzeFile({required AnalysisContext analysisContext, required String path}) async { await runZonedGuarded(() async { @@ -175,8 +175,15 @@ abstract class OverReactAnalyzerPluginBase extends ServerPlugin } /// Analyzer plugin for over_react. -class OverReactAnalyzerPlugin extends OverReactAnalyzerPluginBase { - OverReactAnalyzerPlugin(ResourceProvider provider) : super(provider); +class OverReactAnalyzerPlugin extends ServerPlugin + with + DiagnosticMixin, + NavigationMixin, + DartNavigationMixin, + AsyncAssistsMixin, + AsyncDartAssistsMixin, + OverReactAnalyzerPluginBase { + OverReactAnalyzerPlugin(ResourceProvider provider) : super(resourceProvider: provider); @override String get name => 'over_react'; 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..60edf7b10 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(null, '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(null, ''' 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(null, usageSourceWithinFnComponent(fixed: false)); var selection = createSelection(source, '(##'); await expectNoAssist(selection); } Future test_noAssistWithExistingRefInClassComponentDecl() async { - final source = newSource('test.dart', ''' + final source = newSource(null, ''' 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(null, 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(null, 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(null, 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(null, 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(null, 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(null, 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(null, 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..9c3fae77a 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(null, '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(null, 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(null, 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(null, ''' 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..b856948d9 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(null, 'var foo = true;'); final selection = createSelection(source, '#var foo = true;#'); await expectNoAssist(selection); } @@ -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(null, 'var foo = true;'); final selection = createSelection(source, '#var foo = true;#'); await expectNoAssist(selection); } 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..f5b4e5d78 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(null, 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..5b3b3827d 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(null, 'var foo = true;'); expect(await getAllErrors(source), isEmpty); } Future test_noErrorLastInCascade() async { - final source = newSource('test.dart', /*language=dart*/ r''' + final source = newSource(null, /*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(null, /*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(null, 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(null, 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(null, ''' 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..c1fd953a3 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(null, sourcePrefix + sourceFragment); static const sourcePrefix = /*language=dart*/ r''' import 'package:over_react/over_react.dart'; 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..7b6accdbd 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(null, 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(null, 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(null, 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(null, 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(null, 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(null, 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(null, 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..72f3b8ecb 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(null, usageSourceWithinFnComponent); await expectSingleErrorAt( createSelection(source, CallbackRefDiagnosticTest.selectionToFixBlockFnBodyRefAssignment)); } Future test_blockFnBodyRefAssignmentFix() async { - var source = newSource('test.dart', usageSourceWithinFnComponent); + var source = newSource(null, 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(null, usageSourceWithinFnComponentFixedBlockFnBodyRefAssignment); await expectSingleErrorAt(createSelection(source, CallbackRefDiagnosticTest.selectionToFixArrowFnRefAssignment)); } Future test_arrowFnRefAssignmentErrorFix() async { - var source = newSource('test.dart', usageSourceWithinFnComponentFixedBlockFnBodyRefAssignment); + var source = newSource(null, 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(null, 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(null, usageSourceWithinClassComponent); await expectSingleErrorAt( createSelection(source, CallbackRefDiagnosticTest.selectionToFixBlockFnBodyRefAssignment)); } Future test_blockFnBodyRefAssignmentFix() async { - var source = newSource('test.dart', usageSourceWithinClassComponent); + var source = newSource(null, 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(null, usageSourceWithinClassComponentFixedBlockFnBodyRefAssignment); await expectSingleErrorAt(createSelection(source, CallbackRefDiagnosticTest.selectionToFixArrowFnRefAssignment)); } Future test_arrowFnRefAssignmentErrorFix() async { - var source = newSource('test.dart', usageSourceWithinClassComponentFixedBlockFnBodyRefAssignment); + var source = newSource(null, 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(null, 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..cc9edaf38 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(null, /*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(null, /*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(null, /*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(null, /*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(null, 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..b1363a941 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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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 5fac9832f..90cfc5648 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart @@ -108,6 +108,7 @@ class ObjectWithWritableField { bool errorFilter(AnalysisError error, {@required bool isFromPlugin}) => defaultErrorFilter(error, isFromPlugin: isFromPlugin) && // These are intentionally undefined references + !{'unused_local_variable'}.contains(error.code) && !(error.code == 'undefined_identifier' && error.message.contains("Undefined name 'unresolved'.")); Future setUpTestBase(TestCase testCase) async { @@ -151,7 +152,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(null, preamble + testCase.code); await testBase.expectNoErrors(source, errorFilter: errorFilter); }); }); @@ -170,7 +171,7 @@ class ObjectWithWritableField { final expectedErrors = testCase.errors; expect(expectedErrors, isNotEmpty); - final source = testBase.newSource('test.dart', preamble + testCase.code); + final source = testBase.newSource(null, 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.' 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..b1e6c9442 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(null, 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(null, 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..ad99fbaac 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(null, /*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(null, /*language=dart*/ r''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; @@ -92,7 +92,7 @@ final Foo = uiFunction( _\$FooConfig, // ignore: undefined_identifier ); '''; - var source = newSource('test.dart', contents('props.content')); + var source = newSource(null, contents('props.content')); final selection = createSelection(source, "(#props.content#)"); // Verify error. @@ -124,7 +124,7 @@ final Foo = uiFunction( _\$FooConfig, // ignore: undefined_identifier ); '''; - var source = newSource('test.dart', contents('props.content')); + var source = newSource(null, contents('props.content')); final selection = createSelection(source, "(#props.content#)"); // Verify error. @@ -138,7 +138,7 @@ final Foo = uiFunction( } Future test_multipleErrorsAndFixes() async { - var source = newSource('test.dart', /*language=dart*/ r''' + var source = newSource(null, /*language=dart*/ r''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; diff --git a/tools/analyzer_plugin/test/integration/stubs.dart b/tools/analyzer_plugin/test/integration/stubs.dart index e7cda6262..657cbfcca 100644 --- a/tools/analyzer_plugin/test/integration/stubs.dart +++ b/tools/analyzer_plugin/test/integration/stubs.dart @@ -1,9 +1,19 @@ -import 'package:analyzer/file_system/file_system.dart'; +import 'package:analyzer/dart/analysis/analysis_context.dart'; +import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/file_system/overlay_file_system.dart'; +import 'package:analyzer/src/dart/analysis/byte_store.dart'; import 'package:analyzer/src/dart/analysis/driver.dart'; import 'package:analyzer_plugin/channel/channel.dart'; +import 'package:analyzer_plugin/plugin/navigation_mixin.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:analyzer_plugin/utilities/subscriptions/subscription_manager.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 'package:pub_semver/src/version.dart'; import 'test_bases/assist_test_base.dart'; @@ -19,13 +29,201 @@ class StubChannel implements PluginCommunicationChannel { dynamic noSuchMethod(Invocation invocation) {} } +class StubServerPlugin implements ServerPlugin { + @override + PluginCommunicationChannel get channel => throw UnimplementedError(); + + @override + set channel(PluginCommunicationChannel value) => throw UnimplementedError(); + + @override + Set get priorityPaths => throw UnimplementedError(); + + @override + set priorityPaths(Set value) => throw UnimplementedError(); + + @override + Future afterNewContextCollection({required AnalysisContextCollection contextCollection}) => + 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 + Future beforeContextCollectionDispose({required AnalysisContextCollection contextCollection}) => + throw UnimplementedError(); + + + @override + String? get contactInfo => throw UnimplementedError(); + + @override + Future contentChanged(List paths) => throw UnimplementedError(); + + @override + ByteStore createByteStore() => throw UnimplementedError(); + + @override + List get fileGlobsToAnalyze => throw UnimplementedError(); + + @override + Future flushAnalysisState({bool elementModels = true}) => throw UnimplementedError(); + + @override + Future getResolvedUnitResult(String path) => throw UnimplementedError(); + + @override + Future handleAffectedFiles({required AnalysisContext analysisContext, required List paths}) => + throw UnimplementedError(); + + @override + Future handleAnalysisGetNavigation(AnalysisGetNavigationParams parameters) => + throw UnimplementedError(); + + @override + Future handleAnalysisHandleWatchEvents(AnalysisHandleWatchEventsParams parameters) => + throw UnimplementedError(); + + @override + Future handleAnalysisSetContextRoots(AnalysisSetContextRootsParams parameters) => + throw UnimplementedError(); + + @override + Future handleAnalysisSetPriorityFiles(AnalysisSetPriorityFilesParams parameters) => + throw UnimplementedError(); + + @override + Future handleAnalysisSetSubscriptions(AnalysisSetSubscriptionsParams parameters) => + throw UnimplementedError(); + + @override + Future handleAnalysisUpdateContent(AnalysisUpdateContentParams parameters) => + throw UnimplementedError(); + + @override + Future handleCompletionGetSuggestions(CompletionGetSuggestionsParams parameters) => + throw UnimplementedError(); + + @override + Future handleEditGetAssists(EditGetAssistsParams parameters) => throw UnimplementedError(); + + @override + Future handleEditGetAvailableRefactorings( + EditGetAvailableRefactoringsParams parameters) => + throw UnimplementedError(); + + @override + Future handleEditGetFixes(EditGetFixesParams parameters) => throw UnimplementedError(); + + @override + Future handleEditGetRefactoring(EditGetRefactoringParams parameters) => + throw UnimplementedError(); + + @override + Future handleKytheGetKytheEntries(KytheGetKytheEntriesParams parameters) => + throw UnimplementedError(); + + @override + Future handlePluginShutdown(PluginShutdownParams parameters) => throw UnimplementedError(); + + @override + Future handlePluginVersionCheck(PluginVersionCheckParams parameters) => + throw UnimplementedError(); + + @override + bool isCompatibleWith(Version serverVersion) => throw UnimplementedError(); + + @override + String get name => throw UnimplementedError(); + + @override + void onDone() {} + + @override + void onError(Object exception, StackTrace stackTrace) {} + + @override + OverlayResourceProvider get resourceProvider => throw UnimplementedError(); + + @override + Future sendFoldingNotification(String path) => throw UnimplementedError(); + + @override + Future sendHighlightsNotification(String path) => throw UnimplementedError(); + + @override + Future sendNavigationNotification(String path) => throw UnimplementedError(); + + @override + void sendNotificationsForFile(String path) {} + + @override + void sendNotificationsForSubscriptions(Map> subscriptions) {} + + @override + Future sendOccurrencesNotification(String path) => throw UnimplementedError(); + + @override + Future sendOutlineNotification(String path) => throw UnimplementedError(); + + @override + void start(PluginCommunicationChannel channel) => throw UnimplementedError(); + + @override + SubscriptionManager get subscriptionManager => throw UnimplementedError(); + + @override + String get version => throw UnimplementedError(); +} + /// A concrete [ServerPlugin] implementation designed for use in testing a /// single assist or diagnostic contributor against a real-ish [AnalysisDriver]. /// /// Tests should not use, extend, or implement this class directly. Instead, /// extend the contributor-specific test base class, like [AssistTestBase]. -class PluginForTest extends OverReactAnalyzerPluginBase { - PluginForTest(ResourceProvider resourceProvider) : super(resourceProvider); +class PluginForTest extends StubServerPlugin + with + DiagnosticMixin, + NavigationMixin, + DartNavigationMixin, + AsyncAssistsMixin, + AsyncDartAssistsMixin, + OverReactAnalyzerPluginBase { + PluginCommunicationChannel? _channel; + + @override + PluginCommunicationChannel get channel => _channel!; + + + + @override + void start(PluginCommunicationChannel channel) { + _channel = channel; + } + + @override + late OverlayResourceProvider resourceProvider; + + @override + Future analyzeFile({required AnalysisContext analysisContext, required String path}) => + throw UnimplementedError(); + + @override + Future analyzeFiles({required AnalysisContext analysisContext, required List paths}) => + throw UnimplementedError(); + + @override + Future contentChanged(List paths) => throw UnimplementedError(); + + @override + Future getResolvedUnitResult(String path) => handleGetResolvedUnitResult?.call(path) ?? (throw UnimplementedError()); + + Future Function(String path)? handleGetResolvedUnitResult; @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 14b1f9255..f1d084b1a 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,11 +1,9 @@ -import 'dart:io'; - -import 'package:analyzer/file_system/physical_file_system.dart'; +import 'package:analyzer/file_system/file_system.dart'; import 'package:analyzer/src/generated/source.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; -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 @@ -18,13 +16,8 @@ import 'assist_test_base.dart'; /// [AssistTestBase]. abstract class AnalysisDriverTestBase { /// Provider of all resources created during and needed for tests. - PhysicalResourceProvider get resourceProvider => _resourceProvider!; - PhysicalResourceProvider? _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. /// @@ -36,12 +29,14 @@ abstract class AnalysisDriverTestBase { /// /// [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.getFile(absolutePath); - file.writeAsStringSync(contents); - return file.createSource(); + Source newSource(String? path, [String contents = '']) { + if (path != null && p.isAbsolute(path)) { + throw ArgumentError.value(path, 'path', 'must be a relative path'); + } + final fileContext = sharedContext.fileContextForTest(contents, filename: path, + // TODO could we enable this in some cases? + includeTestDescription: false); + return resourceProvider.getFile(fileContext.path).createSource(); } void modifyFile(String path, String contents) { @@ -53,46 +48,18 @@ abstract class AnalysisDriverTestBase { static const testPathBase = '/Users/greglittlefield/workspaces/over_react2/tools/analyzer_plugin/'; + SharedAnalysisContext? _sharedContext; + SharedAnalysisContext get sharedContext => _sharedContext!; + @mustCallSuper Future setUp() async { - _resourceProvider = PhysicalResourceProvider(); - - // Setup a testing directory. All calls to [newSource] will create files - // within this directory. - _testPath = Directory(testPathBase).createTempSync().path; - - // If analysis_options.yaml isn't set up here, `AnalysisContext.optionsFile` will be null even if the file is added later, - // and also Dart will try to load analysis_options.yaml from an ancestor directory. - resourceProvider - .getFile(p.join(testPath, 'analysis_options.yaml')) - .writeAsStringSync(analysisOptionsYamlContents ?? ''); - - final testPackageName = 'test_package_' + p.basename(_testPath!).replaceAll(RegExp(r'[^a-zA-Z0-9_]'), ''); - const nullSafety = false; - resourceProvider.getFile(p.join(testPath, 'pubspec.yaml')).writeAsStringSync(''' -name: $testPackageName -environment: - sdk: ${nullSafety ? '">=2.12.0 <3.0.0"' : '">=2.10.0 <3.0.0"'} -dependencies: - over_react: - path: /Users/greglittlefield/workspaces/over_react2 -'''); - - // TODO optimize this: running `pub get` before each test is pretty slow. - final pubGetResult = - await Process.run('dart', ['pub', 'get', '--offline'], runInShell: true, workingDirectory: testPath); - if (pubGetResult.exitCode != 0) { - throw Exception('pub get failed with exit code ${pubGetResult.exitCode}.' - '\n${pubGetResult.stdout}${pubGetResult.stderr}'); - } + _sharedContext = SharedAnalysisContext.overReact; + await sharedContext.warmUpAnalysis(); + _resourceProvider = sharedContext.collection.contexts.single.currentSession.resourceProvider; } @mustCallSuper void tearDown() { _resourceProvider = null; - if (_testPath != null) { - Directory(_testPath!).deleteSync(recursive: true); - } - _testPath = 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 9dacd8e38..34c25e5bd 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(null, sourceContents); for (final target in selectionTargets) { final selection = createSelection(source, target); final assists = await _getAllAssists(selection); 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..4842bb53e 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 @@ -74,7 +74,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(null, 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 ad83a41cd..6052e51ff 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,7 @@ +import 'package:analyzer/dart/analysis/results.dart'; +import 'package:analyzer/file_system/overlay_file_system.dart'; import 'package:analyzer/src/generated/source.dart'; 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'; @@ -150,18 +151,21 @@ abstract class ServerPluginContributorTestBase extends AnalysisDriverTestBase { await super.setUp(); _channel = StubChannel(); - _plugin = PluginForTest(resourceProvider)..start(_channel!); - - // ignore: missing_required_param - final contextRoot = ContextRoot(testPath, []); - await testPlugin.handleAnalysisSetContextRoots(AnalysisSetContextRootsParams([contextRoot])); + _plugin = PluginForTest() + ..start(_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_util.dart b/tools/analyzer_plugin/test/test_util.dart index 4905f86d9..a6bb52187 100644 --- a/tools/analyzer_plugin/test/test_util.dart +++ b/tools/analyzer_plugin/test/test_util.dart @@ -1,19 +1,8 @@ -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'; @@ -77,137 +66,6 @@ Future parseAndGetResolvedUnit(String dartSource, {String? p return await context.getResolvedUnit() as ResolvedUnitResult; } -// 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; - } - return results; -} - - /// Returns [expression] parsed as AST. /// /// This is accomplished it by including the [expression] as a statement within a wrapper function @@ -247,15 +105,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/util/package_util.dart b/tools/analyzer_plugin/test/util/package_util.dart index 27bb841f6..33b7286c6 100644 --- a/tools/analyzer_plugin/test/util/package_util.dart +++ b/tools/analyzer_plugin/test/util/package_util.dart @@ -40,9 +40,9 @@ bool _isPubGetNecessary(String packageRoot) { } /// Runs `pub get` in [packageRoot] unless running `pub get` would have no effect. -Future runPubGetIfNeeded(String packageRoot) async { +void runPubGetIfNeeded(String packageRoot) { if (_isPubGetNecessary(packageRoot)) { - await runPubGet(packageRoot); + runPubGet(packageRoot); } else { _logger.info( 'Skipping `pub get`, which has already been run, in `$packageRoot`'); @@ -54,23 +54,22 @@ Future runPubGetIfNeeded(String packageRoot) async { /// /// For convenience, tries running with `pub get --offline` if `pub get` fails, /// for a better experience when not authenticated to private pub servers. -Future runPubGet(String workingDirectory) async { +void runPubGet(String workingDirectory) { _logger.info('Running `pub get` in `$workingDirectory`...'); - final process = await Process.start('pub', ['get'], + final result = Process.runSync('pub', ['get'], workingDirectory: workingDirectory, - runInShell: true, - mode: ProcessStartMode.inheritStdio); - final exitCode = await process.exitCode; + 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 process = await Process.start('pub', ['get', '--offline'], + final retryResult = Process.runSync('pub', ['get', '--offline'], workingDirectory: workingDirectory, - runInShell: true, - mode: ProcessStartMode.inheritStdio); - final exitCode = await process.exitCode; + runInShell: true); + final exitCode = retryResult.exitCode; if (exitCode != 0) { throw Exception('pub get failed with exit code: $exitCode'); } diff --git a/tools/analyzer_plugin/test/util/shared_analysis_context.dart b/tools/analyzer_plugin/test/util/shared_analysis_context.dart index 2d717d220..97021f4c5 100644 --- a/tools/analyzer_plugin/test/util/shared_analysis_context.dart +++ b/tools/analyzer_plugin/test/util/shared_analysis_context.dart @@ -19,7 +19,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:async/async.dart'; import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/analysis/utilities.dart'; @@ -33,6 +32,7 @@ import 'package:source_span/source_span.dart'; // just comment it and related code out. import 'package:test_api/src/backend/invoker.dart' show Invoker; import 'package:uuid/uuid.dart'; +import 'package:yaml/yaml.dart'; import 'package_util.dart'; import 'util.dart'; @@ -52,8 +52,8 @@ import 'util.dart'; 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')); + static final overReact = + SharedAnalysisContext(p.join(findPackageRootFor(p.current), 'test/test_fixtures/over_react_project')); /// The path to the package root in which test files will be created /// and resolved. @@ -61,7 +61,7 @@ class SharedAnalysisContext { /// The analysis context collection for files within [_path], initialized /// lazily. - late final AnalysisContextCollection collection; + late final AnalysisContextCollection collection = _initCollection(); /// A custom error message to display if `pub get` fails. final String? customPubGetErrorMessage; @@ -82,33 +82,29 @@ class SharedAnalysisContext { } } - // This ensures the _initIfNeeded logic is only ever run once, and prevents - // race conditions if there are concurrent calls to it. - final _initMemo = AsyncMemoizer(); - - Future _initIfNeeded() => _initMemo.runOnce(() async { - // 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 { - await runPubGetIfNeeded(_path); - } 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'); - } - - collection = AnalysisContextCollection( - includedPaths: [_path], - ); - }); + 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(_path); + } 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: [_path], + ); + } /// Warms up the AnalysisContextCollection by running `pub get` (if needed) and /// initializing [collection] if that hasn't been done yet, and getting the @@ -118,11 +114,11 @@ class SharedAnalysisContext { /// 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 { - await _initIfNeeded(); final path = p.join(_path, 'lib/analysis_warmup.dart'); await collection.contextFor(path).currentSession.getResolvedLibrary(path); _shouldPrintFirstFileWarning = false; } + // // /// A convenience method that creates a codemod [FileContext], // /// run [suggestor] on it, and returns the patches yielded. @@ -174,8 +170,36 @@ class SharedAnalysisContext { bool throwOnAnalysisErrors = true, IsExpectedError? isExpectedError, }) async { - await _initIfNeeded(); + final fileContext = + fileContextForTest(sourceText, filename: filename, includeTestDescription: includeTestDescription); + + final context = collection.contexts.singleWhere((c) => c.contextRoot.root.path == _path); + if (throwOnAnalysisErrors && !preResolveLibrary) { + throw ArgumentError('If throwOnAnalysisErrors is true, preResolveFile must be false'); + } + if (isExpectedError != null && !throwOnAnalysisErrors) { + throw ArgumentError('If isExpectedError is provided, throwOnAnalysisErrors must be true'); + } + if (preResolveLibrary) { + final result = await _printAboutFirstFile(() => context.currentSession.getResolvedLibrary(fileContext.path)); + if (throwOnAnalysisErrors) { + checkResolvedResultForErrors(result, isExpectedError: isExpectedError); + } + } + + // 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(fileContext.path); + + return fileContext; + } + + FileContext fileContextForTest( + String sourceText, { + String? filename, + bool includeTestDescription = true, + }) { filename ??= nextFilename(); if (includeTestDescription) { @@ -185,10 +209,7 @@ class SharedAnalysisContext { // - you can search for the test description to easily find the right file try { final testName = Invoker.current!.liveTest.test.name; - sourceText = - lineComment('Created within test with name:\n> $testName') + - '\n' + - sourceText; + sourceText = lineComment('Created within test with name:\n> $testName') + '\n' + sourceText; } catch (_) {} } @@ -205,25 +226,6 @@ class SharedAnalysisContext { file.parent.createSync(recursive: true); file.writeAsStringSync(sourceText); - final context = collection.contexts - .singleWhere((c) => c.contextRoot.root.path == _path); - - if (throwOnAnalysisErrors && !preResolveLibrary) { - throw ArgumentError( - 'If throwOnAnalysisErrors is true, preResolveFile must be false'); - } - if (isExpectedError != null && !throwOnAnalysisErrors) { - throw ArgumentError( - 'If isExpectedError is provided, throwOnAnalysisErrors must be true'); - } - if (preResolveLibrary) { - final result = await _printAboutFirstFile( - () => context.currentSession.getResolvedLibrary(path)); - if (throwOnAnalysisErrors) { - checkResolvedResultForErrors(result, isExpectedError: isExpectedError); - } - } - // 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); @@ -287,10 +289,7 @@ void checkResolvedResultForErrors( ' to verify that only the expected errors are present.'; if (result is! ResolvedLibraryResult) { - throw ArgumentError([ - 'Error resolving file; result was $result.', - sharedMessage - ].join(' ')); + throw ArgumentError(['Error resolving file; result was $result.', sharedMessage].join(' ')); } final unexpectedErrors = result.units @@ -374,7 +373,6 @@ extension ParseHelpers on SharedAnalysisContext { } } - /// A helper class for a file located at [path] that provides access to its /// contents and analyzed formats like [CompilationUnit] and [LibraryElement]. class FileContext { @@ -401,8 +399,7 @@ class FileContext { /// A representation of this file that makes it easy to reference spans of /// text, which is useful for the creation of [SourcePatch]es. - late final SourceFile sourceFile = - SourceFile.fromString(sourceText, url: Uri.file(path)); + late final SourceFile sourceFile = SourceFile.fromString(sourceText, url: Uri.file(path)); /// The contents of this file. late final String sourceText = File(path).readAsStringSync(); @@ -410,10 +407,7 @@ class FileContext { /// Uses the analyzer to resolve and return the library result for this file, /// which includes the [LibraryElement]. Future getResolvedLibrary() async { - final result = await _analysisContextCollection - .contextFor(path) - .currentSession - .getResolvedLibrary(path); + final result = await _analysisContextCollection.contextFor(path).currentSession.getResolvedLibrary(path); return result is ResolvedLibraryResult ? result : null; } @@ -423,18 +417,14 @@ class FileContext { /// If the fully resolved AST is not needed, use the much faster /// [getUnresolvedUnit]. Future getResolvedUnit() async { - return _analysisContextCollection - .contextFor(path) - .currentSession - .getResolvedUnit(path); + return _analysisContextCollection.contextFor(path).currentSession.getResolvedUnit(path); } /// Returns the unresolved AST for this file. /// /// If the fully resolved AST is needed, use [getResolvedUnit]. CompilationUnit getUnresolvedUnit() { - final result = - parseString(content: sourceText, path: path, throwIfDiagnostics: false); + final result = parseString(content: sourceText, path: path, throwIfDiagnostics: false); if (result.errors.isEmpty) return result.unit; // Errors thrown by parseString don't include the filename, and result in @@ -447,7 +437,6 @@ class FileContext { buffer.writeln(' ${error.errorCode.name}: ${error.message} - ' '${location.lineNumber}:${location.columnNumber}'); } - throw ArgumentError( - 'File "$relativePath" produced diagnostics when parsed:\n$buffer'); + throw ArgumentError('File "$relativePath" produced diagnostics when parsed:\n$buffer'); } } From 9d2fd9b01a063e02fcd01bb3fc2118c458655953 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Wed, 6 Sep 2023 16:21:07 -0700 Subject: [PATCH 18/45] Fix ast_util tests --- tools/analyzer_plugin/lib/src/util/ast_util.dart | 13 +++++++------ .../test/unit/util/ast_util_test.dart | 9 ++++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/tools/analyzer_plugin/lib/src/util/ast_util.dart b/tools/analyzer_plugin/lib/src/util/ast_util.dart index 907b69906..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; 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 0dbb183b4..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 ]); }); From 6d3583b6fb354b392fbafe741cc67f51b26d0223 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Wed, 6 Sep 2023 18:09:55 -0700 Subject: [PATCH 19/45] Support tests with custom analysis_options.yaml --- .gitignore | 3 +- .../test_bases/analysis_driver_test_base.dart | 21 ++++++-- .../test/util/package_util.dart | 6 +-- .../test/util/shared_analysis_context.dart | 54 ++++++++++++++----- 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 47373ca07..8445e71d4 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,5 @@ pubspec.lock # Should not track the output of code coverage for now coverage/ -tools/analyzer_plugin/test/test_fixtures/over_react_project/lib/dynamic_test_files/ +tools/analyzer_plugin/test/test_fixtures/**/lib/dynamic_test_files/ +tools/analyzer_plugin/test/test_fixtures/copies/** 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 f1d084b1a..661e8d2a3 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,4 +1,6 @@ -import 'package:analyzer/file_system/file_system.dart'; +import 'dart:io'; + +import 'package:analyzer/file_system/file_system.dart' show ResourceProvider; import 'package:analyzer/src/generated/source.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; @@ -46,14 +48,23 @@ abstract class AnalysisDriverTestBase { /// Returns the absolute path for [source]. String sourcePath(Source source) => source.uri.toFilePath(); - static const testPathBase = '/Users/greglittlefield/workspaces/over_react2/tools/analyzer_plugin/'; - SharedAnalysisContext? _sharedContext; + SharedAnalysisContext get sharedContext => _sharedContext!; @mustCallSuper Future setUp() async { - _sharedContext = SharedAnalysisContext.overReact; + // 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 { + _sharedContext = SharedAnalysisContext.copy(defaultContext); + File(p.join(sharedContext.contextRootPath, 'analysis_options.yaml')) + .writeAsStringSync(analysisOptionsYamlContents!); + } + await sharedContext.warmUpAnalysis(); _resourceProvider = sharedContext.collection.contexts.single.currentSession.resourceProvider; } @@ -61,5 +72,7 @@ abstract class AnalysisDriverTestBase { @mustCallSuper void tearDown() { _resourceProvider = null; + // FIXME delete copied context + _sharedContext = null; } } diff --git a/tools/analyzer_plugin/test/util/package_util.dart b/tools/analyzer_plugin/test/util/package_util.dart index 33b7286c6..2369f8ce2 100644 --- a/tools/analyzer_plugin/test/util/package_util.dart +++ b/tools/analyzer_plugin/test/util/package_util.dart @@ -57,7 +57,7 @@ void runPubGetIfNeeded(String packageRoot) { void runPubGet(String workingDirectory) { _logger.info('Running `pub get` in `$workingDirectory`...'); - final result = Process.runSync('pub', ['get'], + final result = Process.runSync('dart', ['pub', 'get'], workingDirectory: workingDirectory, runInShell: true ); @@ -71,10 +71,10 @@ void runPubGet(String workingDirectory) { runInShell: true); final exitCode = retryResult.exitCode; if (exitCode != 0) { - throw Exception('pub get failed with exit code: $exitCode'); + throw Exception('pub get failed with exit code $exitCode in $workingDirectory'); } } else if (exitCode != 0) { - throw Exception('pub get failed with exit code: $exitCode'); + throw Exception('pub get failed with exit code $exitCode in $workingDirectory'); } } diff --git a/tools/analyzer_plugin/test/util/shared_analysis_context.dart b/tools/analyzer_plugin/test/util/shared_analysis_context.dart index 97021f4c5..28756cdfc 100644 --- a/tools/analyzer_plugin/test/util/shared_analysis_context.dart +++ b/tools/analyzer_plugin/test/util/shared_analysis_context.dart @@ -25,6 +25,7 @@ import 'package:analyzer/dart/analysis/utilities.dart'; import 'package:analyzer/dart/ast/ast.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:source_span/source_span.dart'; @@ -32,7 +33,6 @@ import 'package:source_span/source_span.dart'; // just comment it and related code out. import 'package:test_api/src/backend/invoker.dart' show Invoker; import 'package:uuid/uuid.dart'; -import 'package:yaml/yaml.dart'; import 'package_util.dart'; import 'util.dart'; @@ -55,11 +55,39 @@ class SharedAnalysisContext { static final overReact = SharedAnalysisContext(p.join(findPackageRootFor(p.current), 'test/test_fixtures/over_react_project')); + static SharedAnalysisContext copy(SharedAnalysisContext other) { + final copyParentDir = Directory(p.join(findPackageRootFor(p.current), 'test/test_fixtures/copies/')); + copyParentDir.createSync(recursive: true); + final copyDir = copyParentDir.createTempSync().path; + // Adapted from package:io 1.0.4 `copyPathSync` FIXME attribute + 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); + } + } + final pubspec = File(p.join(copyDir, 'pubspec.yaml')); + // FIXME clean this up + // Update relative paths in dependencies. + pubspec.writeAsStringSync(pubspec + .readAsStringSync() + .replaceAllMapped(RegExp(r'(\bpath: )([^/])'), (match) => match[1]! + '../' + match[2]!)); + return SharedAnalysisContext(copyDir); + } + /// The path to the package root in which test files will be created /// and resolved. - final String _path; + final String contextRootPath; - /// The analysis context collection for files within [_path], initialized + /// The analysis context collection for files within [contextRootPath], initialized /// lazily. late final AnalysisContextCollection collection = _initCollection(); @@ -76,9 +104,9 @@ class SharedAnalysisContext { // analysis results (meaning faster test runs). final _testFileSubpath = 'lib/dynamic_test_files/${Uuid().v4()}'; - SharedAnalysisContext(this._path, {this.customPubGetErrorMessage}) { - if (!p.isAbsolute(_path)) { - throw ArgumentError.value(_path, 'projectRoot', 'must be absolute'); + SharedAnalysisContext(this.contextRootPath, {this.customPubGetErrorMessage}) { + if (!p.isAbsolute(contextRootPath)) { + throw ArgumentError.value(contextRootPath, 'projectRoot', 'must be absolute'); } } @@ -88,7 +116,7 @@ class SharedAnalysisContext { // 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(_path); + runPubGetIfNeeded(contextRootPath); } catch (e, st) { var message = [ // ignore: no_adjacent_strings_in_list @@ -102,7 +130,7 @@ class SharedAnalysisContext { } return AnalysisContextCollection( - includedPaths: [_path], + includedPaths: [contextRootPath], ); } @@ -114,7 +142,7 @@ class SharedAnalysisContext { /// 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(_path, 'lib/analysis_warmup.dart'); + final path = p.join(contextRootPath, 'lib/analysis_warmup.dart'); await collection.contextFor(path).currentSession.getResolvedLibrary(path); _shouldPrintFirstFileWarning = false; } @@ -173,7 +201,7 @@ class SharedAnalysisContext { final fileContext = fileContextForTest(sourceText, filename: filename, includeTestDescription: includeTestDescription); - final context = collection.contexts.singleWhere((c) => c.contextRoot.root.path == _path); + final context = collection.contexts.singleWhere((c) => c.contextRoot.root.path == contextRootPath); if (throwOnAnalysisErrors && !preResolveLibrary) { throw ArgumentError('If throwOnAnalysisErrors is true, preResolveFile must be false'); @@ -213,7 +241,7 @@ class SharedAnalysisContext { } catch (_) {} } - final path = p.join(_path, _testFileSubpath, filename); + final path = p.join(contextRootPath, _testFileSubpath, filename); final file = File(path); if (file.existsSync()) { throw StateError('File already exists: $filename.' @@ -230,7 +258,7 @@ class SharedAnalysisContext { // existing in the context we've set up (which shouldn't ever happen). collection.contextFor(path); - return FileContext(path, collection, root: _path); + return FileContext(path, collection, root: contextRootPath); } int _fileNameCounter = 0; @@ -252,7 +280,7 @@ class SharedAnalysisContext { } if (shouldPrint) { - final contextName = p.basename(_path); + final contextName = p.basename(contextRootPath); print('Resolving a file for the first time in context "$contextName";' ' this will take a few seconds...'); } From 47797289b1a3d9dd408c00764bf1c77c0d866c27 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 7 Sep 2023 11:04:18 -0700 Subject: [PATCH 20/45] Sync NodeLocator(2) and tests from analyzer package --- .../lib/src/util/analyzer_util.dart | 96 +++++++++- .../test/unit/util/analyzer_util_test.dart | 170 ++++++++++++++---- 2 files changed, 222 insertions(+), 44 deletions(-) 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/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; } } From 511ca2ac7a24dd029c4bc33e1a734a152f076ba5 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 7 Sep 2023 12:49:46 -0700 Subject: [PATCH 21/45] Fix boilerplate_util tests --- .../unit/util/boilerplate_utils_test.dart | 184 ++++++++++-------- 1 file changed, 104 insertions(+), 80 deletions(-) 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) { From 4e961de867b81b450eff5c8297caea759bb2dac2 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 7 Sep 2023 14:50:37 -0700 Subject: [PATCH 22/45] Fix analysis warnings in test cases --- .../exhaustive_deps_test_cases.dart | 31 +++++++++++-------- .../diagnostics/non_defaulted_prop_test.dart | 7 +++++ 2 files changed, 25 insertions(+), 13 deletions(-) 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/non_defaulted_prop_test.dart b/tools/analyzer_plugin/test/integration/diagnostics/non_defaulted_prop_test.dart index ad99fbaac..54b58e220 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 @@ -61,6 +61,7 @@ mixin FooProps on UiProps { final Foo = uiFunction( (props) { + // ignore: unused_local_variable final content = 'abc'; return (Dom.div() @@ -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 From 6070bd63dbdc07e09245962e9e1c1d2db5a8cf42 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 7 Sep 2023 16:00:03 -0700 Subject: [PATCH 23/45] Fix references to types no longer being Identifiers --- .../lib/src/diagnostic/exhaustive_deps.dart | 218 +++++++++++------- tools/analyzer_plugin/lib/src/util/util.dart | 29 +++ 2 files changed, 166 insertions(+), 81 deletions(-) diff --git a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart index a31910b87..f77de0edb 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,86 @@ 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 if/else instead of Union.switchCase so we can 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.name2.lexeme; + // 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 +765,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 +788,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; @@ -1267,38 +1301,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 +1359,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; @@ -1516,7 +1561,7 @@ class ReactiveHookCallbackInfo { class _Dependency { final bool isStable; - final List references; + final List> references; bool isUsedSomewhereAsCascadeTarget = false; @@ -1525,7 +1570,7 @@ class _Dependency { @override String toString() => prettyPrint({ 'isStable': isStable, - 'references': references, + 'references': references.map((r) => r.either).toList(), 'isUsedSomewhereAsCascadeTarget': isUsedSomewhereAsCascadeTarget, }); } @@ -1544,8 +1589,19 @@ 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); + } + } + } +} enum HookTypeWithStableMethods { stateHook, reducerHook, transitionHook } 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)!; +} From d798b105c2b8b418f37bb05ef81a07287cc41664 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 7 Sep 2023 16:00:47 -0700 Subject: [PATCH 24/45] Fix warning --- .../test/integration/test_bases/analysis_driver_test_base.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 661e8d2a3..b58886060 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 @@ -30,7 +30,7 @@ abstract class AnalysisDriverTestBase { /// [contents]. /// /// [path] must be relative; the returned source will be created within - /// [testPath]. + /// [sharedContext]. Source newSource(String? path, [String contents = '']) { if (path != null && p.isAbsolute(path)) { throw ArgumentError.value(path, 'path', 'must be a relative path'); From 2d80fd4663959d8234b89716cd791914a575350a Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 7 Sep 2023 16:48:25 -0700 Subject: [PATCH 25/45] Revert local pubspec.yaml changes --- tools/analyzer_plugin/pubspec.yaml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tools/analyzer_plugin/pubspec.yaml b/tools/analyzer_plugin/pubspec.yaml index ea454e566..52a621973 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -14,10 +14,7 @@ dependencies: # Upon release, this should be pinned to the over_react version from ../../pubspec.yaml # so that it always resolves to the same version of over_react that the user has pulled in, # and thus has the same boilerplate parsing code that's running in the builder. -# over_react: 4.10.0 - - over_react: - path: /Users/greglittlefield/workspaces/over_react2 + over_react: 4.10.0 path: ^1.5.1 source_span: ^1.7.0 yaml: ^3.0.0 @@ -37,7 +34,6 @@ dev_dependencies: pub_semver: ^2.0.0 test: ^1.14.0 test_reflective_loader: ^0.2.0 - uuid: ^3.0.5 workiva_analysis_options: ^1.1.0 dependency_validator: @@ -56,6 +52,6 @@ workiva: # # In CI, this should also be the path to the over_react checkout # -#dependency_overrides: +# dependency_overrides: # over_react: -# path: /Users/greglittlefield/workspaces/over_react +# path: /Users/me/workspaces/over_react From eb61939d823fd86f31f3b0804b2a6c06aa98f3b5 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 7 Sep 2023 16:54:55 -0700 Subject: [PATCH 26/45] Fix dependency_validator issues --- tools/analyzer_plugin/pubspec.yaml | 2 +- .../test_bases/analysis_driver_test_base.dart | 4 +-- .../test/util/shared_analysis_context.dart | 26 ++----------------- 3 files changed, 4 insertions(+), 28 deletions(-) diff --git a/tools/analyzer_plugin/pubspec.yaml b/tools/analyzer_plugin/pubspec.yaml index 52a621973..e3cccda7b 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -30,10 +30,10 @@ dev_dependencies: 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 <4.0.0' workiva_analysis_options: ^1.1.0 dependency_validator: 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 b58886060..977addc49 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 @@ -35,9 +35,7 @@ abstract class AnalysisDriverTestBase { if (path != null && p.isAbsolute(path)) { throw ArgumentError.value(path, 'path', 'must be a relative path'); } - final fileContext = sharedContext.fileContextForTest(contents, filename: path, - // TODO could we enable this in some cases? - includeTestDescription: false); + final fileContext = sharedContext.fileContextForTest(contents, filename: path); return resourceProvider.getFile(fileContext.path).createSource(); } diff --git a/tools/analyzer_plugin/test/util/shared_analysis_context.dart b/tools/analyzer_plugin/test/util/shared_analysis_context.dart index 28756cdfc..48dcf59cc 100644 --- a/tools/analyzer_plugin/test/util/shared_analysis_context.dart +++ b/tools/analyzer_plugin/test/util/shared_analysis_context.dart @@ -28,10 +28,6 @@ import 'package:analyzer/error/error.dart'; import 'package:io/io.dart'; import 'package:path/path.dart' as p; import 'package:source_span/source_span.dart'; - -// This isn't strictly required for anything, so if the import becomes invalid, -// just comment it and related code out. -import 'package:test_api/src/backend/invoker.dart' show Invoker; import 'package:uuid/uuid.dart'; import 'package_util.dart'; @@ -193,13 +189,11 @@ class SharedAnalysisContext { Future resolvedFileContextForTest( String sourceText, { String? filename, - bool includeTestDescription = true, bool preResolveLibrary = true, bool throwOnAnalysisErrors = true, IsExpectedError? isExpectedError, }) async { - final fileContext = - fileContextForTest(sourceText, filename: filename, includeTestDescription: includeTestDescription); + final fileContext = fileContextForTest(sourceText, filename: filename); final context = collection.contexts.singleWhere((c) => c.contextRoot.root.path == contextRootPath); @@ -223,24 +217,8 @@ class SharedAnalysisContext { return fileContext; } - FileContext fileContextForTest( - String sourceText, { - String? filename, - bool includeTestDescription = true, - }) { + FileContext fileContextForTest(String sourceText, {String? filename}) { filename ??= nextFilename(); - - if (includeTestDescription) { - // For convenience, include the current test description in the file as - // a comment, so that: - // - you can tell you're looking at the right file for a given test - // - you can search for the test description to easily find the right file - try { - final testName = Invoker.current!.liveTest.test.name; - sourceText = lineComment('Created within test with name:\n> $testName') + '\n' + sourceText; - } catch (_) {} - } - final path = p.join(contextRootPath, _testFileSubpath, filename); final file = File(path); if (file.existsSync()) { From 1dc9e273a9bb116263189baa18ae76f255147aaf Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 7 Sep 2023 17:12:37 -0700 Subject: [PATCH 27/45] Fix code dependent on newer analyzer version than lower bound --- .../lib/src/diagnostic/exhaustive_deps.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart index f77de0edb..0ad9dc565 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart @@ -678,7 +678,7 @@ class ExhaustiveDeps extends DiagnosticContributor { // NamedType case. This is for references to generic type parameters // (references to other types will get filtered out by the isDeclaredInPureScope check above). (reference) { - dependency = reference.name2.lexeme; + dependency = reference.name.name; // These aren't possible for type annotations. isStable = false; isUsedAsCascadeTarget = false; @@ -1603,6 +1603,12 @@ Iterable> resolvedReferencesWithin(AstNode node) sy } } +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 } abstract class HookConstants { From 9eb82d7137eca0fdba50bf70324063642bf4cf33 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Thu, 7 Sep 2023 17:12:45 -0700 Subject: [PATCH 28/45] Fix hint --- tools/analyzer_plugin/test/integration/stubs.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/analyzer_plugin/test/integration/stubs.dart b/tools/analyzer_plugin/test/integration/stubs.dart index 657cbfcca..0edc7c32a 100644 --- a/tools/analyzer_plugin/test/integration/stubs.dart +++ b/tools/analyzer_plugin/test/integration/stubs.dart @@ -33,9 +33,6 @@ class StubServerPlugin implements ServerPlugin { @override PluginCommunicationChannel get channel => throw UnimplementedError(); - @override - set channel(PluginCommunicationChannel value) => throw UnimplementedError(); - @override Set get priorityPaths => throw UnimplementedError(); From 92095942614c55401a3973d44d67abb2b1a45023 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 10:21:35 -0700 Subject: [PATCH 29/45] Clean up implementation imports --- tools/analyzer_plugin/analysis_options.yaml | 2 ++ .../analyzer_plugin/lib/src/async_plugin_apis/diagnostic.dart | 2 +- tools/analyzer_plugin/lib/src/util/ignore_info.dart | 3 ++- .../test/integration/test_bases/analysis_driver_test_base.dart | 3 ++- .../test/integration/test_bases/diagnostic_test_base.dart | 1 + .../test_bases/server_plugin_contributor_test_base.dart | 3 ++- 6 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/analyzer_plugin/analysis_options.yaml b/tools/analyzer_plugin/analysis_options.yaml index ed45147b1..cac19697a 100644 --- a/tools/analyzer_plugin/analysis_options.yaml +++ b/tools/analyzer_plugin/analysis_options.yaml @@ -15,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 @@ -29,3 +30,4 @@ linter: rules: - avoid_returning_null_for_future - avoid_void_async + - implementation_imports 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 08f0d1f2b..bc4d43eda 100644 --- a/tools/analyzer_plugin/lib/src/async_plugin_apis/diagnostic.dart +++ b/tools/analyzer_plugin/lib/src/async_plugin_apis/diagnostic.dart @@ -42,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'; diff --git a/tools/analyzer_plugin/lib/src/util/ignore_info.dart b/tools/analyzer_plugin/lib/src/util/ignore_info.dart index 8e621d41a..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 { 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 977addc49..071780b84 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,7 +1,8 @@ import 'dart:io'; import 'package:analyzer/file_system/file_system.dart' show ResourceProvider; -import 'package:analyzer/src/generated/source.dart'; +// ignore: implementation_imports +import 'package:analyzer/src/generated/source.dart' show Source; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; 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 4842bb53e..e53167d37 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 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 6052e51ff..d98fe38e9 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,7 @@ import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/file_system/overlay_file_system.dart'; -import 'package:analyzer/src/generated/source.dart'; +// ignore: implementation_imports +import 'package:analyzer/src/generated/source.dart' show Source, SourceRange; import 'package:analyzer_plugin/protocol/protocol_common.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; From eb9415f5ee19e65ab905e16debab6108a30f5f50 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 10:22:14 -0700 Subject: [PATCH 30/45] Omit types in StubServerPlugin overrides to clean up imports and remove some src imports --- .../test/integration/stubs.dart | 115 +++++++----------- 1 file changed, 45 insertions(+), 70 deletions(-) diff --git a/tools/analyzer_plugin/test/integration/stubs.dart b/tools/analyzer_plugin/test/integration/stubs.dart index 0edc7c32a..32075a81a 100644 --- a/tools/analyzer_plugin/test/integration/stubs.dart +++ b/tools/analyzer_plugin/test/integration/stubs.dart @@ -1,19 +1,13 @@ import 'package:analyzer/dart/analysis/analysis_context.dart'; -import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/file_system/overlay_file_system.dart'; -import 'package:analyzer/src/dart/analysis/byte_store.dart'; -import 'package:analyzer/src/dart/analysis/driver.dart'; import 'package:analyzer_plugin/channel/channel.dart'; import 'package:analyzer_plugin/plugin/navigation_mixin.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:analyzer_plugin/utilities/subscriptions/subscription_manager.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 'package:pub_semver/src/version.dart'; import 'test_bases/assist_test_base.dart'; @@ -31,155 +25,137 @@ class StubChannel implements PluginCommunicationChannel { class StubServerPlugin implements ServerPlugin { @override - PluginCommunicationChannel get channel => throw UnimplementedError(); + get channel => throw UnimplementedError(); @override - Set get priorityPaths => throw UnimplementedError(); + get priorityPaths => throw UnimplementedError(); @override - set priorityPaths(Set value) => throw UnimplementedError(); + set priorityPaths(value) => throw UnimplementedError(); @override - Future afterNewContextCollection({required AnalysisContextCollection contextCollection}) => - throw UnimplementedError(); + afterNewContextCollection({required contextCollection}) => throw UnimplementedError(); @override - Future analyzeFile({required AnalysisContext analysisContext, required String path}) => - throw UnimplementedError(); + analyzeFile({required analysisContext, required path}) => throw UnimplementedError(); @override - Future analyzeFiles({required AnalysisContext analysisContext, required List paths}) => - throw UnimplementedError(); + analyzeFiles({required analysisContext, required paths}) => throw UnimplementedError(); @override - Future beforeContextCollectionDispose({required AnalysisContextCollection contextCollection}) => - throw UnimplementedError(); - + beforeContextCollectionDispose({required contextCollection}) => throw UnimplementedError(); @override - String? get contactInfo => throw UnimplementedError(); + get contactInfo => throw UnimplementedError(); @override - Future contentChanged(List paths) => throw UnimplementedError(); + contentChanged(paths) => throw UnimplementedError(); @override - ByteStore createByteStore() => throw UnimplementedError(); + createByteStore() => throw UnimplementedError(); @override - List get fileGlobsToAnalyze => throw UnimplementedError(); + get fileGlobsToAnalyze => throw UnimplementedError(); @override - Future flushAnalysisState({bool elementModels = true}) => throw UnimplementedError(); + flushAnalysisState({elementModels = true}) => throw UnimplementedError(); @override - Future getResolvedUnitResult(String path) => throw UnimplementedError(); + getResolvedUnitResult(path) => throw UnimplementedError(); @override - Future handleAffectedFiles({required AnalysisContext analysisContext, required List paths}) => - throw UnimplementedError(); + handleAffectedFiles({required analysisContext, required paths}) => throw UnimplementedError(); @override - Future handleAnalysisGetNavigation(AnalysisGetNavigationParams parameters) => - throw UnimplementedError(); + handleAnalysisGetNavigation(parameters) => throw UnimplementedError(); @override - Future handleAnalysisHandleWatchEvents(AnalysisHandleWatchEventsParams parameters) => - throw UnimplementedError(); + handleAnalysisHandleWatchEvents(parameters) => throw UnimplementedError(); @override - Future handleAnalysisSetContextRoots(AnalysisSetContextRootsParams parameters) => - throw UnimplementedError(); + handleAnalysisSetContextRoots(parameters) => throw UnimplementedError(); @override - Future handleAnalysisSetPriorityFiles(AnalysisSetPriorityFilesParams parameters) => - throw UnimplementedError(); + handleAnalysisSetPriorityFiles(parameters) => throw UnimplementedError(); @override - Future handleAnalysisSetSubscriptions(AnalysisSetSubscriptionsParams parameters) => - throw UnimplementedError(); + handleAnalysisSetSubscriptions(parameters) => throw UnimplementedError(); @override - Future handleAnalysisUpdateContent(AnalysisUpdateContentParams parameters) => - throw UnimplementedError(); + handleAnalysisUpdateContent(parameters) => throw UnimplementedError(); @override - Future handleCompletionGetSuggestions(CompletionGetSuggestionsParams parameters) => - throw UnimplementedError(); + handleCompletionGetSuggestions(parameters) => throw UnimplementedError(); @override - Future handleEditGetAssists(EditGetAssistsParams parameters) => throw UnimplementedError(); + handleEditGetAssists(parameters) => throw UnimplementedError(); @override - Future handleEditGetAvailableRefactorings( - EditGetAvailableRefactoringsParams parameters) => - throw UnimplementedError(); + handleEditGetAvailableRefactorings(parameters) => throw UnimplementedError(); @override - Future handleEditGetFixes(EditGetFixesParams parameters) => throw UnimplementedError(); + handleEditGetFixes(parameters) => throw UnimplementedError(); @override - Future handleEditGetRefactoring(EditGetRefactoringParams parameters) => - throw UnimplementedError(); + handleEditGetRefactoring(parameters) => throw UnimplementedError(); @override - Future handleKytheGetKytheEntries(KytheGetKytheEntriesParams parameters) => - throw UnimplementedError(); + handleKytheGetKytheEntries(parameters) => throw UnimplementedError(); @override - Future handlePluginShutdown(PluginShutdownParams parameters) => throw UnimplementedError(); + handlePluginShutdown(parameters) => throw UnimplementedError(); @override - Future handlePluginVersionCheck(PluginVersionCheckParams parameters) => - throw UnimplementedError(); + handlePluginVersionCheck(parameters) => throw UnimplementedError(); @override - bool isCompatibleWith(Version serverVersion) => throw UnimplementedError(); + isCompatibleWith(serverVersion) => throw UnimplementedError(); @override - String get name => throw UnimplementedError(); + get name => throw UnimplementedError(); @override void onDone() {} @override - void onError(Object exception, StackTrace stackTrace) {} + void onError(exception, stackTrace) {} @override - OverlayResourceProvider get resourceProvider => throw UnimplementedError(); + get resourceProvider => throw UnimplementedError(); @override - Future sendFoldingNotification(String path) => throw UnimplementedError(); + sendFoldingNotification(path) => throw UnimplementedError(); @override - Future sendHighlightsNotification(String path) => throw UnimplementedError(); + sendHighlightsNotification(path) => throw UnimplementedError(); @override - Future sendNavigationNotification(String path) => throw UnimplementedError(); + sendNavigationNotification(path) => throw UnimplementedError(); @override - void sendNotificationsForFile(String path) {} + void sendNotificationsForFile(path) {} @override - void sendNotificationsForSubscriptions(Map> subscriptions) {} + void sendNotificationsForSubscriptions(subscriptions) {} @override - Future sendOccurrencesNotification(String path) => throw UnimplementedError(); + sendOccurrencesNotification(path) => throw UnimplementedError(); @override - Future sendOutlineNotification(String path) => throw UnimplementedError(); + sendOutlineNotification(path) => throw UnimplementedError(); @override - void start(PluginCommunicationChannel channel) => throw UnimplementedError(); + void start(channel) => throw UnimplementedError(); @override - SubscriptionManager get subscriptionManager => throw UnimplementedError(); + get subscriptionManager => throw UnimplementedError(); @override - String get version => throw UnimplementedError(); + 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]. @@ -196,8 +172,6 @@ class PluginForTest extends StubServerPlugin @override PluginCommunicationChannel get channel => _channel!; - - @override void start(PluginCommunicationChannel channel) { _channel = channel; @@ -218,7 +192,8 @@ class PluginForTest extends StubServerPlugin Future contentChanged(List paths) => throw UnimplementedError(); @override - Future getResolvedUnitResult(String path) => handleGetResolvedUnitResult?.call(path) ?? (throw UnimplementedError()); + Future getResolvedUnitResult(String path) => + handleGetResolvedUnitResult?.call(path) ?? (throw UnimplementedError()); Future Function(String path)? handleGetResolvedUnitResult; From ec32364dba2db12119f343a27f19985b79483b08 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 10:22:23 -0700 Subject: [PATCH 31/45] Cleanup --- tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart | 3 ++- tools/analyzer_plugin/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart index 0ad9dc565..04c5a7ea6 100644 --- a/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart +++ b/tools/analyzer_plugin/lib/src/diagnostic/exhaustive_deps.dart @@ -632,7 +632,8 @@ class ExhaustiveDeps extends DiagnosticContributor { continue; } - // Use if/else instead of Union.switchCase so we can ensure these variables are definitely assigned. + // 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; diff --git a/tools/analyzer_plugin/pubspec.yaml b/tools/analyzer_plugin/pubspec.yaml index e3cccda7b..0f5815640 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -33,7 +33,7 @@ dev_dependencies: pub_semver: ^2.0.0 test: ^1.14.0 test_reflective_loader: ^0.2.0 - uuid: '>3.0.0 <4.0.0' + uuid: '>3.0.0 <5.0.0' workiva_analysis_options: ^1.1.0 dependency_validator: From a542aee77983b84a25f9252fce2f7080446f8fc4 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 10:46:33 -0700 Subject: [PATCH 32/45] Port over package_util tests --- .../test/util/package_util_test.dart | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 tools/analyzer_plugin/test/util/package_util_test.dart 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); +} From 07fb65ff712a160830987543eb4d28af0067b2ed Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 10:57:40 -0700 Subject: [PATCH 33/45] Clean up unused SharedAnalysisContext-related code --- .../test_bases/analysis_driver_test_base.dart | 4 +- tools/analyzer_plugin/test/test_util.dart | 19 +- .../test/util/shared_analysis_context.dart | 209 +----------------- 3 files changed, 22 insertions(+), 210 deletions(-) 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 071780b84..dafba6805 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 @@ -36,8 +36,8 @@ abstract class AnalysisDriverTestBase { if (path != null && p.isAbsolute(path)) { throw ArgumentError.value(path, 'path', 'must be a relative path'); } - final fileContext = sharedContext.fileContextForTest(contents, filename: path); - return resourceProvider.getFile(fileContext.path).createSource(); + final testFilePath = sharedContext.createTestFile(contents, filename: path); + return resourceProvider.getFile(testFilePath).createSource(); } void modifyFile(String path, String contents) { diff --git a/tools/analyzer_plugin/test/test_util.dart b/tools/analyzer_plugin/test/test_util.dart index a6bb52187..6abcf2d45 100644 --- a/tools/analyzer_plugin/test/test_util.dart +++ b/tools/analyzer_plugin/test/test_util.dart @@ -1,8 +1,11 @@ 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: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 'util/shared_analysis_context.dart'; @@ -62,8 +65,20 @@ 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}) async { - final context = await SharedAnalysisContext.overReact.resolvedFileContextForTest(dartSource, filename: path); - return await context.getResolvedUnit() as ResolvedUnitResult; + 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 result; } /// Returns [expression] parsed as AST. diff --git a/tools/analyzer_plugin/test/util/shared_analysis_context.dart b/tools/analyzer_plugin/test/util/shared_analysis_context.dart index 48dcf59cc..de4ed2624 100644 --- a/tools/analyzer_plugin/test/util/shared_analysis_context.dart +++ b/tools/analyzer_plugin/test/util/shared_analysis_context.dart @@ -21,19 +21,16 @@ 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/error/error.dart'; import 'package:io/io.dart'; import 'package:path/path.dart' as p; -import 'package:source_span/source_span.dart'; import 'package:uuid/uuid.dart'; import 'package_util.dart'; import 'util.dart'; -/// Provides a mechanism for getting resolved codemod [FileContext]s for test cases +/// 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) @@ -140,31 +137,8 @@ class SharedAnalysisContext { Future warmUpAnalysis() async { final path = p.join(contextRootPath, 'lib/analysis_warmup.dart'); await collection.contextFor(path).currentSession.getResolvedLibrary(path); - _shouldPrintFirstFileWarning = false; } - // - // /// A convenience method that creates a codemod [FileContext], - // /// run [suggestor] on it, and returns the patches yielded. - // /// - // /// Most arguments are forwarded to [resolvedFileContextForTest]; - // /// see that method for more details. - // Future> getPatches( - // Suggestor suggestor, - // String sourceText, { - // String? filename, - // bool preResolveLibrary = true, - // bool throwOnAnalysisErrors = true, - // }) async { - // final context = await resolvedFileContextForTest( - // sourceText, - // preResolveLibrary: preResolveLibrary, - // throwOnAnalysisErrors: throwOnAnalysisErrors, - // filename: filename, - // ); - // return await suggestor(context).toList(); - // } - /// 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. @@ -177,47 +151,7 @@ class SharedAnalysisContext { /// 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. - /// - /// If [preResolveLibrary] is `true`, then the file will be resolved as a library - /// and checked for errors (if [throwOnAnalysisErrors] is `true`) to help - /// validate that there are no issues resolving the test file, which are likely - /// the result of a bad test file that could result in false positives or - /// confusing errors in your test. - /// - /// If you expect analysis errors in your test file, provide a [isExpectedError] - /// so that those errors can be ignored while others can be filtered out. - Future resolvedFileContextForTest( - String sourceText, { - String? filename, - bool preResolveLibrary = true, - bool throwOnAnalysisErrors = true, - IsExpectedError? isExpectedError, - }) async { - final fileContext = fileContextForTest(sourceText, filename: filename); - - final context = collection.contexts.singleWhere((c) => c.contextRoot.root.path == contextRootPath); - - if (throwOnAnalysisErrors && !preResolveLibrary) { - throw ArgumentError('If throwOnAnalysisErrors is true, preResolveFile must be false'); - } - if (isExpectedError != null && !throwOnAnalysisErrors) { - throw ArgumentError('If isExpectedError is provided, throwOnAnalysisErrors must be true'); - } - if (preResolveLibrary) { - final result = await _printAboutFirstFile(() => context.currentSession.getResolvedLibrary(fileContext.path)); - if (throwOnAnalysisErrors) { - checkResolvedResultForErrors(result, isExpectedError: isExpectedError); - } - } - - // 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(fileContext.path); - - return fileContext; - } - - FileContext fileContextForTest(String sourceText, {String? filename}) { + String createTestFile(String sourceText, {String? filename}) { filename ??= nextFilename(); final path = p.join(contextRootPath, _testFileSubpath, filename); final file = File(path); @@ -236,38 +170,13 @@ class SharedAnalysisContext { // existing in the context we've set up (which shouldn't ever happen). collection.contextFor(path); - return FileContext(path, collection, root: contextRootPath); + return path; } int _fileNameCounter = 0; /// Returns a filename that hasn't been used before. String nextFilename() => 'test_${_fileNameCounter++}.dart'; - - bool _shouldPrintFirstFileWarning = true; - - /// We can't intelligently warn only when this is taking too long since - /// getResolvedLibrary2 blocks the main thread for a long period of time, - /// making it so that timers don't fire until it's done. - /// So, we'll just always print this. - Future _printAboutFirstFile(Future Function() callback) async { - var shouldPrint = false; - if (_shouldPrintFirstFileWarning) { - _shouldPrintFirstFileWarning = false; - shouldPrint = true; - } - - if (shouldPrint) { - final contextName = p.basename(contextRootPath); - print('Resolving a file for the first time in context "$contextName";' - ' this will take a few seconds...'); - } - final result = await callback(); - if (shouldPrint) { - print('Done resolving.'); - } - return result; - } } /// A function that returns whether an error is expected and thus should be ignored @@ -334,115 +243,3 @@ extension FileSystemDeleteIfExistExtension on FileSystemEntity { } } } - -extension ParseHelpers on SharedAnalysisContext { - /// Returns [expression] parsed as AST. - /// - /// This is accomplished it by including the [expression] as a statement within a wrapper function - /// with any necessary [imports] at the top of the source. As a result, the offset of the - /// returned expression will not be 0. - /// - /// To return resolved AST, set [isResolved] to true. - Future parseExpression( - String expression, { - String imports = '', - String otherSource = '', - bool isResolved = false, - }) async { - CompilationUnit unit; - // Wrap the expression in parens to ensure this is interpreted as an expression - // for ambiguous cases (e.g, a map literal that could be interpreted as an empty block). - final source = ''' - $imports - void wrapperFunction() { - ($expression); - } - $otherSource - '''; - final fileContext = await resolvedFileContextForTest(source, - // We don't want to get the resolved unit if `isResolve = false`, - // since it may fail. - preResolveLibrary: false, - throwOnAnalysisErrors: false); - if (isResolved) { - final result = await fileContext.getResolvedUnit(); - unit = (result as ResolvedUnitResult).unit; - } else { - unit = fileContext.getUnresolvedUnit(); - } - final parsedFunction = unit.childEntities - .whereType() - .singleWhere((function) => function.name.lexeme == 'wrapperFunction'); - final body = parsedFunction.functionExpression.body as BlockFunctionBody; - final statement = body.block.statements.single as ExpressionStatement; - return (statement.expression as ParenthesizedExpression).expression; - } -} - -/// A helper class for a file located at [path] that provides access to its -/// contents and analyzed formats like [CompilationUnit] and [LibraryElement]. -class FileContext { - final AnalysisContextCollection _analysisContextCollection; - - /// This file's absolute path. - final String path; - - /// This file's path relative to [root]. - final String relativePath; - - /// The path to the working directory from which this file was discovered. - /// - /// Defaults to current working directory. - final String root; - - FileContext(this.path, this._analysisContextCollection, {String? root}) - : root = root ?? p.current, - relativePath = p.relative(path, from: root) { - if (!p.isAbsolute(path)) { - throw ArgumentError.value(path, 'path', 'must be absolute.'); - } - } - - /// A representation of this file that makes it easy to reference spans of - /// text, which is useful for the creation of [SourcePatch]es. - late final SourceFile sourceFile = SourceFile.fromString(sourceText, url: Uri.file(path)); - - /// The contents of this file. - late final String sourceText = File(path).readAsStringSync(); - - /// Uses the analyzer to resolve and return the library result for this file, - /// which includes the [LibraryElement]. - Future getResolvedLibrary() async { - final result = await _analysisContextCollection.contextFor(path).currentSession.getResolvedLibrary(path); - return result is ResolvedLibraryResult ? result : null; - } - - /// Uses the analyzer to resolve and return the AST result for this file, - /// which includes the [CompilationUnit]. - /// - /// If the fully resolved AST is not needed, use the much faster - /// [getUnresolvedUnit]. - Future getResolvedUnit() async { - return _analysisContextCollection.contextFor(path).currentSession.getResolvedUnit(path); - } - - /// Returns the unresolved AST for this file. - /// - /// If the fully resolved AST is needed, use [getResolvedUnit]. - CompilationUnit getUnresolvedUnit() { - final result = parseString(content: sourceText, path: path, throwIfDiagnostics: false); - if (result.errors.isEmpty) return result.unit; - - // Errors thrown by parseString don't include the filename, and result in - // the codemod halting without indicating which file it failed on. - // To aid in debugging, we'll construct the error message the same way - // parseString does, but also include the path to the file. - var buffer = StringBuffer(); - for (final error in result.errors) { - var location = result.lineInfo.getLocation(error.offset); - buffer.writeln(' ${error.errorCode.name}: ${error.message} - ' - '${location.lineNumber}:${location.columnNumber}'); - } - throw ArgumentError('File "$relativePath" produced diagnostics when parsed:\n$buffer'); - } -} From 86feba08ae967df8de5f4d5653d13c3d26d00fb4 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 11:12:45 -0700 Subject: [PATCH 34/45] Delete temporary shared contexts after use --- .../test_bases/analysis_driver_test_base.dart | 6 ++-- .../test/util/shared_analysis_context.dart | 28 +++++++++++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) 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 dafba6805..186da6b47 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 @@ -59,7 +59,7 @@ abstract class AnalysisDriverTestBase { if (analysisOptionsYamlContents == null) { _sharedContext = defaultContext; } else { - _sharedContext = SharedAnalysisContext.copy(defaultContext); + _sharedContext = SharedAnalysisContext.createTemporaryCopy(defaultContext); File(p.join(sharedContext.contextRootPath, 'analysis_options.yaml')) .writeAsStringSync(analysisOptionsYamlContents!); } @@ -71,7 +71,9 @@ abstract class AnalysisDriverTestBase { @mustCallSuper void tearDown() { _resourceProvider = null; - // FIXME delete copied context + if (_sharedContext != null && sharedContext.isTemporaryCopy) { + Directory(sharedContext.contextRootPath).deleteSync(recursive: true); + } _sharedContext = null; } } diff --git a/tools/analyzer_plugin/test/util/shared_analysis_context.dart b/tools/analyzer_plugin/test/util/shared_analysis_context.dart index de4ed2624..1b07ee13c 100644 --- a/tools/analyzer_plugin/test/util/shared_analysis_context.dart +++ b/tools/analyzer_plugin/test/util/shared_analysis_context.dart @@ -48,7 +48,11 @@ class SharedAnalysisContext { static final overReact = SharedAnalysisContext(p.join(findPackageRootFor(p.current), 'test/test_fixtures/over_react_project')); - static SharedAnalysisContext copy(SharedAnalysisContext other) { + /// 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) { final copyParentDir = Directory(p.join(findPackageRootFor(p.current), 'test/test_fixtures/copies/')); copyParentDir.createSync(recursive: true); final copyDir = copyParentDir.createTempSync().path; @@ -73,13 +77,16 @@ class SharedAnalysisContext { pubspec.writeAsStringSync(pubspec .readAsStringSync() .replaceAllMapped(RegExp(r'(\bpath: )([^/])'), (match) => match[1]! + '../' + match[2]!)); - return SharedAnalysisContext(copyDir); + 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(); @@ -97,12 +104,27 @@ class SharedAnalysisContext { // analysis results (meaning faster test runs). final _testFileSubpath = 'lib/dynamic_test_files/${Uuid().v4()}'; - SharedAnalysisContext(this.contextRootPath, {this.customPubGetErrorMessage}) { + 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 From 080bb7ff00004ed5c0f3ca14be11106055e4c58e Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 11:20:00 -0700 Subject: [PATCH 35/45] Clean up temporary test fixture pathing --- .gitignore | 2 +- .../test_bases/analysis_driver_test_base.dart | 9 ++++++++- .../test/util/shared_analysis_context.dart | 10 ++-------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 8445e71d4..095463ceb 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,4 @@ pubspec.lock coverage/ tools/analyzer_plugin/test/test_fixtures/**/lib/dynamic_test_files/ -tools/analyzer_plugin/test/test_fixtures/copies/** +tools/analyzer_plugin/test/temporary_test_fixtures/** 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 186da6b47..d8ec0a956 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 @@ -59,7 +59,14 @@ abstract class AnalysisDriverTestBase { if (analysisOptionsYamlContents == null) { _sharedContext = defaultContext; } else { - _sharedContext = SharedAnalysisContext.createTemporaryCopy(defaultContext); + // 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!); } diff --git a/tools/analyzer_plugin/test/util/shared_analysis_context.dart b/tools/analyzer_plugin/test/util/shared_analysis_context.dart index 1b07ee13c..b9b41b7e0 100644 --- a/tools/analyzer_plugin/test/util/shared_analysis_context.dart +++ b/tools/analyzer_plugin/test/util/shared_analysis_context.dart @@ -52,8 +52,8 @@ class SharedAnalysisContext { /// /// 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) { - final copyParentDir = Directory(p.join(findPackageRootFor(p.current), 'test/test_fixtures/copies/')); + 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` FIXME attribute @@ -71,12 +71,6 @@ class SharedAnalysisContext { Link(copyTo).createSync(file.targetSync(), recursive: true); } } - final pubspec = File(p.join(copyDir, 'pubspec.yaml')); - // FIXME clean this up - // Update relative paths in dependencies. - pubspec.writeAsStringSync(pubspec - .readAsStringSync() - .replaceAllMapped(RegExp(r'(\bpath: )([^/])'), (match) => match[1]! + '../' + match[2]!)); return SharedAnalysisContext._temporaryCopy(copyDir, customPubGetErrorMessage: other.customPubGetErrorMessage); } From aed6ca315674152c26e5fcf9cae2b04a2740078e Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 11:27:46 -0700 Subject: [PATCH 36/45] Add attribution --- .../test/util/shared_analysis_context.dart | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/tools/analyzer_plugin/test/util/shared_analysis_context.dart b/tools/analyzer_plugin/test/util/shared_analysis_context.dart index b9b41b7e0..dfd5a1cb6 100644 --- a/tools/analyzer_plugin/test/util/shared_analysis_context.dart +++ b/tools/analyzer_plugin/test/util/shared_analysis_context.dart @@ -56,7 +56,36 @@ class SharedAnalysisContext { 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` FIXME attribute + // 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; From 471bf576f4bbe9f9b182b2c9352f8a733b2898cf Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 11:33:59 -0700 Subject: [PATCH 37/45] Clean up newSource API --- .../assists/add_create_ref_test.dart | 22 ++++++------- .../integration/assists/add_props_test.dart | 8 ++--- .../assists/toggle_statefulness_test.dart | 32 +++++++++---------- .../analysis_options_configuration_test.dart | 2 +- .../diagnostics/arrow_function_prop_test.dart | 12 +++---- .../integration/diagnostics/bad_key_test.dart | 2 +- .../boilerplate_validator_test.dart | 14 ++++---- .../diagnostics/callback_ref_test.dart | 20 ++++++------ .../diagnostics/create_ref_usage_test.dart | 10 +++--- .../duplicate_prop_cascade_test.dart | 22 ++++++------- .../diagnostics/exhaustive_deps_test.dart | 4 +-- ...d_only_dom_props_to_dom_builders_test.dart | 4 +-- .../diagnostics/non_defaulted_prop_test.dart | 10 +++--- .../test_bases/analysis_driver_test_base.dart | 12 ++++--- .../test_bases/assist_test_base.dart | 2 +- .../test_bases/diagnostic_test_base.dart | 2 +- 16 files changed, 90 insertions(+), 88 deletions(-) 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 60edf7b10..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(null, '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(null, ''' + 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(null, usageSourceWithinFnComponent(fixed: false)); + var source = newSource(usageSourceWithinFnComponent(fixed: false)); var selection = createSelection(source, '(##'); await expectNoAssist(selection); } Future test_noAssistWithExistingRefInClassComponentDecl() async { - final source = newSource(null, ''' + 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(null, usageSourceWithinClassComponent(fixed: false)); + var source = newSource(usageSourceWithinClassComponent(fixed: false)); var selection = createSelection(source, ' {##'); await expectNoAssist(selection); } Future test_classComponentAssist_componentNameSelection() async { - var source = newSource(null, 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(null, 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(null, 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(null, 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(null, 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(null, 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 9c3fae77a..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(null, '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(null, 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(null, 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(null, ''' + 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 b856948d9..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(null, '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(null, '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 f5b4e5d78..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(null, 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 5b3b3827d..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(null, 'var foo = true;'); + final source = newSource('var foo = true;'); expect(await getAllErrors(source), isEmpty); } Future test_noErrorLastInCascade() async { - final source = newSource(null, /*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(null, /*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(null, 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(null, 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(null, ''' + 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 c1fd953a3..ea45a8001 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(null, sourcePrefix + sourceFragment); + Source newSourceWithPrefix(String sourceFragment) => newSource(sourcePrefix + sourceFragment); static const sourcePrefix = /*language=dart*/ r''' import 'package:over_react/over_react.dart'; 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 7b6accdbd..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(null, source); + final _source = newSource(source); expect(await getAllErrors(_source), isEmpty); } } @@ -59,7 +59,7 @@ ${BoilerplateValidatorDiagnosticTest.boilerplateThatRequiresGeneratedPart} '''; Future test_error() async { - final _source = newSource(null, 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(null, 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(null, 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(null, 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(null, 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(null, 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 72f3b8ecb..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(null, usageSourceWithinFnComponent); + final source = newSource(usageSourceWithinFnComponent); await expectSingleErrorAt( createSelection(source, CallbackRefDiagnosticTest.selectionToFixBlockFnBodyRefAssignment)); } Future test_blockFnBodyRefAssignmentFix() async { - var source = newSource(null, 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(null, usageSourceWithinFnComponentFixedBlockFnBodyRefAssignment); + final source = newSource(usageSourceWithinFnComponentFixedBlockFnBodyRefAssignment); await expectSingleErrorAt(createSelection(source, CallbackRefDiagnosticTest.selectionToFixArrowFnRefAssignment)); } Future test_arrowFnRefAssignmentErrorFix() async { - var source = newSource(null, 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(null, 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(null, usageSourceWithinClassComponent); + final source = newSource(usageSourceWithinClassComponent); await expectSingleErrorAt( createSelection(source, CallbackRefDiagnosticTest.selectionToFixBlockFnBodyRefAssignment)); } Future test_blockFnBodyRefAssignmentFix() async { - var source = newSource(null, 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(null, usageSourceWithinClassComponentFixedBlockFnBodyRefAssignment); + final source = newSource(usageSourceWithinClassComponentFixedBlockFnBodyRefAssignment); await expectSingleErrorAt(createSelection(source, CallbackRefDiagnosticTest.selectionToFixArrowFnRefAssignment)); } Future test_arrowFnRefAssignmentErrorFix() async { - var source = newSource(null, 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(null, 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 cc9edaf38..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(null, /*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(null, /*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(null, /*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(null, /*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(null, 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 b1363a941..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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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(null, /*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 90cfc5648..ec040d356 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/exhaustive_deps_test.dart @@ -152,7 +152,7 @@ class ObjectWithWritableField { printOnFailure('Test case source (before adding preamble): ```\n${testCase.code}\n```'); final testBase = await setUpTestBase(testCase); - final source = testBase.newSource(null, preamble + testCase.code); + final source = testBase.newSource(preamble + testCase.code); await testBase.expectNoErrors(source, errorFilter: errorFilter); }); }); @@ -171,7 +171,7 @@ class ObjectWithWritableField { final expectedErrors = testCase.errors; expect(expectedErrors, isNotEmpty); - final source = testBase.newSource(null, 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.' 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 b1e6c9442..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(null, 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(null, 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 54b58e220..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(null, /*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(null, /*language=dart*/ r''' + final source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; @@ -93,7 +93,7 @@ final Foo = uiFunction( _\$FooConfig, // ignore: undefined_identifier ); '''; - var source = newSource(null, contents('props.content')); + var source = newSource(contents('props.content')); final selection = createSelection(source, "(#props.content#)"); // Verify error. @@ -125,7 +125,7 @@ final Foo = uiFunction( _\$FooConfig, // ignore: undefined_identifier ); '''; - var source = newSource(null, contents('props.content')); + var source = newSource(contents('props.content')); final selection = createSelection(source, "(#props.content#)"); // Verify error. @@ -139,7 +139,7 @@ final Foo = uiFunction( } Future test_multipleErrorsAndFixes() async { - var source = newSource(null, /*language=dart*/ r''' + var source = newSource(/*language=dart*/ r''' import 'package:over_react/over_react.dart'; part 'test.over_react.g.dart'; 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 d8ec0a956..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 @@ -27,12 +27,14 @@ abstract class AnalysisDriverTestBase { /// 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]. /// - /// [path] must be relative; the returned source will be created within - /// [sharedContext]. - Source newSource(String? path, [String contents = '']) { + /// Creates the file at [path] if it's non-null, and otherwise uses a new, unique path. + /// + /// 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'); } 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 34c25e5bd..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(null, sourceContents); + final source = newSource(sourceContents); for (final target in selectionTargets) { final selection = createSelection(source, target); final assists = await _getAllAssists(selection); 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 e53167d37..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 @@ -75,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(null, sourceContents); + final source = newSource(sourceContents); for (final target in selectionTargets) { final selection = createSelection(source, target); await expectSingleErrorFix(selection); From edc08c67b23a312271956a2e70231dff53482da7 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 11:44:15 -0700 Subject: [PATCH 38/45] Clean up and document StubChannel --- .../test/integration/stubs.dart | 21 +++++++------------ .../server_plugin_contributor_test_base.dart | 2 +- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/tools/analyzer_plugin/test/integration/stubs.dart b/tools/analyzer_plugin/test/integration/stubs.dart index 32075a81a..fda5b9ecd 100644 --- a/tools/analyzer_plugin/test/integration/stubs.dart +++ b/tools/analyzer_plugin/test/integration/stubs.dart @@ -23,6 +23,8 @@ 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(); @@ -167,18 +169,17 @@ class PluginForTest extends StubServerPlugin AsyncAssistsMixin, AsyncDartAssistsMixin, OverReactAnalyzerPluginBase { - PluginCommunicationChannel? _channel; - @override - PluginCommunicationChannel get channel => _channel!; + late PluginCommunicationChannel channel; @override - void start(PluginCommunicationChannel channel) { - _channel = channel; - } + late OverlayResourceProvider resourceProvider; + + late Future Function(String path)? handleGetResolvedUnitResult; @override - late OverlayResourceProvider resourceProvider; + Future getResolvedUnitResult(String path) => + handleGetResolvedUnitResult?.call(path) ?? (throw UnimplementedError()); @override Future analyzeFile({required AnalysisContext analysisContext, required String path}) => @@ -191,12 +192,6 @@ class PluginForTest extends StubServerPlugin @override Future contentChanged(List paths) => throw UnimplementedError(); - @override - Future getResolvedUnitResult(String path) => - handleGetResolvedUnitResult?.call(path) ?? (throw UnimplementedError()); - - Future Function(String path)? handleGetResolvedUnitResult; - @override String get name => 'over_react (for test)'; } 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 d98fe38e9..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 @@ -153,7 +153,7 @@ abstract class ServerPluginContributorTestBase extends AnalysisDriverTestBase { _channel = StubChannel(); _plugin = PluginForTest() - ..start(_channel!) + ..channel = _channel! ..resourceProvider = OverlayResourceProvider(sharedContext.collection.contexts.single.currentSession.resourceProvider) ..handleGetResolvedUnitResult = (path) async { final result = await sharedContext.collection.contextFor(path).currentSession.getResolvedUnit(path); From d49d36aac9aac9813a4b7a226cf3340807207544 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 11:45:00 -0700 Subject: [PATCH 39/45] Remove unused dependency --- tools/analyzer_plugin/pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/analyzer_plugin/pubspec.yaml b/tools/analyzer_plugin/pubspec.yaml index 0f5815640..4c8089fdb 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -30,7 +30,6 @@ dev_dependencies: io: ^1.0.0 logging: ^1.0.1 markdown: ^4.0.0 - pub_semver: ^2.0.0 test: ^1.14.0 test_reflective_loader: ^0.2.0 uuid: '>3.0.0 <5.0.0' From c10f1d3ce1f033f20570a36713622aba98dae6b5 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 13:18:06 -0700 Subject: [PATCH 40/45] Clean up unused_local_variable ignore --- .../test/integration/diagnostics/exhaustive_deps_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 ec040d356..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'; @@ -108,7 +108,6 @@ class ObjectWithWritableField { bool errorFilter(AnalysisError error, {@required bool isFromPlugin}) => defaultErrorFilter(error, isFromPlugin: isFromPlugin) && // These are intentionally undefined references - !{'unused_local_variable'}.contains(error.code) && !(error.code == 'undefined_identifier' && error.message.contains("Undefined name 'unresolved'.")); Future setUpTestBase(TestCase testCase) async { From 8adc8ed43d852c16fe5266af20514a2725c7dfde Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 13:19:29 -0700 Subject: [PATCH 41/45] Remove outdated comment --- tools/analyzer_plugin/pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/analyzer_plugin/pubspec.yaml b/tools/analyzer_plugin/pubspec.yaml index 4c8089fdb..86618599e 100644 --- a/tools/analyzer_plugin/pubspec.yaml +++ b/tools/analyzer_plugin/pubspec.yaml @@ -7,7 +7,6 @@ environment: sdk: '>=2.12.0 <3.0.0' dependencies: analyzer: ^5.1.0 - # Can't be 0.7.0 since its results in errors around JenkinsSmiHash not existing analyzer_plugin: ^0.11.0 collection: ^1.15.0-nullsafety.4 meta: ^1.6.0 From b132fe3c0cb8d4a38e60276494cb24608db7c68a Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 13:58:31 -0700 Subject: [PATCH 42/45] Remove unused NavigationMixin to fix VS Code For some reason, the call to getResolvedUnitResult within DartNavigationMixin results in an InconsistentAnalysisException only in VS Code and not in JetBrains IDES. --- tools/analyzer_plugin/lib/src/plugin.dart | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tools/analyzer_plugin/lib/src/plugin.dart b/tools/analyzer_plugin/lib/src/plugin.dart index 24906bab1..099bc37c2 100644 --- a/tools/analyzer_plugin/lib/src/plugin.dart +++ b/tools/analyzer_plugin/lib/src/plugin.dart @@ -34,10 +34,8 @@ import 'dart:async'; import 'package:analyzer/dart/analysis/analysis_context.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/file_system/file_system.dart'; -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/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'; @@ -83,8 +81,6 @@ mixin OverReactAnalyzerPluginBase ServerPlugin, // OutlineMixin, DartOutlineMixin, DiagnosticMixin, - NavigationMixin, - DartNavigationMixin, AsyncAssistsMixin, AsyncDartAssistsMixin { @override @@ -111,9 +107,6 @@ mixin OverReactAnalyzerPluginBase // WrapUnwrapAssistContributor(), ]; - @override - List getNavigationContributors(String path) => []; - @override List getDiagnosticContributors(AnalysisContext analysisContext, String path) { // Do not use protocol.ContextRoot's optionsFile, since it's null at least in tests; @@ -178,8 +171,6 @@ mixin OverReactAnalyzerPluginBase class OverReactAnalyzerPlugin extends ServerPlugin with DiagnosticMixin, - NavigationMixin, - DartNavigationMixin, AsyncAssistsMixin, AsyncDartAssistsMixin, OverReactAnalyzerPluginBase { From e52d9943fd46c0c94d3a07e56ce645d261e42849 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 14:24:58 -0700 Subject: [PATCH 43/45] Fix missed NavigationMixin --- tools/analyzer_plugin/test/integration/stubs.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/analyzer_plugin/test/integration/stubs.dart b/tools/analyzer_plugin/test/integration/stubs.dart index fda5b9ecd..6af6f0050 100644 --- a/tools/analyzer_plugin/test/integration/stubs.dart +++ b/tools/analyzer_plugin/test/integration/stubs.dart @@ -2,7 +2,6 @@ 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/navigation_mixin.dart'; import 'package:analyzer_plugin/plugin/plugin.dart'; import 'package:analyzer_plugin/protocol/protocol.dart'; import 'package:over_react_analyzer_plugin/src/async_plugin_apis/assist.dart'; @@ -164,8 +163,6 @@ class StubServerPlugin implements ServerPlugin { class PluginForTest extends StubServerPlugin with DiagnosticMixin, - NavigationMixin, - DartNavigationMixin, AsyncAssistsMixin, AsyncDartAssistsMixin, OverReactAnalyzerPluginBase { From 9bcf9b0282ea0c830e10d52f063a116f641e1eff Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 14:39:22 -0700 Subject: [PATCH 44/45] Don't analyze non-Dart files --- tools/analyzer_plugin/lib/src/plugin.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/analyzer_plugin/lib/src/plugin.dart b/tools/analyzer_plugin/lib/src/plugin.dart index 099bc37c2..f67d28b85 100644 --- a/tools/analyzer_plugin/lib/src/plugin.dart +++ b/tools/analyzer_plugin/lib/src/plugin.dart @@ -156,6 +156,8 @@ mixin OverReactAnalyzerPluginBase @override Future analyzeFile({required AnalysisContext analysisContext, required String path}) async { + if (!path.endsWith('.dart')) return; + await runZonedGuarded(() async { final result = await analysisContext.currentSession.getResolvedUnit(path); if (result is ResolvedUnitResult) { From 2aaba91ad5e5430d59327bebb463de4fb9c0d97a Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 8 Sep 2023 14:39:36 -0700 Subject: [PATCH 45/45] Don't warn on keys based on enum values --- tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart | 6 ++++-- .../test/integration/diagnostics/bad_key_test.dart | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart b/tools/analyzer_plugin/lib/src/diagnostic/bad_key.dart index 6ea263a76..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 + // Enums have toStrings that include their name. + element is! EnumElement && + (element .tryCast() ?.lookUpConcreteMethod('toString', element.library!) ?.thisOrAncestorOfType() ?.thisType .isDartCoreObject ?? - false; + false); } /// Recursively collects expressions that are used to effectively call `toString()`: 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 ea45a8001..d5c2974ea 100644 --- a/tools/analyzer_plugin/test/integration/diagnostics/bad_key_test.dart +++ b/tools/analyzer_plugin/test/integration/diagnostics/bad_key_test.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)(),