From a1590103f0df46570dde2c5f51b94c9873470016 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Mon, 9 Feb 2026 16:18:05 -0700 Subject: [PATCH 01/10] Update dependencies to allow analyzer 8.x, 9.x, and 10.x --- pubspec.yaml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 95b1ed28d..6081d9f89 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,8 +7,8 @@ environment: dependencies: collection: ^1.15.0 - analyzer: '>=5.13.0 <8.0.0' - build: ^2.0.0 + analyzer: '>=5.13.0 <11.0.0' + build: '>=2.0.0 <5.0.0' dart_style: '>=2.0.0 <4.0.0' js: ^0.6.1+1 logging: ^1.0.0 @@ -27,12 +27,11 @@ dependencies: dev_dependencies: benchmark_harness: ^2.2.1 - build_resolvers: ^2.0.0 build_runner: ^2.0.0 build_test: '>=2.0.0 <4.0.0' build_web_compilers: '>=3.2.6 <5.0.0' dart_dev: ^4.0.1 - dependency_validator: ^3.0.0 + dependency_validator: '>=3.0.0 <6.0.0' glob: ^2.0.1 io: ^1.0.0 react_testing_library: ^3.1.1 @@ -46,3 +45,9 @@ workiva: core_checks: version: 1 react_boilerplate: disabled + +dependency_overrides: + dart_dev: + git: + url: https://github.com/Workiva/dart_dev.git + ref: raise-analyzer From 10ebb3468da1050b06f212844ec73cdfa6a6b3a6 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Mon, 9 Feb 2026 16:18:50 -0700 Subject: [PATCH 02/10] Remove test that's difficult to author in multiple analyzer/build versions We'll follow up by adding an integration test that runs the real builder --- .github/workflows/ci.yml | 4 +- .../builder/over_react_builder_test.dart | 276 ------------------ tool/update_tests_for_dart_3.sh | 18 -- .../build_test_3_updates.patch | 45 --- 4 files changed, 1 insertion(+), 342 deletions(-) delete mode 100644 test/vm_tests/builder/over_react_builder_test.dart delete mode 100755 tool/update_tests_for_dart_3.sh delete mode 100644 tool/update_tests_for_dart_3/build_test_3_updates.patch diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 030dcf219..f0758326f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,6 @@ jobs: DART_VERSION="${{ steps.setup-dart.outputs.dart-version }}" if [[ "$DART_VERSION" =~ ^3 ]]; then ./tool/delete_dart_2_only_files.sh - ./tool/update_tests_for_dart_3.sh # Stage these changes so they don't show up in the generated file `git diff` check below git add . fi @@ -146,7 +145,6 @@ jobs: DART_VERSION="${{ steps.setup-dart.outputs.dart-version }}" if [[ "$DART_VERSION" =~ ^3 ]]; then ./tool/delete_dart_2_only_files.sh - ./tool/update_tests_for_dart_3.sh fi - name: Analyze package source @@ -196,7 +194,7 @@ jobs: run: | DART_VERSION="${{ steps.setup-dart.outputs.dart-version }}" if [[ "$DART_VERSION" =~ ^3 ]]; then - (cd ../.. && ./tool/delete_dart_2_only_files.sh && ./tool/update_tests_for_dart_3.sh) + (cd ../.. && ./tool/delete_dart_2_only_files.sh) fi - name: Validate dependencies diff --git a/test/vm_tests/builder/over_react_builder_test.dart b/test/vm_tests/builder/over_react_builder_test.dart deleted file mode 100644 index 48c96b624..000000000 --- a/test/vm_tests/builder/over_react_builder_test.dart +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2020 Workiva Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -@TestOn('vm') -import 'dart:async'; -import 'dart:io'; - -import 'package:build/build.dart'; -import 'package:build_resolvers/build_resolvers.dart'; -import 'package:build_test/build_test.dart'; -import 'package:collection/collection.dart' show IterableExtension; -import 'package:logging/logging.dart'; -import 'package:over_react/src/builder/builder.dart'; -import 'package:path/path.dart' as p; -import 'package:test/test.dart'; - -main() { - group('overReactBuilder', () { - final builder = overReactBuilder(null); - final logger = Logger('overReactBuilderTestLogger'); - - // [1] Note: for this test code to analyze/run in Dart 3 with build_test 3.x, - // it needs to be temporarily modified to use new APIs. - // - // We do this in CI, and it can also be done during local development; - // see tool/update_tests_for_dart_3.sh. - // - // This is a workaround to us not being able to be compatible with build - // test 2.x and 3.x at the same time, and Dart 2 not being compatible with - // build_test 3.x. - - late AssetReader reader; - late InMemoryAssetWriter writer; // [1] - late AssetWriterSpy writerSpy; - late List logs; - - setUp(() async { - // [1] - reader = await PackageAssetReader.currentIsolate( - rootPackage: 'over_react', - ); - - writer = InMemoryAssetWriter(); // [1] - writerSpy = AssetWriterSpy(writer); - - logs = []; - final logSub = logger.onRecord.listen(logs.add); - addTearDown(logSub.cancel); - }); - - void verifyNoErrorLogs() { - expect(logs.where((log) => log.level >= Level.WARNING), isEmpty, - reason: 'Expected no logs at WARNING or SEVERE level, but got:\n' - '\t${logs.join('\n\t')}'); - } - - Future checkBuildForFile(String assetPath, String expectedOutputAssetPath, - String pathToGoldFile) async { - final inputAsset = makeAssetId(assetPath); - final expectedContent = File(pathToGoldFile).readAsStringSync(); - - final expected = { - expectedOutputAssetPath : expectedContent - }; - - await runBuilder(builder, [inputAsset], reader, writerSpy, AnalyzerResolvers(), logger: logger); - final actual = writerSpy.assetsWritten; - - checkOutputs(expected, actual, writer); - verifyNoErrorLogs(); - } - - test('does not produce a build output for a file with no over_react annotations', () async { - var basicAsset = makeAssetId('over_react|test_fixtures/source_files/no_annotations.dart'); - await runBuilder(builder, [basicAsset], reader, writerSpy, AnalyzerResolvers(), logger: logger); - - expect(writerSpy.assetsWritten, isEmpty); - verifyNoErrorLogs(); - }); - - test('warns if .over_react.g.dart part directive is present and no declarations are present, but no code is generated', () async { - var libraryAsset = makeAssetId('over_react|test_fixtures/source_files/has_part_directive_missing_gen/no_declarations.dart'); - await runBuilder(builder, [libraryAsset], reader, writerSpy, AnalyzerResolvers(), logger: logger); - final expectedWarning = logs.firstWhereOrNull((log) { - return log.level == Level.WARNING && log.message == 'An over_react part directive was found in test_fixtures/source_files/has_part_directive_missing_gen/no_declarations.dart, but no code was generated. The part directive may be unnecessary if the file does not contain any concrete components or abstract state/props classes.'; - }); - expect(expectedWarning, isNotNull, - reason: 'Expected a WARNING log for a part directive being present in a file with no generated output.'); - }); - - test('warns if .over_react.g.dart part directive is present and declarations are present, but no code is generated', () async { - var libraryAsset = makeAssetId('over_react|test_fixtures/source_files/has_part_directive_missing_gen/with_declarations.dart'); - await runBuilder(builder, [libraryAsset], reader, writerSpy, AnalyzerResolvers(), logger: logger); - final expectedWarning = logs.firstWhereOrNull((log) { - return log.level == Level.WARNING && log.message == 'An over_react part directive was found in test_fixtures/source_files/has_part_directive_missing_gen/with_declarations.dart, but no code was generated. The part directive may be unnecessary if the file does not contain any concrete components or abstract state/props classes.'; - }); - expect(expectedWarning, isNotNull, - reason: 'Expected a WARNING log for a part directive being present in a file with no generated output.'); - }); - - group('for backwards compatible boilerplate:', () { - test('builds from basic component file', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/backwards_compatible/basic.dart', - 'over_react|test_fixtures/source_files/backwards_compatible/basic.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/backwards_compatible/basic.over_react.g.dart.goldFile'); - }); - - test('builds from basic multi-part library', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/backwards_compatible/basic_library.dart', - 'over_react|test_fixtures/source_files/backwards_compatible/basic_library.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/backwards_compatible/basic_library.over_react.g.dart.goldFile'); - }); - - test('builds for props mixins', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/backwards_compatible/props_mixin.dart', - 'over_react|test_fixtures/source_files/backwards_compatible/props_mixin.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/backwards_compatible/props_mixin.over_react.g.dart.goldFile'); - }); - - test('builds for state mixins', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/backwards_compatible/state_mixin.dart', - 'over_react|test_fixtures/source_files/backwards_compatible/state_mixin.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/backwards_compatible/state_mixin.over_react.g.dart.goldFile'); - }); - - test('does not produce a build output for just a part file', () async { - var basicAsset = makeAssetId('over_react|test_fixtures/source_files/backwards_compatible/part_of_basic_library.dart'); - await runBuilder(builder, [basicAsset], reader, writerSpy, AnalyzerResolvers(), logger: logger); - - expect(writerSpy.assetsWritten, isEmpty); - verifyNoErrorLogs(); - }); - }); - - group('for Dart 2 only compatible boilerplate:', () { - test('builds from basic component file', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/dart2_only/basic.dart', - 'over_react|test_fixtures/source_files/dart2_only/basic.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/dart2_only/basic.over_react.g.dart.goldFile'); - }); - - test('builds from basic multi-part library', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/dart2_only/basic_library.dart', - 'over_react|test_fixtures/source_files/dart2_only/basic_library.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/dart2_only/basic_library.over_react.g.dart.goldFile'); - }); - - test('builds for props mixins', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/dart2_only/props_mixin.dart', - 'over_react|test_fixtures/source_files/dart2_only/props_mixin.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/dart2_only/props_mixin.over_react.g.dart.goldFile'); - }); - - test('builds for state mixins', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/dart2_only/state_mixin.dart', - 'over_react|test_fixtures/source_files/dart2_only/state_mixin.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/dart2_only/state_mixin.over_react.g.dart.goldFile'); - }); - - test('does not produce a build output for just a part file', () async { - var basicAsset = makeAssetId('over_react|test_fixtures/source_files/dart2_only/part_of_basic_library.dart'); - await runBuilder(builder, [basicAsset], reader, writerSpy, AnalyzerResolvers(), logger: logger); - - expect(writerSpy.assetsWritten, isEmpty); - verifyNoErrorLogs(); - }); - - test('fails if the .over_react.g.dart part directive is missing', () async { - var libraryAsset = makeAssetId('over_react|test_fixtures/source_files/dart2_only/missing_over_react_g_part/library.dart'); - await runBuilder(builder, [libraryAsset], reader, writerSpy, AnalyzerResolvers(), logger: logger); - final expectedLog = logs.firstWhereOrNull((log) { - return log.level == Level.SEVERE && log.message == 'Missing "part \'library.over_react.g.dart\';".'; - }); - expect(expectedLog, isNotNull, - reason: 'Expected a SEVERE log for the missing over_react part.'); - }); - - group('for Component2:', () { - test('builds from basic component file', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/dart2_only/component2/basic.dart', - 'over_react|test_fixtures/source_files/dart2_only/component2/basic.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/dart2_only/component2/basic.over_react.g.dart.goldFile'); - }); - - test('builds from basic multi-part library', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/dart2_only/component2/basic_library.dart', - 'over_react|test_fixtures/source_files/dart2_only/component2/basic_library.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/dart2_only/component2/basic_library.over_react.g.dart.goldFile'); - }); - }); - }); - - group('for mixin based boilerplate:', () { - test('builds from basic component file', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/mixin_based/basic.dart', - 'over_react|test_fixtures/source_files/mixin_based/basic.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/mixin_based/basic.over_react.g.dart.goldFile'); - }); - - test('builds from basic component file using Dart >=2.9.0 syntax', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/mixin_based/basic_two_nine.dart', - 'over_react|test_fixtures/source_files/mixin_based/basic_two_nine.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/mixin_based/basic_two_nine.over_react.g.dart.goldFile'); - }); - - test('builds from basic multi-part library', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/mixin_based/basic_library.dart', - 'over_react|test_fixtures/source_files/mixin_based/basic_library.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/mixin_based/basic_library.over_react.g.dart.goldFile'); - }); - - test('builds when props mixins and classes have type parameters', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/mixin_based/type_parameters.dart', - 'over_react|test_fixtures/source_files/mixin_based/type_parameters.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/mixin_based/type_parameters.over_react.g.dart.goldFile'); - }); - - test('builds for props mixins', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/mixin_based/props_mixin.dart', - 'over_react|test_fixtures/source_files/mixin_based/props_mixin.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/mixin_based/props_mixin.over_react.g.dart.goldFile'); - }); - - test('builds for state mixins', () async { - await checkBuildForFile( - 'over_react|test_fixtures/source_files/mixin_based/state_mixin.dart', - 'over_react|test_fixtures/source_files/mixin_based/state_mixin.over_react.g.dart', - '${p.absolute(p.current)}/test_fixtures/gold_output_files/mixin_based/state_mixin.over_react.g.dart.goldFile'); - }); - - test('does not produce a build output for just a part file', () async { - var basicAsset = makeAssetId('over_react|test_fixtures/source_files/mixin_based/part_of_basic_library.dart'); - await runBuilder(builder, [basicAsset], reader, writerSpy, AnalyzerResolvers(), logger: logger); - - expect(writerSpy.assetsWritten, isEmpty); - verifyNoErrorLogs(); - }); - - test('fails if the .over_react.g.dart part directive is missing', () async { - var libraryAsset = makeAssetId('over_react|test_fixtures/source_files/mixin_based/missing_over_react_g_part/library.dart'); - await runBuilder(builder, [libraryAsset], reader, writerSpy, AnalyzerResolvers(), logger: logger); - final expectedLog = logs.firstWhereOrNull((log) { - return log.level == Level.SEVERE && log.message == 'Missing "part \'library.over_react.g.dart\';".'; - }); - expect(expectedLog, isNotNull, - reason: 'Expected a SEVERE log for the missing over_react part.'); - }); - }); - }); -} diff --git a/tool/update_tests_for_dart_3.sh b/tool/update_tests_for_dart_3.sh deleted file mode 100755 index c1c8b06a3..000000000 --- a/tool/update_tests_for_dart_3.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -# -# This script makes updates to tests to make them compatible with Dart 3. -# This is a last-resort workaround for cases that can't analyze cleanly in both versions. -# - -set -e -set -x - -# Update over_react_builder_test.dart by applying a patch in build_test 3 (Dart 3 only); -# see comment in test file for more details on the changes. -build_test_version=$(yq '.packages.build_test.version' pubspec.lock) -if [[ "$build_test_version" == "3."* ]]; then - # This patch is derived from fixing tests in build_test 3 and getting the diff with: - # git diff test/vm_tests/builder/over_react_builder_test.dart > tool/update_tests_for_dart_3/build_test_3_updates.patch - git apply tool/update_tests_for_dart_3/build_test_3_updates.patch -fi diff --git a/tool/update_tests_for_dart_3/build_test_3_updates.patch b/tool/update_tests_for_dart_3/build_test_3_updates.patch deleted file mode 100644 index 616526fbc..000000000 --- a/tool/update_tests_for_dart_3/build_test_3_updates.patch +++ /dev/null @@ -1,45 +0,0 @@ -diff --git a/test/vm_tests/builder/over_react_builder_test.dart b/test/vm_tests/builder/over_react_builder_test.dart -index 48c96b62..b9b60b55 100644 ---- a/test/vm_tests/builder/over_react_builder_test.dart -+++ b/test/vm_tests/builder/over_react_builder_test.dart -@@ -20,6 +20,8 @@ import 'package:build/build.dart'; - import 'package:build_resolvers/build_resolvers.dart'; - import 'package:build_test/build_test.dart'; - import 'package:collection/collection.dart' show IterableExtension; -+import 'package:glob/glob.dart'; -+import 'package:glob/list_local_fs.dart'; - import 'package:logging/logging.dart'; - import 'package:over_react/src/builder/builder.dart'; - import 'package:path/path.dart' as p; -@@ -40,18 +42,27 @@ main() { - // test 2.x and 3.x at the same time, and Dart 2 not being compatible with - // build_test 3.x. - -- late AssetReader reader; -- late InMemoryAssetWriter writer; // [1] -+ late TestReaderWriter reader; -+ late TestReaderWriter writer; // [1] - late AssetWriterSpy writerSpy; - late List logs; - - setUp(() async { - // [1] -- reader = await PackageAssetReader.currentIsolate( -+ reader = TestReaderWriter( - rootPackage: 'over_react', - ); - -- writer = InMemoryAssetWriter(); // [1] -+ // Load test fixture assets from the filesystem into the test reader. -+ for (final file in Glob('test_fixtures/**').listSync().whereType()) { -+ // Write via `testing` so it's not tracked as a builder output. -+ reader.testing.writeBytes( -+ makeAssetId('over_react|${file.path}'), -+ file.readAsBytesSync(), -+ ); -+ } -+ -+ writer = TestReaderWriter(); // [1] - writerSpy = AssetWriterSpy(writer); - - logs = []; From 14379c8b07d174a0040b9a11b96bec735ea181a6 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Mon, 9 Feb 2026 16:26:22 -0700 Subject: [PATCH 03/10] Run on newer analyzers in CI --- .github/workflows/ci.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0758326f..64e2bdd58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,17 +110,17 @@ jobs: strategy: fail-fast: false matrix: - sdk: [ 2.19.6, stable ] + sdk: [ stable ] analyzer: - - ^5.13.0 # This should match the lower bound - ^6.0.0 - ^7.0.0 - exclude: - # Analyzer 6+ only resolves in Dart 3 - - sdk: 2.19.6 - analyzer: ^6.0.0 + - ^8.0.0 + - ^9.0.0 + - ^10.0.0 + # Analyzer 6+ does not resolve in Dart 2. + include: - sdk: 2.19.6 - analyzer: ^7.0.0 + analyzer: ^5.13.0 # This should match the lower bound steps: - uses: actions/checkout@v4 From 5aa7103d6c99c27c54a825eb1b1e6c4df71b9877 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Mon, 9 Feb 2026 16:29:12 -0700 Subject: [PATCH 04/10] Work around analyzer API breakages --- lib/src/builder/parsing/ast_util.dart | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/src/builder/parsing/ast_util.dart b/lib/src/builder/parsing/ast_util.dart index 5fc5f8e11..db62f1e1b 100644 --- a/lib/src/builder/parsing/ast_util.dart +++ b/lib/src/builder/parsing/ast_util.dart @@ -68,16 +68,27 @@ extension TypeAnnotationNameHelper on TypeAnnotation { /// Extension built on [NameHelper] to allow for easy access to the `name` /// field of [Identifier]s. extension TypeNameHelper on NamedType { + // Backwards compatibility for various analyzer versions that remove name/name2. + dynamic get name => name2; + dynamic get name2 => name; + dynamic get _name => name; + String get nameLexeme { + final name = this._name; + if (name is Identifier) return name.name; + if (name is Token) return name.lexeme; + if (name is String) return name; + throw UnimplementedError('Unexpected type for name: ${name.runtimeType}'); + } + /// The type name without any namespace prefixes. - String get nameWithoutPrefix => name2.lexeme; + String get nameWithoutPrefix => nameLexeme; /// The type name including the namespace prefix. String get nameWithPrefix { final prefix = importPrefix?.name.lexeme; - final name = name2.lexeme; return [ if (prefix != null) prefix, - name, + nameLexeme, ].join('.'); } } From 97fcec3e60cf13780bf5b1bbc0e67beda2b589a7 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Mon, 9 Feb 2026 16:31:03 -0700 Subject: [PATCH 05/10] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc64abd21..9a36e3b6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # OverReact Changelog +## Unreleased +- Allow analyzer 8.x, 9.x, 10.x + ## 5.5.0 - [#989] Optimize generated code to decrease dart2js compile size, saving ~577 bytes per component (when using `-03 --csp --minify`) - [#992] Fix compilation errors for legacy boilerplate defined in libraries with a Dart language version of >=3.0 From b7a104b494f0915521db35fc3bd0524d02750e14 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Mon, 9 Feb 2026 16:32:33 -0700 Subject: [PATCH 06/10] Fix bad references --- lib/src/builder/parsing/ast_util.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/builder/parsing/ast_util.dart b/lib/src/builder/parsing/ast_util.dart index db62f1e1b..7660552a7 100644 --- a/lib/src/builder/parsing/ast_util.dart +++ b/lib/src/builder/parsing/ast_util.dart @@ -69,8 +69,8 @@ extension TypeAnnotationNameHelper on TypeAnnotation { /// field of [Identifier]s. extension TypeNameHelper on NamedType { // Backwards compatibility for various analyzer versions that remove name/name2. - dynamic get name => name2; - dynamic get name2 => name; + dynamic get name => this.name2; // Use `this.` to point to real impl if it exists, not extension. + dynamic get name2 => this.name; // Use `this.` to point to real impl if it exists, not extension. dynamic get _name => name; String get nameLexeme { final name = this._name; From 027c800e9781433f47f110f438df65b3110a2c4f Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Mon, 9 Feb 2026 16:33:29 -0700 Subject: [PATCH 07/10] Format --- .../src/analyzer_helpers.dart | 32 ++++++++----------- .../transformer_utils/src/barback_utils.dart | 7 ++-- .../transformer_utils/src/node_with_meta.dart | 10 +++--- .../transformer_utils/src/text_util.dart | 7 ++-- .../src/transformed_source_file.dart | 13 +++----- .../transformer_utils/transformer_utils.dart | 6 ++-- 6 files changed, 27 insertions(+), 48 deletions(-) diff --git a/lib/src/builder/vendor/transformer_utils/src/analyzer_helpers.dart b/lib/src/builder/vendor/transformer_utils/src/analyzer_helpers.dart index c83a1c1ad..788c1ed99 100644 --- a/lib/src/builder/vendor/transformer_utils/src/analyzer_helpers.dart +++ b/lib/src/builder/vendor/transformer_utils/src/analyzer_helpers.dart @@ -34,8 +34,7 @@ String copyClassMember(ClassMember? member, String body) { if (member.isSetter) return _copySetterDeclaration(member, body); return _copyMethodDeclaration(member, body); } - throw UnsupportedError( - 'Unsupported class member type: ${member.runtimeType}. ' + throw UnsupportedError('Unsupported class member type: ${member.runtimeType}. ' 'Only FieldDeclaration and MethodDeclaration are supported.'); } @@ -45,8 +44,7 @@ String copyClassMember(ClassMember? member, String body) { /// If this is being leveraged within a transformer, you can associate the /// returned [DeclarationWithMeta] instance with the asset in which it is /// located by passing in an [assetId]. -Iterable getDeclarationsAnnotatedBy( - CompilationUnit unit, Type annotation) { +Iterable getDeclarationsAnnotatedBy(CompilationUnit unit, Type annotation) { var annotationName = _getReflectedName(annotation); return unit.declarations.where((member) { return member.metadata.any((meta) => meta.name.name == annotationName); @@ -91,8 +89,8 @@ dynamic getValue(Expression expression, /// or null if no matching annotations are found. Annotation? getMatchingAnnotation(AnnotatedNode member, Type annotationType) { // Be sure to use `originalDeclaration` so that generic parameters work. - final classMirror = mirrors.reflectClass(annotationType).originalDeclaration - as mirrors.ClassMirror; + final classMirror = + mirrors.reflectClass(annotationType).originalDeclaration as mirrors.ClassMirror; String className = mirrors.MirrorSystem.getName(classMirror.simpleName); // Find the annotation that matches [type]'s name. @@ -128,19 +126,16 @@ dynamic instantiateAnnotation(AnnotatedNode member, Type annotationType, List positionalParameters = []; matchingAnnotationArgs.arguments.forEach((argument) { - var onUnsupportedExpression = onUnsupportedArgument == null - ? null - : (_) => onUnsupportedArgument(argument); + var onUnsupportedExpression = + onUnsupportedArgument == null ? null : (_) => onUnsupportedArgument(argument); if (argument is NamedExpression) { var name = argument.name.label.name; - var value = getValue(argument.expression, - onUnsupportedExpression: onUnsupportedExpression); + var value = getValue(argument.expression, onUnsupportedExpression: onUnsupportedExpression); namedParameters[Symbol(name)] = value; } else { - var value = - getValue(argument, onUnsupportedExpression: onUnsupportedExpression); + var value = getValue(argument, onUnsupportedExpression: onUnsupportedExpression); positionalParameters.add(value); } @@ -150,12 +145,12 @@ dynamic instantiateAnnotation(AnnotatedNode member, Type annotationType, String constructorName = _getConstructorName(matchingAnnotation) ?? ''; // Be sure to use `originalDeclaration` so that generic parameters work. - final classMirror = mirrors.reflectClass(annotationType).originalDeclaration - as mirrors.ClassMirror; + final classMirror = + mirrors.reflectClass(annotationType).originalDeclaration as mirrors.ClassMirror; try { - var instanceMirror = classMirror.newInstance( - Symbol(constructorName), positionalParameters, namedParameters); + var instanceMirror = + classMirror.newInstance(Symbol(constructorName), positionalParameters, namedParameters); return instanceMirror.reflectee; } catch (e, stacktrace) { throw Exception('Unable to instantiate annotation: $matchingAnnotation. This is ' @@ -232,8 +227,7 @@ String _copyMethodDeclaration(MethodDeclaration decl, String body) { /// /// Workaround for a Dart analyzer issue where the constructor name is included /// in [annotation.name]. -String _getClassName(Annotation annotation) => - annotation.name.name.split('.').first; +String _getClassName(Annotation annotation) => annotation.name.name.split('.').first; /// Returns the name of the constructor being instantiated for [annotation], or /// null if the annotation is not the invocation of a named constructor. diff --git a/lib/src/builder/vendor/transformer_utils/src/barback_utils.dart b/lib/src/builder/vendor/transformer_utils/src/barback_utils.dart index 6b0d167b3..86012a7e2 100644 --- a/lib/src/builder/vendor/transformer_utils/src/barback_utils.dart +++ b/lib/src/builder/vendor/transformer_utils/src/barback_utils.dart @@ -27,9 +27,7 @@ import 'package:source_span/source_span.dart'; /// `lib/`. Uri assetIdToPackageUri(AssetId id) { if (!id.path.startsWith('lib/')) return Uri(path: id.path); - return Uri( - scheme: 'package', - path: path.url.join(id.package, id.path.replaceFirst('lib/', ''))); + return Uri(scheme: 'package', path: path.url.join(id.package, id.path.replaceFirst('lib/', ''))); } /// Returns a [SourceSpan] spanning from the beginning to the end of the given @@ -38,8 +36,7 @@ Uri assetIdToPackageUri(AssetId id) { SourceSpan getSpanForNode(SourceFile sourceFile, AstNode node, {bool skipCommentAndMetadata = true}) { if (skipCommentAndMetadata && node is AnnotatedNode) { - return sourceFile.span( - node.firstTokenAfterCommentAndMetadata.offset, node.end); + return sourceFile.span(node.firstTokenAfterCommentAndMetadata.offset, node.end); } return sourceFile.span(node.offset, node.end); diff --git a/lib/src/builder/vendor/transformer_utils/src/node_with_meta.dart b/lib/src/builder/vendor/transformer_utils/src/node_with_meta.dart index f83970bfb..54c806645 100644 --- a/lib/src/builder/vendor/transformer_utils/src/node_with_meta.dart +++ b/lib/src/builder/vendor/transformer_utils/src/node_with_meta.dart @@ -43,10 +43,9 @@ class NodeWithMeta { /// Construct a [NodeWithMeta] instance from an [AnnotatedNode]. /// The original node will be available via [node]. /// The instantiated annotation of type `TMeta` will be available via [meta]. - NodeWithMeta(this.node, {this.assetId}) - : this.metaNode = getMatchingAnnotation(node, TMeta) { - this._meta = instantiateAnnotation(node, TMeta, - onUnsupportedArgument: unsupportedArguments.add) as TMeta?; + NodeWithMeta(this.node, {this.assetId}) : this.metaNode = getMatchingAnnotation(node, TMeta) { + this._meta = instantiateAnnotation(node, TMeta, onUnsupportedArgument: unsupportedArguments.add) + as TMeta?; } /// Whether this node's metadata has arguments that could not be initialized using [getValue] @@ -58,8 +57,7 @@ class NodeWithMeta { /// Throws a [StateError] if this node's metadata is incomplete. TMeta? get meta { if (isIncomplete) { - throw StateError( - 'Metadata is incomplete; unsupported arguments $unsupportedArguments. ' + throw StateError('Metadata is incomplete; unsupported arguments $unsupportedArguments. ' 'Use `potentiallyIncompleteMeta` instead.'); } return _meta; diff --git a/lib/src/builder/vendor/transformer_utils/src/text_util.dart b/lib/src/builder/vendor/transformer_utils/src/text_util.dart index a60a9c8dc..a6cbe2b69 100644 --- a/lib/src/builder/vendor/transformer_utils/src/text_util.dart +++ b/lib/src/builder/vendor/transformer_utils/src/text_util.dart @@ -21,8 +21,7 @@ library; /// /// If [content] is null, a string version of the the `null` literal will be /// returned instead. -String stringLiteral(String? content, - {bool quote = true, bool useSingleQuote = true}) { +String stringLiteral(String? content, {bool quote = true, bool useSingleQuote = true}) { // Adapted from dart.convert library's JSON encoder: // https://github.com/dart-lang/sdk/blob/1.12.0/sdk/lib/convert/json.dart#L565 // @@ -129,9 +128,7 @@ String stringLiteral(String? content, writeCharCode(hexDigit(charCode & 0xf)); break; } - } else if (charCode == quoteChar || - charCode == BACKSLASH || - charCode == DOLLAR_SIGN) { + } else if (charCode == quoteChar || charCode == BACKSLASH || charCode == DOLLAR_SIGN) { if (i > offset) writeStringSlice(content, offset, i); offset = i + 1; writeCharCode(BACKSLASH); diff --git a/lib/src/builder/vendor/transformer_utils/src/transformed_source_file.dart b/lib/src/builder/vendor/transformer_utils/src/transformed_source_file.dart index 252eeb82f..e8ed2d510 100644 --- a/lib/src/builder/vendor/transformer_utils/src/transformed_source_file.dart +++ b/lib/src/builder/vendor/transformer_utils/src/transformed_source_file.dart @@ -75,8 +75,7 @@ class TransformedSourceFile { throw Exception('Overlapping replacement $replacement in replacements $_replacements.'); } - var unmodifiedText = - sourceFile.getText(lastEdge, replacement.span.start.offset); + var unmodifiedText = sourceFile.getText(lastEdge, replacement.span.start.offset); var removalText = replacement.span.text; var additionText = replacement.newText; @@ -102,9 +101,7 @@ class TransformedSourceFile { String getTransformedText() { StringBuffer transformedSource = StringBuffer(); - iterateReplacements( - onUnmodified: transformedSource.write, - onAddition: transformedSource.write); + iterateReplacements(onUnmodified: transformedSource.write, onAddition: transformedSource.write); return transformedSource.toString(); } @@ -156,11 +153,9 @@ class TransformedSourceFile { } } -SourceSpan getSpan(SourceFile sourceFile, AstNode node, - {bool skipCommentAndMetadata = true}) { +SourceSpan getSpan(SourceFile sourceFile, AstNode node, {bool skipCommentAndMetadata = true}) { if (skipCommentAndMetadata && node is AnnotatedNode) { - return sourceFile.span( - node.firstTokenAfterCommentAndMetadata.offset, node.end); + return sourceFile.span(node.firstTokenAfterCommentAndMetadata.offset, node.end); } return sourceFile.span(node.offset, node.end); diff --git a/lib/src/builder/vendor/transformer_utils/transformer_utils.dart b/lib/src/builder/vendor/transformer_utils/transformer_utils.dart index 1ee559cd8..3cac85dd7 100644 --- a/lib/src/builder/vendor/transformer_utils/transformer_utils.dart +++ b/lib/src/builder/vendor/transformer_utils/transformer_utils.dart @@ -18,9 +18,7 @@ library; export './src/analyzer_helpers.dart' show copyClassMember, getDeclarationsAnnotatedBy, instantiateAnnotation; -export './src/barback_utils.dart' - show assetIdToPackageUri, getSpanForNode; +export './src/barback_utils.dart' show assetIdToPackageUri, getSpanForNode; export './src/node_with_meta.dart' show NodeWithMeta; export './src/text_util.dart' show stringLiteral; -export './src/transformed_source_file.dart' - show TransformedSourceFile, getSpan; +export './src/transformed_source_file.dart' show TransformedSourceFile, getSpan; From 5ab3ab79d1d06ca7c42c056e90f07842e72beb5e Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Mon, 9 Feb 2026 16:40:55 -0700 Subject: [PATCH 08/10] Fix tests in newer analyzers --- .../ast_util/classish_declaration_test.dart | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/vm_tests/builder/parsing/ast_util/classish_declaration_test.dart b/test/vm_tests/builder/parsing/ast_util/classish_declaration_test.dart index 5bfe6a424..ba018f66f 100644 --- a/test/vm_tests/builder/parsing/ast_util/classish_declaration_test.dart +++ b/test/vm_tests/builder/parsing/ast_util/classish_declaration_test.dart @@ -65,7 +65,7 @@ main() { expect(parseAndGetSingleClassish(''' abstract class Foo implements Bar, Baz {} - ''').interfaces.map((i) => i.name2.name), ['Bar', 'Baz']); + ''').interfaces.map((i) => i.nameLexeme), ['Bar', 'Baz']); }); test('withClause', () { @@ -105,7 +105,7 @@ main() { expect(parseAndGetSingleClassish(''' class Foo extends Bar {} - ''').superclass?.name2.name, 'Bar'); + ''').superclass?.nameLexeme, 'Bar'); }); test('mixins', () { @@ -115,13 +115,13 @@ main() { expect(parseAndGetSingleClassish(''' class Foo extends Object with Bar, Baz {} - ''').mixins.map((m) => m.name2.name), ['Bar', 'Baz']); + ''').mixins.map((m) => m.nameLexeme), ['Bar', 'Baz']); }); test('allSuperTypes', () { expect(parseAndGetSingleClassish(''' class Foo extends Bar with Baz implements Qux {} - ''').allSuperTypes.map((m) => m.name2.name), unorderedEquals(['Bar', 'Baz', 'Qux'])); + ''').allSuperTypes.map((m) => m.nameLexeme), unorderedEquals(['Bar', 'Baz', 'Qux'])); }); }); }); @@ -148,7 +148,7 @@ main() { expect(parseAndGetSingleClassish(''' abstract class Foo = Object with Something implements Bar, Baz; - ''').interfaces.map((i) => i.name2.name), ['Bar', 'Baz']); + ''').interfaces.map((i) => i.nameLexeme), ['Bar', 'Baz']); }); test('withClause', () { @@ -180,19 +180,19 @@ main() { test('superclass', () { expect(parseAndGetSingleClassish(''' class Foo = Bar with Baz; - ''').superclass?.name2.name, 'Bar'); + ''').superclass?.nameLexeme, 'Bar'); }); test('mixins', () { expect(parseAndGetSingleClassish(''' class Foo = Object with Bar, Baz; - ''').mixins.map((m) => m.name2.name), ['Bar', 'Baz']); + ''').mixins.map((m) => m.nameLexeme), ['Bar', 'Baz']); }); test('allSuperTypes', () { expect(parseAndGetSingleClassish(''' class Foo = Bar with Baz implements Qux; - ''').allSuperTypes.map((m) => m.name2.name), unorderedEquals(['Bar', 'Baz', 'Qux'])); + ''').allSuperTypes.map((m) => m.nameLexeme), unorderedEquals(['Bar', 'Baz', 'Qux'])); }); }); }); @@ -232,7 +232,7 @@ main() { expect(parseAndGetSingleClassish(''' mixin Foo on Bar implements Baz {} - ''').interfaces.map((i) => i.name2.name), unorderedEquals(['Bar', 'Baz'])); + ''').interfaces.map((i) => i.nameLexeme), unorderedEquals(['Bar', 'Baz'])); }); test('withClause', () { @@ -268,7 +268,7 @@ main() { test('allSuperTypes', () { expect(parseAndGetSingleClassish(''' mixin Foo on Bar implements Baz {} - ''').allSuperTypes.map((m) => m.name2.name), unorderedEquals(['Bar', 'Baz'])); + ''').allSuperTypes.map((m) => m.nameLexeme), unorderedEquals(['Bar', 'Baz'])); }); }); }); From 359e437bc8f8530cf0f05d88b1061375f7ec8e64 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Mon, 9 Feb 2026 17:11:31 -0700 Subject: [PATCH 09/10] Pull in dart_dev release with analyzer updates, remove unused core_checks block --- pubspec.yaml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 6081d9f89..e6372d1cd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,7 +30,7 @@ dev_dependencies: build_runner: ^2.0.0 build_test: '>=2.0.0 <4.0.0' build_web_compilers: '>=3.2.6 <5.0.0' - dart_dev: ^4.0.1 + dart_dev: ^4.2.5 dependency_validator: '>=3.0.0 <6.0.0' glob: ^2.0.1 io: ^1.0.0 @@ -40,14 +40,3 @@ dev_dependencies: workiva_analysis_options: ^1.4.0 yaml: ^3.1.0 mocktail: ^1.0.3 - -workiva: - core_checks: - version: 1 - react_boilerplate: disabled - -dependency_overrides: - dart_dev: - git: - url: https://github.com/Workiva/dart_dev.git - ref: raise-analyzer From a6053038e902ab1a471a5278c2af60b88e42c4e2 Mon Sep 17 00:00:00 2001 From: Greg Littlefield Date: Fri, 13 Feb 2026 15:02:11 -0700 Subject: [PATCH 10/10] Tweak changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5e854f4d..21de42b0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # OverReact Changelog ## Unreleased -- Allow analyzer 8.x, 9.x, 10.x +- Update analyzer dependency to `>=5.13.0 <11.0.0` (allow 8, 9, and 10) - Remove dependency on `transformer_utils` ## 5.5.0