diff --git a/packages/custom_lint/CHANGELOG.md b/packages/custom_lint/CHANGELOG.md
index ef5db835..87e8528c 100644
--- a/packages/custom_lint/CHANGELOG.md
+++ b/packages/custom_lint/CHANGELOG.md
@@ -1,3 +1,9 @@
+## Unreleased 0.7.0
+
+- `custom_lint --fix` and the generated "Fix all " assists
+ now correctly handle imports.
+- Now supports a broad number of analyzer version.
+
## 0.6.10 - 2024-10-10
- Support installing custom_lint plugins in `dependencies:` instead of `dev_dependencies` (thanks to @dickermoshe).
diff --git a/packages/custom_lint/example/example_lint/lib/custom_lint_example_lint.dart b/packages/custom_lint/example/example_lint/lib/custom_lint_example_lint.dart
index d23aa0a4..6647f974 100644
--- a/packages/custom_lint/example/example_lint/lib/custom_lint_example_lint.dart
+++ b/packages/custom_lint/example/example_lint/lib/custom_lint_example_lint.dart
@@ -1,4 +1,7 @@
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, necessary to support lower analyzer versions
+ LintCode;
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/source/source_range.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
diff --git a/packages/custom_lint/example/example_lint/pubspec.yaml b/packages/custom_lint/example/example_lint/pubspec.yaml
index a9964685..d1ce269a 100644
--- a/packages/custom_lint/example/example_lint/pubspec.yaml
+++ b/packages/custom_lint/example/example_lint/pubspec.yaml
@@ -12,9 +12,3 @@ dependencies:
dev_dependencies:
custom_lint:
-
-dependency_overrides:
- custom_lint:
- path: ../../../custom_lint
- custom_lint_core:
- path: ../../../custom_lint_core
\ No newline at end of file
diff --git a/packages/custom_lint/example/pubspec.yaml b/packages/custom_lint/example/pubspec.yaml
index 128c97e3..47fac126 100644
--- a/packages/custom_lint/example/pubspec.yaml
+++ b/packages/custom_lint/example/pubspec.yaml
@@ -11,11 +11,3 @@ dev_dependencies:
custom_lint:
custom_lint_example_lint:
path: ./example_lint
-
-dependency_overrides:
- custom_lint:
- path: ../../custom_lint
- custom_lint_builder:
- path: ../../custom_lint_builder
- custom_lint_core:
- path: ../../custom_lint_core
diff --git a/packages/custom_lint/test/cli_process_test.dart b/packages/custom_lint/test/cli_process_test.dart
index 8f8e9514..92bddc09 100644
--- a/packages/custom_lint/test/cli_process_test.dart
+++ b/packages/custom_lint/test/cli_process_test.dart
@@ -588,6 +588,61 @@ Analyzing...
expect(process.exitCode, 1);
});
+ test('Supports adding imports', () async {
+ final fixedPlugin = createPluginSource([
+ TestLintRule(
+ code: 'oy',
+ message: 'Oy',
+ onVariable: 'if (node.name.toString().endsWith("fixed")) return;',
+ fixes: [
+ TestLintFix(
+ name: 'OyFix',
+ dartBuilderCode: r'''
+builder.importLibrary(Uri.parse('package:path/path.dart'));
+
+builder.addSimpleReplacement(node.name.sourceRange, '${node.name}fixed');
+''',
+ ),
+ ],
+ ),
+ ]);
+
+ final plugin = createPlugin(name: 'test_lint', main: fixedPlugin);
+
+ final app = createLintUsage(
+ name: 'test_app',
+ source: {
+ 'lib/main.dart': '''
+void fn() {}
+void fn2() {}
+''',
+ },
+ plugins: {'test_lint': plugin.uri},
+ );
+
+ final process = await Process.run(
+ 'dart',
+ [customLintBinPath, '--fix'],
+ workingDirectory: app.path,
+ );
+
+ expect(trimDependencyOverridesWarning(process.stderr), isEmpty);
+
+ expect(app.file('lib', 'main.dart').readAsStringSync(), '''
+import 'package:path/path.dart';
+
+void fnfixed() {}
+void fn2fixed() {}
+''');
+
+ expect(process.stdout, '''
+Analyzing...
+
+No issues found!
+''');
+ expect(process.exitCode, 0);
+ });
+
test('Can fix all lints', () async {
final plugin = createPlugin(name: 'test_lint', main: fixedPlugin);
diff --git a/packages/custom_lint/test/cli_test.dart b/packages/custom_lint/test/cli_test.dart
index f5550895..68be191b 100644
--- a/packages/custom_lint/test/cli_test.dart
+++ b/packages/custom_lint/test/cli_test.dart
@@ -1,7 +1,10 @@
import 'dart:convert';
import 'dart:io';
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:custom_lint/src/output/output_format.dart';
import 'package:test/test.dart';
diff --git a/packages/custom_lint/test/create_project.dart b/packages/custom_lint/test/create_project.dart
index b0e50fe1..3d1560d5 100644
--- a/packages/custom_lint/test/create_project.dart
+++ b/packages/custom_lint/test/create_project.dart
@@ -1,7 +1,10 @@
import 'dart:convert';
import 'dart:io';
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:path/path.dart';
import 'package:test/scaffolding.dart';
@@ -100,10 +103,13 @@ class TestLintFix {
TestLintFix({
required this.name,
this.nodeVisitor,
+ this.dartBuilderCode =
+ r"builder.addSimpleReplacement(node.name.sourceRange, '${node.name}fixed');",
});
final String name;
final String? nodeVisitor;
+ final String? dartBuilderCode;
void write(StringBuffer buffer, TestLintRule rule) {
buffer.write('''
@@ -124,9 +130,9 @@ class $name extends DartFix {
message: 'Fix ${rule.code}',
);
- ${nodeVisitor ?? r'''
+ ${nodeVisitor ?? '''
changeBuilder.addDartFileEdit((builder) {
- builder.addSimpleReplacement(node.name.sourceRange, '${node.name}fixed');
+ $dartBuilderCode
});
'''}
});
@@ -251,6 +257,14 @@ dev_dependencies:
custom_lint:
path: ${PeerProjectMeta.current.customLintPath}
${installAsDevDependency ? pluginDependencies : ""}
+
+dependency_overrides:
+ custom_lint:
+ path: ${PeerProjectMeta.current.customLintPath}
+ custom_lint_core:
+ path: ${PeerProjectMeta.current.customLintCorePath}
+ custom_lint_builder:
+ path: ${PeerProjectMeta.current.customLintBuilderPath}
''',
packageConfig: createPackageConfig(
plugins: {...plugins, ...extraPackageConfig},
diff --git a/packages/custom_lint/test/fixes_test.dart b/packages/custom_lint/test/fixes_test.dart
index 686952a8..ba8567c5 100644
--- a/packages/custom_lint/test/fixes_test.dart
+++ b/packages/custom_lint/test/fixes_test.dart
@@ -58,6 +58,8 @@ final multiChangeFixPlugin = createPluginSource([
),
]);
+const ignoreId = '<>';
+
void main() {
test('Can emit fixes', () async {
final plugin = createPlugin(
@@ -89,11 +91,7 @@ void fn2() {}
[await fixes, await fixes2]
.expand((e) => e.fixes)
.expand((e) => e.fixes)
- .where(
- (e) =>
- e.change.id != 'ignore_for_file' &&
- e.change.id != 'ignore_for_line',
- ),
+ .where((e) => e.change.id != ignoreId),
sources: ({'**/*': mainSource}, relativePath: app.path),
file: Directory.current.file(
'test',
@@ -125,11 +123,7 @@ void fn() {}
final fixes = await runner.getFixes(mainPath, 6);
expectMatchesGoldenFixes(
- fixes.fixes.expand((e) => e.fixes).where(
- (e) =>
- e.change.id != 'ignore_for_file' &&
- e.change.id != 'ignore_for_line',
- ),
+ fixes.fixes.expand((e) => e.fixes).where((e) => e.change.id != ignoreId),
sources: ({'**/*': mainSource}, relativePath: app.path),
file: Directory.current.file(
'test',
@@ -169,11 +163,7 @@ void fn4() {}
final fixes = await runner.getFixes(mainPath, 6);
expectMatchesGoldenFixes(
- fixes.fixes.expand((e) => e.fixes).where(
- (e) =>
- e.change.id != 'ignore_for_file' &&
- e.change.id != 'ignore_for_line',
- ),
+ fixes.fixes.expand((e) => e.fixes).where((e) => e.change.id != ignoreId),
sources: ({'**/*': mainSource}, relativePath: app.path),
file: Directory.current.file(
'test',
@@ -211,11 +201,7 @@ void fn2() {}
[await fixes, await fixes2]
.expand((e) => e.fixes)
.expand((e) => e.fixes)
- .where(
- (e) =>
- e.change.id != 'ignore_for_file' &&
- e.change.id != 'ignore_for_line',
- ),
+ .where((e) => e.change.id != ignoreId),
sources: ({'**/*': mainSource}, relativePath: app.path),
file: Directory.current.file(
'test',
@@ -253,11 +239,7 @@ void fn2() {}
[await fixes, await fixes2]
.expand((e) => e.fixes)
.expand((e) => e.fixes)
- .where(
- (e) =>
- e.change.id != 'ignore_for_file' &&
- e.change.id != 'ignore_for_line',
- ),
+ .where((e) => e.change.id != ignoreId),
sources: ({'**/*': mainSource}, relativePath: app.path),
file: Directory.current.file(
'test',
diff --git a/packages/custom_lint/test/goldens.dart b/packages/custom_lint/test/goldens.dart
index ba9e0038..cf2d026d 100644
--- a/packages/custom_lint/test/goldens.dart
+++ b/packages/custom_lint/test/goldens.dart
@@ -55,9 +55,6 @@ String _encodePrioritizedSourceChanges(
for (final prioritizedSourceChange in changes) {
buffer.writeln('Message: `${prioritizedSourceChange.change.message}`');
buffer.writeln('Priority: ${prioritizedSourceChange.priority}');
- if (prioritizedSourceChange.change.id != null) {
- buffer.writeln('Id: `${prioritizedSourceChange.change.id}`');
- }
if (prioritizedSourceChange.change.selection case final selection?) {
buffer.writeln(
'Selection: offset ${selection.offset} ; '
diff --git a/packages/custom_lint/test/goldens/fixes/add_ignore.diff b/packages/custom_lint/test/goldens/fixes/add_ignore.diff
index 4603ca61..b1f675ea 100644
--- a/packages/custom_lint/test/goldens/fixes/add_ignore.diff
+++ b/packages/custom_lint/test/goldens/fixes/add_ignore.diff
@@ -1,6 +1,5 @@
Message: `Ignore "hello_world" for line`
Priority: 1
-Id: `ignore_for_line`
Diff for file `lib/main.dart:1`:
```
- void fn() {}
@@ -12,7 +11,6 @@ void fn2() {}
---
Message: `Ignore "hello_world" for file`
Priority: 0
-Id: `ignore_for_file`
Diff for file `lib/main.dart:1`:
```
- void fn() {}
@@ -24,7 +22,6 @@ void fn2() {}
---
Message: `Ignore "hello_world" for line`
Priority: 1
-Id: `ignore_for_line`
Diff for file `lib/main.dart:3`:
```
void fn() {}
@@ -38,7 +35,6 @@ void fn() {}
---
Message: `Ignore "hello_world" for file`
Priority: 0
-Id: `ignore_for_file`
Diff for file `lib/main.dart:1`:
```
- void fn() {}
@@ -50,7 +46,6 @@ void fn2() {}
---
Message: `Ignore "hello_world" for line`
Priority: 1
-Id: `ignore_for_line`
Diff for file `lib/main.dart:5`:
```
void fn2() {}
@@ -62,7 +57,6 @@ void fn2() {}
---
Message: `Ignore "hello_world" for file`
Priority: 0
-Id: `ignore_for_file`
Diff for file `lib/main.dart:1`:
```
- void fn() {}
diff --git a/packages/custom_lint/test/goldens/fixes/update_ignore.diff b/packages/custom_lint/test/goldens/fixes/update_ignore.diff
index c037b634..9805e57d 100644
--- a/packages/custom_lint/test/goldens/fixes/update_ignore.diff
+++ b/packages/custom_lint/test/goldens/fixes/update_ignore.diff
@@ -1,6 +1,5 @@
Message: `Ignore "hello_world" for line`
Priority: 1
-Id: `ignore_for_line`
Diff for file `lib/main.dart:5`:
```
@@ -12,7 +11,6 @@ void fn2() {}
---
Message: `Ignore "hello_world" for file`
Priority: 0
-Id: `ignore_for_file`
Diff for file `lib/main.dart:4`:
```
void fn() {}
diff --git a/packages/custom_lint/test/goldens/ignore_quick_fix.json b/packages/custom_lint/test/goldens/ignore_quick_fix.json
index 9d7ed37e..16c0c663 100644
--- a/packages/custom_lint/test/goldens/ignore_quick_fix.json
+++ b/packages/custom_lint/test/goldens/ignore_quick_fix.json
@@ -16,7 +16,7 @@
}
],
"linkedEditGroups": [],
- "id": "ignore_for_line"
+ "id": "<>"
}
},
{
@@ -36,7 +36,7 @@
}
],
"linkedEditGroups": [],
- "id": "ignore_for_file"
+ "id": "<>"
}
},
{
@@ -56,7 +56,7 @@
}
],
"linkedEditGroups": [],
- "id": "ignore_for_line"
+ "id": "<>"
}
},
{
@@ -76,7 +76,7 @@
}
],
"linkedEditGroups": [],
- "id": "ignore_for_file"
+ "id": "<>"
}
}
]
\ No newline at end of file
diff --git a/packages/custom_lint_builder/CHANGELOG.md b/packages/custom_lint_builder/CHANGELOG.md
index c361d290..5016fd4a 100644
--- a/packages/custom_lint_builder/CHANGELOG.md
+++ b/packages/custom_lint_builder/CHANGELOG.md
@@ -1,3 +1,9 @@
+## Unreleased 0.7.0
+
+- `custom_lint --fix` and the generated "Fix all " assists
+ now correctly handle imports.
+- Now supports a broad number of analyzer version.
+
## 0.6.10 - 2024-10-10
- `custom_lint` upgraded to `0.6.10`
diff --git a/packages/custom_lint_builder/example/example_lint/lib/custom_lint_builder_example_lint.dart b/packages/custom_lint_builder/example/example_lint/lib/custom_lint_builder_example_lint.dart
index 484d93bb..6e47ae81 100644
--- a/packages/custom_lint_builder/example/example_lint/lib/custom_lint_builder_example_lint.dart
+++ b/packages/custom_lint_builder/example/example_lint/lib/custom_lint_builder_example_lint.dart
@@ -1,4 +1,7 @@
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/source/source_range.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
diff --git a/packages/custom_lint_builder/example/example_lint/pubspec.yaml b/packages/custom_lint_builder/example/example_lint/pubspec.yaml
index 8f457d1c..cca04819 100644
--- a/packages/custom_lint_builder/example/example_lint/pubspec.yaml
+++ b/packages/custom_lint_builder/example/example_lint/pubspec.yaml
@@ -9,9 +9,3 @@ dependencies:
analyzer_plugin: ^0.11.2
custom_lint_builder:
path: ../../../custom_lint_builder
-
-dependency_overrides:
- custom_lint:
- path: ../../../custom_lint
- custom_lint_core:
- path: ../../../custom_lint_core
\ No newline at end of file
diff --git a/packages/custom_lint_builder/example/pubspec.yaml b/packages/custom_lint_builder/example/pubspec.yaml
index c8ee79e7..2113819e 100644
--- a/packages/custom_lint_builder/example/pubspec.yaml
+++ b/packages/custom_lint_builder/example/pubspec.yaml
@@ -11,11 +11,3 @@ dev_dependencies:
custom_lint:
custom_lint_builder_example_lint:
path: ./example_lint
-
-dependency_overrides:
- custom_lint:
- path: ../../custom_lint
- custom_lint_builder:
- path: ../../custom_lint_builder
- custom_lint_core:
- path: ../../custom_lint_core
diff --git a/packages/custom_lint_builder/lib/src/client.dart b/packages/custom_lint_builder/lib/src/client.dart
index c74b20fe..bfa5a0ee 100644
--- a/packages/custom_lint_builder/lib/src/client.dart
+++ b/packages/custom_lint_builder/lib/src/client.dart
@@ -9,7 +9,10 @@ import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/session.dart';
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
@@ -30,11 +33,14 @@ import 'package:custom_lint/src/v2/protocol.dart';
// ignore: implementation_imports, tight versioning
import 'package:custom_lint_core/src/change_reporter.dart';
// ignore: implementation_imports, tight versioning
-import 'package:custom_lint_core/src/node_lint_visitor.dart';
+import 'package:custom_lint_core/src/fixes.dart';
// ignore: implementation_imports, tight versioning
import 'package:custom_lint_core/src/plugin_base.dart';
// ignore: implementation_imports, tight versioning
import 'package:custom_lint_core/src/resolver.dart';
+// ignore: implementation_imports, tight versioning
+import 'package:custom_lint_core/src/runnable.dart';
+import 'package:custom_lint_visitor/custom_lint_visitor.dart';
import 'package:glob/glob.dart';
import 'package:hotreloader/hotreloader.dart';
import 'package:meta/meta.dart';
@@ -91,23 +97,30 @@ Future _isVmServiceEnabled() async {
}
extension on analyzer_plugin.AnalysisErrorFixes {
- bool canBatchFix(String filePath) {
- final fixesExcludingIgnores =
- fixes.where((change) => !change.isIgnoreChange).toList();
-
- return fixesExcludingIgnores.length == 1 &&
- fixesExcludingIgnores.single.canBatchFix(filePath);
+ ({String id, int priority})? findBatchFix(String filePath) {
+ final fixToBatch = fixes
+ .where((change) => change.canBatchFix(filePath))
+ // Only a single fix at a time can be batched.
+ .singleOrNull;
+ if (fixToBatch == null) return null;
+ final id = fixToBatch.change.id;
+ if (id == null) return null;
+
+ return (
+ id: id,
+ priority: fixToBatch.priority,
+ );
}
}
extension on analyzer_plugin.PrioritizedSourceChange {
bool canBatchFix(String filePath) {
- return change.edits.every((element) => element.file == filePath);
+ return !isIgnoreChange &&
+ change.edits.every((element) => element.file == filePath);
}
bool get isIgnoreChange {
- return change.id == IgnoreCode.ignoreForFileCode ||
- change.id == IgnoreCode.ignoreForLineCode;
+ return change.id == IgnoreCode.ignoreId;
}
}
@@ -364,6 +377,26 @@ class _AnalysisErrorsKey {
int get hashCode => Object.hash(filePath, analysisContext);
}
+class _FileContext {
+ _FileContext({
+ required this.resolver,
+ required this.analysisContext,
+ required this.contextCollection,
+ required this.path,
+ required this.configs,
+ }) : key = _AnalysisErrorsKey(
+ filePath: path,
+ analysisContext: analysisContext,
+ );
+
+ final String path;
+ final _AnalysisErrorsKey key;
+ final CustomLintResolverImpl resolver;
+ final AnalysisContext analysisContext;
+ final AnalysisContextCollection contextCollection;
+ final _CustomLintAnalysisConfigs configs;
+}
+
class _ClientAnalyzerPlugin extends analyzer_plugin.ServerPlugin {
_ClientAnalyzerPlugin(
this._channel,
@@ -378,7 +411,7 @@ class _ClientAnalyzerPlugin extends analyzer_plugin.ServerPlugin {
var _customLintConfigsForAnalysisContexts =
{};
final _analysisErrorsForAnalysisContexts =
- <_AnalysisErrorsKey, Iterable>{};
+ <_AnalysisErrorsKey, List>{};
@override
List get fileGlobsToAnalyze => ['*'];
@@ -389,6 +422,27 @@ class _ClientAnalyzerPlugin extends analyzer_plugin.ServerPlugin {
@override
String get version => '1.0.0-alpha.0';
+ Future<_FileContext?> _fileContext(String path) async {
+ final contextCollection = await _contextCollection.safeFirst;
+ final analysisContext = contextCollection.contextFor(path);
+ final resolver = analysisContext.createResolverForFile(
+ resourceProvider.getFile(path),
+ );
+
+ if (resolver == null) return null;
+
+ final configs = _customLintConfigsForAnalysisContexts[analysisContext];
+ if (configs == null) return null;
+
+ return _FileContext(
+ path: path,
+ resolver: resolver,
+ analysisContext: analysisContext,
+ contextCollection: contextCollection,
+ configs: configs,
+ );
+ }
+
Future reAnalyze() async {
final contextCollection = _contextCollection.valueOrNull;
if (contextCollection != null) {
@@ -510,7 +564,7 @@ class _ClientAnalyzerPlugin extends analyzer_plugin.ServerPlugin {
runPostRunCallbacks(postRunCallbacks);
return analyzer_plugin.EditGetAssistsResult(
- await changeReporter.waitForCompletion(),
+ await changeReporter.complete(),
);
}
@@ -545,121 +599,119 @@ class _ClientAnalyzerPlugin extends analyzer_plugin.ServerPlugin {
Future handleEditGetFixes(
analyzer_plugin.EditGetFixesParams parameters,
) async {
- final contextCollection = await _contextCollection.safeFirst;
- final analysisContext = contextCollection.contextFor(parameters.file);
- final resolver = analysisContext.createResolverForFile(
- resourceProvider.getFile(parameters.file),
+ final context = await _fileContext(parameters.file);
+ if (context == null) return analyzer_plugin.EditGetFixesResult([]);
+
+ final analysisErrorsForContext =
+ _analysisErrorsForAnalysisContexts[context.key] ?? const [];
+
+ final fixes = await _computeFixes(
+ analysisErrorsForContext
+ .where((error) => error.sourceRange.contains(parameters.offset))
+ .toList(),
+ context,
+ analysisErrorsForContext,
);
- if (resolver == null) return analyzer_plugin.EditGetFixesResult([]);
- final key = _AnalysisErrorsKey(
- filePath: parameters.file,
- analysisContext: analysisContext,
+ return analyzer_plugin.EditGetFixesResult(
+ fixes.expand((fixes) {
+ return [
+ fixes.fix,
+ if (fixes.batchFixes case final batchFixes?) batchFixes,
+ ];
+ }).toList(),
);
- final configs = _customLintConfigsForAnalysisContexts[analysisContext];
+ }
- final analysisErrorsForContext =
- _analysisErrorsForAnalysisContexts[key] ?? const {};
- final errorsAtOffset = analysisErrorsForContext
- .where(
- (error) =>
- parameters.offset >= error.offset &&
- parameters.offset <= error.offset + error.length,
- )
- .toList();
+ Future<
+ List<
+ ({
+ analyzer_plugin.AnalysisErrorFixes? batchFixes,
+ analyzer_plugin.AnalysisErrorFixes fix
+ })>> _computeFixes(
+ List errorsToFix,
+ _FileContext context,
+ List analysisErrorsForContext,
+ ) async {
+ return Future.wait(
+ errorsToFix.map((error) async {
+ final toBatch = analysisErrorsForContext
+ .where((e) => e.errorCode == error.errorCode)
+ .toList();
- if (errorsAtOffset.isEmpty || configs == null) {
- return analyzer_plugin.EditGetFixesResult([]);
- }
+ final changeReporterBuilder = ChangeReporterBuilderImpl(
+ context.resolver,
+ context.configs.analysisContext.currentSession,
+ );
- final allAnalysisErrorFixes = await Future.wait([
- for (final error in analysisErrorsForContext)
- _handlesFixesForError(
+ await _runFixes(
+ context,
error,
analysisErrorsForContext,
- configs,
- resolver,
- parameters,
- ),
- ]);
-
- final analysisErrorFixesForOffset = allAnalysisErrorFixes.nonNulls
- .where(
- (fix) =>
- parameters.offset >= fix.error.location.offset &&
- parameters.offset <=
- fix.error.location.offset + fix.error.location.length,
- )
- .toList();
+ changeReporterBuilder: changeReporterBuilder,
+ );
+ final fix = await changeReporterBuilder.completeAsFixes(
+ error,
+ context,
+ );
- final fixAll = {};
- for (final fix in allAnalysisErrorFixes) {
- if (fix == null) continue;
+ final batchFix = fix.findBatchFix(context.path);
+ if (batchFix == null || toBatch.length <= 1) {
+ return (
+ fix: fix,
+ batchFixes: null,
+ );
+ }
- final errorCode = fix.error.code;
- fixAll.putIfAbsent(errorCode, () {
- final analysisErrorsWithCode = allAnalysisErrorFixes.nonNulls
- .where((fix) => fix.error.code == errorCode)
- .toList();
+ final batchReporter = ChangeReporterImpl(
+ context.configs.analysisContext.currentSession,
+ context.resolver,
+ );
- // Don't show "fix-all" unless at least two errors have the same code.
- if (analysisErrorsWithCode.length < 2) return null;
+ final batchReporterBuilder = BatchChangeReporterBuilder(
+ batchReporter.createChangeBuilder(
+ message: 'Fix all "${error.errorCode}"',
+ priority: batchFix.priority - 1,
+ ),
+ );
- final fixesWithCode = analysisErrorsWithCode
- .where((e) => e.canBatchFix(parameters.file))
- // Ignoring "ignore" fixes
- .map((e) {
- final fixesExcludingIgnores =
- e.fixes.where((change) => !change.isIgnoreChange).toList();
+ // Compute batch in sequential mode because ChangeBuilder requires it.
+ for (final toBatchError in toBatch) {
+ await _runFixes(
+ where: (fix) => fix.id == batchFix.id,
+ context,
+ toBatchError,
+ analysisErrorsForContext,
+ changeReporterBuilder: batchReporterBuilder,
+ sequential: true,
+ );
+ }
- return (fixes: fixesExcludingIgnores, error: e.error);
- }).sorted(
- (a, b) => b.error.location.offset - a.error.location.offset,
- );
+ final batchFixes =
+ await batchReporterBuilder.completeAsFixes(error, context);
- // Don't show fix-all if there's no good fix.
- if (fixesWithCode.isEmpty) return null;
-
- final priority = fixesWithCode
- .expand((e) => e.fixes)
- .map((e) => e.priority - 1)
- .firstOrNull ??
- 0;
-
- return analyzer_plugin.AnalysisErrorFixes(
- fix.error,
- fixes: [
- analyzer_plugin.PrioritizedSourceChange(
- priority,
- analyzer_plugin.SourceChange(
- 'Fix all "$errorCode"',
- edits: fixesWithCode
- .expand((e) => e.fixes)
- .expand((e) => e.change.edits)
- .toList(),
- ),
- ),
- ],
+ return (
+ fix: fix,
+ batchFixes: batchFixes,
);
- });
- }
-
- return analyzer_plugin.EditGetFixesResult([
- ...analysisErrorFixesForOffset,
- ...fixAll.values.nonNulls,
- ]);
+ }),
+ );
}
- Future _handlesFixesForError(
+ Future _runFixes(
+ _FileContext context,
AnalysisError analysisError,
- Iterable allErrors,
- _CustomLintAnalysisConfigs configs,
- CustomLintResolver resolver,
- analyzer_plugin.EditGetFixesParams parameters,
- ) async {
- final fixesForError = configs.fixes[analysisError.errorCode];
- if (fixesForError == null || fixesForError.isEmpty) {
- return null;
+ List allErrors, {
+ required ChangeReporterBuilder changeReporterBuilder,
+ bool sequential = false,
+ bool Function(Fix fix)? where,
+ }) async {
+ Iterable? fixesForError =
+ context.configs.fixes[analysisError.errorCode];
+ if (fixesForError == null) return;
+
+ if (where != null) {
+ fixesForError = fixesForError.where(where);
}
final otherErrors = allErrors
@@ -670,83 +722,76 @@ class _ClientAnalyzerPlugin extends analyzer_plugin.ServerPlugin {
)
.toList();
- final postRunCallbacks = [];
- // TODO implement verbose mode to log lint duration
- final registry = NodeLintRegistry(LintRegistry(), enableTiming: false);
- final sharedState = {};
-
- final changeReporter = ChangeReporterImpl(
- configs.analysisContext.currentSession,
- resolver,
+ await _run(
+ context,
+ fixesForError.map((fix) {
+ return (
+ runnable: fix,
+ args: (
+ reporter: changeReporterBuilder.createChangeReporter(id: fix.id),
+ analysisError: analysisError,
+ others: otherErrors,
+ )
+ );
+ }),
+ sequential: sequential,
);
- await Future.wait([
- for (final fix in fixesForError)
- _runFixStartup(
- resolver,
- fix,
- CustomLintContext(
- LintRuleNodeRegistry(registry, fix.runtimeType.toString()),
- postRunCallbacks.add,
- sharedState,
- configs.pubspec,
- ),
- ),
- ]);
- await Future.wait([
- for (final fix in fixesForError)
- _runFixRun(
- resolver,
- fix,
- CustomLintContext(
- LintRuleNodeRegistry(registry, fix.runtimeType.toString()),
- postRunCallbacks.add,
- sharedState,
- configs.pubspec,
- ),
- changeReporter,
- analysisError,
- otherErrors,
- ),
- ]);
+ await changeReporterBuilder.waitForCompletion();
+ }
- runPostRunCallbacks(postRunCallbacks);
+ Future _run(
+ _FileContext context,
+ Iterable<({Runnable runnable, ArgsT args})> allRunnables, {
+ bool sequential = false,
+ }) async {
+ // TODO implement verbose mode to log lint duration
- return analyzer_plugin.AnalysisErrorFixes(
- CustomAnalyzerConverter().convertAnalysisError(
- analysisError,
- lineInfo: resolver.lineInfo,
- severity: analysisError.errorCode.errorSeverity,
- ),
- fixes: await changeReporter.waitForCompletion(),
- );
- }
+ final bundledRunnables =
+ sequential ? allRunnables.map((e) => [e]).toList() : [allRunnables];
- Future _runFixStartup(
- CustomLintResolver resolver,
- Fix fix,
- CustomLintContext context,
- ) async {
- return _runLintZoned(
- resolver,
- () => fix.startUp(resolver, context),
- name: fix.runtimeType.toString(),
- );
- }
+ for (final runnableBundle in bundledRunnables) {
+ final registry = NodeLintRegistry(LintRegistry(), enableTiming: false);
+ final postRunCallbacks = [];
+ final sharedState = {};
- Future _runFixRun(
- CustomLintResolver resolver,
- Fix fix,
- CustomLintContext context,
- ChangeReporter changeReporter,
- AnalysisError analysisError,
- List others,
- ) async {
- return _runLintZoned(
- resolver,
- () => fix.run(resolver, changeReporter, context, analysisError, others),
- name: fix.runtimeType.toString(),
- );
+ await Future.wait([
+ for (final (:runnable, args: _) in runnableBundle)
+ _runLintZoned(
+ context.resolver,
+ () => runnable.startUp(
+ context.resolver,
+ CustomLintContext(
+ LintRuleNodeRegistry(registry, runnable.runtimeType.toString()),
+ postRunCallbacks.add,
+ sharedState,
+ context.configs.pubspec,
+ ),
+ ),
+ name: runnable.runtimeType.toString(),
+ ),
+ ]);
+
+ await Future.wait([
+ for (final (:runnable, :args) in runnableBundle)
+ _runLintZoned(
+ context.resolver,
+ () => runnable.callRun(
+ context.resolver,
+ CustomLintContext(
+ LintRuleNodeRegistry(registry, runnable.runtimeType.toString()),
+ postRunCallbacks.add,
+ sharedState,
+ context.configs.pubspec,
+ ),
+ args,
+ ),
+ name: runnable.runtimeType.toString(),
+ ),
+ ]);
+
+ runPostRunCallbacks(postRunCallbacks);
+ }
}
@override
@@ -966,41 +1011,30 @@ class _ClientAnalyzerPlugin extends analyzer_plugin.ServerPlugin {
final fixedCodes =
(Zone.current[#_fixedCodes] as Set?) ?? {};
- final allFixes = await _computeFixes(
+ final context = await _fileContext(path);
+ if (context == null) return false;
+
+ final allFixes = await _computeFistBatchFixes(
allAnalysisErrors,
- resolver,
- configs,
+ context,
fixedCodes,
path: path,
- ).toList();
+ );
if (allFixes.isEmpty) return false;
final source = resolver.source.contents.data;
- final firstFixCode = allFixes.first.analysisError.errorCode;
- final didApplyAllFixes =
- allFixes.every((e) => e.analysisError.errorCode == firstFixCode);
try {
- // Apply fixes from top to bottom.
- allFixes.sort(
- (a, b) => b.analysisError.offset - a.analysisError.offset,
- );
-
final editedSource = analyzer_plugin.SourceEdit.applySequence(
source,
- // We apply fixes only once at a time, to avoid conflicts.
- // To do so, we take the first fixed lint code, and apply fixes
- // only for that code.
allFixes
- .where((e) => e.analysisError.errorCode == firstFixCode)
+ .expand((e) => e.fixes)
+ .expand((e) => e.change.edits)
.expand((e) => e.edits),
);
- if (didApplyAllFixes) {
- // Apply fixes to the file
- io.File(path).writeAsStringSync(editedSource);
- }
+ io.File(path).writeAsStringSync(editedSource);
// Update in-memory file content before re-running analysis.
resourceProvider.setOverlay(
@@ -1017,7 +1051,7 @@ class _ClientAnalyzerPlugin extends analyzer_plugin.ServerPlugin {
},
zoneValues: {
// We update the list of fixed codes to avoid re-fixing the same lint
- #_fixedCodes: {...fixedCodes, firstFixCode.name},
+ #_fixedCodes: {...fixedCodes, ...allFixes.map((e) => e.error.code)},
},
);
} catch (e) {
@@ -1027,39 +1061,26 @@ class _ClientAnalyzerPlugin extends analyzer_plugin.ServerPlugin {
}
}
- Stream<
- ({
- AnalysisError analysisError,
- Iterable edits,
- })> _computeFixes(
+ Future> _computeFistBatchFixes(
List allAnalysisErrors,
- CustomLintResolver resolver,
- _CustomLintAnalysisConfigs configs,
+ _FileContext context,
Set fixedCodes, {
required String path,
- }) async* {
- if (!_client.fix) return;
-
- for (final analysisError in allAnalysisErrors) {
- if (fixedCodes.contains(analysisError.errorCode.name)) continue;
+ }) async {
+ if (!_client.fix) return [];
- final fixesForLint = await _handlesFixesForError(
- analysisError,
- allAnalysisErrors.toSet(),
- configs,
- resolver,
- analyzer_plugin.EditGetFixesParams(path, analysisError.offset),
- );
+ final errorToFix = allAnalysisErrors
+ .where((e) => !fixedCodes.contains(e.errorCode.name))
+ .firstOrNull;
+ if (errorToFix == null) return [];
- if (fixesForLint == null || !fixesForLint.canBatchFix(resolver.path)) {
- continue;
- }
+ final fixes = await _computeFixes(
+ [errorToFix],
+ context,
+ allAnalysisErrors,
+ );
- yield (
- analysisError: analysisError,
- edits: fixesForLint.fixes.single.change.edits.expand((e) => e.edits),
- );
- }
+ return fixes.map((e) => e.batchFixes ?? e.fix).toList();
}
/// Queue an operation to be awaited by [_awaitAnalysisDone]
@@ -1201,3 +1222,19 @@ class _AnalysisErrorListenerDelegate implements AnalysisErrorListener {
@override
void onError(AnalysisError error) => _onError(error);
}
+
+extension on ChangeReporterBuilder {
+ Future completeAsFixes(
+ AnalysisError analysisError,
+ _FileContext context,
+ ) async {
+ return analyzer_plugin.AnalysisErrorFixes(
+ CustomAnalyzerConverter().convertAnalysisError(
+ analysisError,
+ lineInfo: context.resolver.lineInfo,
+ severity: analysisError.errorCode.errorSeverity,
+ ),
+ fixes: await complete(),
+ );
+ }
+}
diff --git a/packages/custom_lint_builder/lib/src/expect_lint.dart b/packages/custom_lint_builder/lib/src/expect_lint.dart
index c69b7599..f110f371 100644
--- a/packages/custom_lint_builder/lib/src/expect_lint.dart
+++ b/packages/custom_lint_builder/lib/src/expect_lint.dart
@@ -1,4 +1,7 @@
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/source/line_info.dart';
import 'package:meta/meta.dart';
diff --git a/packages/custom_lint_builder/lib/src/ignore.dart b/packages/custom_lint_builder/lib/src/ignore.dart
index 8e48db53..b5d57b25 100644
--- a/packages/custom_lint_builder/lib/src/ignore.dart
+++ b/packages/custom_lint_builder/lib/src/ignore.dart
@@ -1,4 +1,7 @@
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:collection/collection.dart';
import 'package:custom_lint_core/custom_lint_core.dart';
@@ -101,10 +104,10 @@ List parseIgnoreForFile(String source) {
/// Built in fix to ignore a lint.
class IgnoreCode extends DartFix {
/// The code for 'ignore for line' fix.
- static const ignoreForLineCode = 'ignore_for_line';
+ static const ignoreId = '<>';
- /// The code for 'ignore for file' fix.
- static const ignoreForFileCode = 'ignore_for_file';
+ @override
+ String get id => ignoreId;
@override
void run(
@@ -120,7 +123,6 @@ class IgnoreCode extends DartFix {
final ignoreForLineChangeBuilder = reporter.createChangeBuilder(
message: 'Ignore "${analysisError.errorCode.name}" for line',
priority: 1,
- id: ignoreForLineCode,
);
ignoreForLineChangeBuilder.addDartFileEdit((builder) {
@@ -149,7 +151,6 @@ class IgnoreCode extends DartFix {
final ignoreForFileChangeBuilder = reporter.createChangeBuilder(
message: 'Ignore "${analysisError.errorCode.name}" for file',
priority: 0,
- id: ignoreForFileCode,
);
ignoreForFileChangeBuilder.addDartFileEdit((builder) {
diff --git a/packages/custom_lint_builder/pubspec.yaml b/packages/custom_lint_builder/pubspec.yaml
index 352f629e..131fb91b 100644
--- a/packages/custom_lint_builder/pubspec.yaml
+++ b/packages/custom_lint_builder/pubspec.yaml
@@ -16,6 +16,8 @@ dependencies:
# Using tight constraints as custom_lint_builder communicate with each-other
# using a specific contract
custom_lint_core: 0.6.10
+ # Using loose constraint to support a range of analyzer versions.
+ custom_lint_visitor: ^1.0.0
glob: ^2.1.1
hotreloader: ">=3.0.5 <5.0.0"
meta: ^1.7.0
diff --git a/packages/custom_lint_builder/test/analyzer_converter_test.dart b/packages/custom_lint_builder/test/analyzer_converter_test.dart
index c529adc3..654c7cb5 100644
--- a/packages/custom_lint_builder/test/analyzer_converter_test.dart
+++ b/packages/custom_lint_builder/test/analyzer_converter_test.dart
@@ -1,4 +1,7 @@
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:analyzer/file_system/memory_file_system.dart';
import 'package:analyzer/source/file_source.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
diff --git a/packages/custom_lint_core/CHANGELOG.md b/packages/custom_lint_core/CHANGELOG.md
index 8ee73af5..f0df11c3 100644
--- a/packages/custom_lint_core/CHANGELOG.md
+++ b/packages/custom_lint_core/CHANGELOG.md
@@ -1,3 +1,9 @@
+## Unreleased 0.7.0
+
+- `custom_lint --fix` and the generated "Fix all " assists
+ now correctly handle imports.
+- Now supports a broad number of analyzer version.
+
## 0.6.10 - 2024-10-10
- Added support for `dart:io` imports when using `TypeChecker.fromPackage` (thanks to @oskar-zeinomahmalat-sonarsource)
diff --git a/packages/custom_lint_core/lib/custom_lint_core.dart b/packages/custom_lint_core/lib/custom_lint_core.dart
index 12260514..deab9b5f 100644
--- a/packages/custom_lint_core/lib/custom_lint_core.dart
+++ b/packages/custom_lint_core/lib/custom_lint_core.dart
@@ -1,12 +1,20 @@
+export 'package:custom_lint_visitor/custom_lint_visitor.dart'
+ hide LintRegistry, LinterVisitor, NodeLintRegistry;
+
export 'src/assist.dart';
-export 'src/change_reporter.dart' hide ChangeReporterImpl;
+export 'src/change_reporter.dart'
+ hide
+ BatchChangeReporterBuilder,
+ BatchChangeReporterImpl,
+ ChangeBuilderImpl,
+ ChangeReporterBuilder,
+ ChangeReporterBuilderImpl,
+ ChangeReporterImpl;
export 'src/configs.dart';
-export 'src/fixes.dart';
+export 'src/fixes.dart' hide FixArgs;
export 'src/lint_codes.dart';
export 'src/lint_rule.dart';
export 'src/matcher.dart';
-export 'src/node_lint_visitor.dart'
- hide LintRegistry, LinterVisitor, NodeLintRegistry;
export 'src/package_utils.dart' hide FindProjectError;
export 'src/plugin_base.dart' hide runPostRunCallbacks;
export 'src/resolver.dart' hide CustomLintResolverImpl;
diff --git a/packages/custom_lint_core/lib/src/assist.dart b/packages/custom_lint_core/lib/src/assist.dart
index f3d3fa22..ba96444c 100644
--- a/packages/custom_lint_core/lib/src/assist.dart
+++ b/packages/custom_lint_core/lib/src/assist.dart
@@ -4,13 +4,13 @@ import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:analyzer/source/source_range.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
+import 'package:custom_lint_visitor/custom_lint_visitor.dart';
import 'package:meta/meta.dart';
import 'package:pubspec_parse/pubspec_parse.dart';
import 'change_reporter.dart';
import 'fixes.dart';
import 'lint_rule.dart';
-import 'node_lint_visitor.dart';
import 'plugin_base.dart';
import 'resolver.dart';
@@ -123,7 +123,7 @@ abstract class DartAssist extends Assist {
run(resolver, reporter, context, target);
runPostRunCallbacks(postRunCallbacks);
- return reporter.waitForCompletion();
+ return reporter.complete();
}
/// Analyze a Dart file and runs this assist in test mode.
diff --git a/packages/custom_lint_core/lib/src/change_reporter.dart b/packages/custom_lint_core/lib/src/change_reporter.dart
index 57ba593c..33c0b348 100644
--- a/packages/custom_lint_core/lib/src/change_reporter.dart
+++ b/packages/custom_lint_core/lib/src/change_reporter.dart
@@ -19,27 +19,125 @@ abstract class ChangeReporter {
ChangeBuilder createChangeBuilder({
required String message,
required int priority,
- String? id,
});
+
+ /// Waits for all [ChangeBuilder] to fully compute the source changes.
+ Future waitForCompletion();
+
+ /// Waits for completion and obtains the changes.
+ ///
+ /// This life-cycle can only be called once per [ChangeReporter].
+ Future> complete();
+}
+
+@internal
+abstract class ChangeReporterBuilder {
+ ChangeReporter createChangeReporter({required String id});
+
+ Future> complete();
+
+ Future waitForCompletion();
+}
+
+@internal
+class BatchChangeReporterBuilder extends ChangeReporterBuilder {
+ BatchChangeReporterBuilder(ChangeBuilderImpl batchBuilder)
+ : _reporter = BatchChangeReporterImpl(batchBuilder);
+
+ final BatchChangeReporterImpl _reporter;
+
+ @override
+ ChangeReporter createChangeReporter({required String id}) => _reporter;
+
+ @override
+ Future waitForCompletion() => _reporter.waitForCompletion();
+
+ @override
+ Future> complete() => _reporter.complete();
+}
+
+@internal
+class BatchChangeReporterImpl implements ChangeReporter {
+ BatchChangeReporterImpl(this.batchBuilder);
+
+ final ChangeBuilderImpl batchBuilder;
+
+ @override
+ ChangeBuilder createChangeBuilder({
+ required String message,
+ required int priority,
+ String? id,
+ }) {
+ return batchBuilder;
+ }
+
+ @override
+ Future waitForCompletion() async => batchBuilder.waitForCompletion();
+
+ @override
+ Future> complete() async {
+ return [await batchBuilder.complete()];
+ }
+}
+
+@internal
+class ChangeReporterBuilderImpl extends ChangeReporterBuilder {
+ ChangeReporterBuilderImpl(this._resolver, this._analysisSession);
+
+ final CustomLintResolver _resolver;
+ final AnalysisSession _analysisSession;
+ final List _reporters = [];
+
+ @override
+ ChangeReporter createChangeReporter({required String id}) {
+ final reporter = ChangeReporterImpl(
+ _analysisSession,
+ _resolver,
+ id: id,
+ );
+ _reporters.add(reporter);
+
+ return reporter;
+ }
+
+ @override
+ Future waitForCompletion() async {
+ await Future.wait(
+ _reporters.map((e) => e.waitForCompletion()),
+ );
+ }
+
+ @override
+ Future> complete() async {
+ final changes = Stream.fromFutures(
+ _reporters.map((e) => e.complete()),
+ );
+
+ return changes.expand((e) => e).toList();
+ }
}
/// The implementation of [ChangeReporter]
@internal
class ChangeReporterImpl implements ChangeReporter {
/// The implementation of [ChangeReporter]
- ChangeReporterImpl(this._analysisSession, this._resolver);
+ ChangeReporterImpl(
+ this._analysisSession,
+ this._resolver, {
+ this.id,
+ });
final CustomLintResolver _resolver;
final AnalysisSession _analysisSession;
- final _changeBuilders = <_ChangeBuilderImpl>[];
+ final _changeBuilders = [];
+ final String? id;
@override
- ChangeBuilder createChangeBuilder({
+ ChangeBuilderImpl createChangeBuilder({
required String message,
required int priority,
- String? id,
}) {
- final changeBuilderImpl = _ChangeBuilderImpl(
+ final changeBuilderImpl = ChangeBuilderImpl(
message,
analysisSession: _analysisSession,
priority: priority,
@@ -51,11 +149,17 @@ class ChangeReporterImpl implements ChangeReporter {
return changeBuilderImpl;
}
- /// Waits for all [ChangeBuilder] to fully compute the source changes.
- @internal
- Future> waitForCompletion() async {
+ @override
+ Future waitForCompletion() async {
+ await Future.wait(
+ _changeBuilders.map((e) => e.waitForCompletion()),
+ );
+ }
+
+ @override
+ Future> complete() async {
return Future.wait(
- _changeBuilders.map((e) => e._waitForCompletion()),
+ _changeBuilders.map((e) => e.complete()),
);
}
}
@@ -106,8 +210,9 @@ abstract class ChangeBuilder {
);
}
-class _ChangeBuilderImpl implements ChangeBuilder {
- _ChangeBuilderImpl(
+@internal
+class ChangeBuilderImpl implements ChangeBuilder {
+ ChangeBuilderImpl(
this._message, {
required this.path,
required this.priority,
@@ -121,6 +226,7 @@ class _ChangeBuilderImpl implements ChangeBuilder {
final String path;
final String? id;
final analyzer_plugin.ChangeBuilder _innerChangeBuilder;
+ var _completed = false;
final _operations = >[];
@override
@@ -175,8 +281,17 @@ class _ChangeBuilderImpl implements ChangeBuilder {
);
}
- Future _waitForCompletion() async {
+ Future waitForCompletion() async {
await Future.wait(_operations);
+ }
+
+ Future complete() async {
+ if (_completed) {
+ throw StateError('Cannot call waitForCompletion more than once');
+ }
+ _completed = true;
+
+ await waitForCompletion();
return PrioritizedSourceChange(
priority,
diff --git a/packages/custom_lint_core/lib/src/fixes.dart b/packages/custom_lint_core/lib/src/fixes.dart
index 00e2e053..5af24f37 100644
--- a/packages/custom_lint_core/lib/src/fixes.dart
+++ b/packages/custom_lint_core/lib/src/fixes.dart
@@ -2,28 +2,48 @@ import 'dart:io';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/utilities.dart';
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
+import 'package:custom_lint_visitor/custom_lint_visitor.dart';
import 'package:meta/meta.dart';
import 'package:pubspec_parse/pubspec_parse.dart';
+import 'package:uuid/uuid.dart';
import 'change_reporter.dart';
import 'lint_rule.dart';
-import 'node_lint_visitor.dart';
import 'plugin_base.dart';
import 'resolver.dart';
+import 'runnable.dart';
+
+/// Args for [Fix].
+@internal
+typedef FixArgs = ({
+ ChangeReporter reporter,
+ AnalysisError analysisError,
+ List others,
+});
+
+const _uid = Uuid();
/// {@template custom_lint_builder.lint_rule}
/// A base class for defining quick-fixes for a [LintRule]
///
/// For creating assists inside Dart files, see [DartFix].
-/// Suclassing [Fix] can be helpful if you wish to implement assists for
+/// Subclassing [Fix] can be helpful if you wish to implement assists for
/// non-Dart files (yaml, json, ...)
///
/// For usage information, see https://github.com/invertase/dart_custom_lint/blob/main/docs/fixes.md
/// {@endtemplate}
@immutable
-abstract class Fix {
+abstract class Fix extends Runnable {
+ /// A unique ID for a fix. Must be unique across all fixes of any package.
+ ///
+ /// This is used to know which fix triggered a change, for batch support.
+ late final String id = _uid.v4();
+
/// A list of glob patterns matching the files that [run] cares about.
///
/// This can include Dart files, Yaml files, ...
@@ -32,11 +52,28 @@ abstract class Fix {
/// Emits lints for a given file.
///
/// [run] will only be invoked with files respecting [filesToAnalyze]
+ @override
Future startUp(
CustomLintResolver resolver,
CustomLintContext context,
) async {}
+ @internal
+ @override
+ void callRun(
+ CustomLintResolver resolver,
+ CustomLintContext context,
+ FixArgs args,
+ ) {
+ run(
+ resolver,
+ args.reporter,
+ context,
+ args.analysisError,
+ args.others,
+ );
+ }
+
/// Emits lints for a given file.
///
/// [run] will only be invoked with files respecting [filesToAnalyze]
@@ -119,7 +156,7 @@ abstract class DartFix extends Fix {
run(resolver, reporter, context, analysisError, others);
runPostRunCallbacks(postRunCallbacks);
- return reporter.waitForCompletion();
+ return reporter.complete();
}
/// Analyze a Dart file and runs this fix in test mode.
diff --git a/packages/custom_lint_core/lib/src/lint_codes.dart b/packages/custom_lint_core/lib/src/lint_codes.dart
index 0e449389..c1c60cf5 100644
--- a/packages/custom_lint_core/lib/src/lint_codes.dart
+++ b/packages/custom_lint_core/lib/src/lint_codes.dart
@@ -4,7 +4,10 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:meta/meta.dart';
import '../custom_lint_core.dart';
diff --git a/packages/custom_lint_core/lib/src/lint_rule.dart b/packages/custom_lint_core/lib/src/lint_rule.dart
index cfa84502..dff02af9 100644
--- a/packages/custom_lint_core/lib/src/lint_rule.dart
+++ b/packages/custom_lint_core/lib/src/lint_rule.dart
@@ -6,10 +6,10 @@ import 'package:analyzer/dart/analysis/utilities.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/error.dart' show AnalysisError;
import 'package:analyzer/error/listener.dart';
+import 'package:custom_lint_visitor/custom_lint_visitor.dart';
import 'package:meta/meta.dart';
import 'package:pubspec_parse/pubspec_parse.dart';
import '../custom_lint_core.dart';
-import 'node_lint_visitor.dart';
import 'plugin_base.dart';
import 'resolver.dart';
diff --git a/packages/custom_lint_core/lib/src/runnable.dart b/packages/custom_lint_core/lib/src/runnable.dart
new file mode 100644
index 00000000..a37194bd
--- /dev/null
+++ b/packages/custom_lint_core/lib/src/runnable.dart
@@ -0,0 +1,21 @@
+import 'package:meta/meta.dart';
+
+import 'lint_rule.dart';
+import 'resolver.dart';
+
+/// A base-class for runnable objects.
+abstract class Runnable {
+ /// Initializes the runnable object.
+ Future startUp(
+ CustomLintResolver resolver,
+ CustomLintContext context,
+ );
+
+ /// Runs the runnable object.
+ @internal
+ void callRun(
+ CustomLintResolver resolver,
+ CustomLintContext context,
+ RunArgs args,
+ );
+}
diff --git a/packages/custom_lint_core/lib/src/source_range_extensions.dart b/packages/custom_lint_core/lib/src/source_range_extensions.dart
index 2af234c7..0097c192 100644
--- a/packages/custom_lint_core/lib/src/source_range_extensions.dart
+++ b/packages/custom_lint_core/lib/src/source_range_extensions.dart
@@ -1,5 +1,8 @@
import 'package:analyzer/dart/ast/syntactic_entity.dart';
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:analyzer/source/source_range.dart';
/// Adds [sourceRange]
diff --git a/packages/custom_lint_core/pubspec.yaml b/packages/custom_lint_core/pubspec.yaml
index 15cc73f3..4065c8c3 100644
--- a/packages/custom_lint_core/pubspec.yaml
+++ b/packages/custom_lint_core/pubspec.yaml
@@ -7,9 +7,10 @@ environment:
sdk: ">=3.0.0 <4.0.0"
dependencies:
- analyzer: ^6.10.0
+ analyzer: ^6.7.0
analyzer_plugin: ^0.11.0
collection: ^1.16.0
+ custom_lint_visitor: ^1.0.0
glob: ^2.1.2
matcher: ^0.12.0
meta: ^1.7.0
@@ -17,6 +18,7 @@ dependencies:
path: ^1.8.0
pubspec_parse: ^1.2.2
source_span: ^1.8.0
+ uuid: ^4.5.1
yaml: ^3.1.1
dev_dependencies:
diff --git a/packages/custom_lint_core/test/fix_test.dart b/packages/custom_lint_core/test/fix_test.dart
index bf46ab84..cf344116 100644
--- a/packages/custom_lint_core/test/fix_test.dart
+++ b/packages/custom_lint_core/test/fix_test.dart
@@ -1,6 +1,9 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/utilities.dart';
-import 'package:analyzer/error/error.dart' hide LintCode;
+import 'package:analyzer/error/error.dart'
+ hide
+ // ignore: undefined_hidden_name, Needed to support lower analyzer versions
+ LintCode;
import 'package:custom_lint_core/src/change_reporter.dart';
import 'package:custom_lint_core/src/fixes.dart';
import 'package:custom_lint_core/src/lint_rule.dart';
diff --git a/packages/custom_lint_visitor/CHANGELOG.md b/packages/custom_lint_visitor/CHANGELOG.md
new file mode 100644
index 00000000..43c4a891
--- /dev/null
+++ b/packages/custom_lint_visitor/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 1.0.0+ (6.7.0/6.11.0)
+
+Initial release
diff --git a/packages/custom_lint_visitor/LICENSE b/packages/custom_lint_visitor/LICENSE
new file mode 100644
index 00000000..3f58cd65
--- /dev/null
+++ b/packages/custom_lint_visitor/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2020 Invertase Limited
+
+ 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.
diff --git a/packages/custom_lint_visitor/README.md b/packages/custom_lint_visitor/README.md
new file mode 100644
index 00000000..2e02d64f
--- /dev/null
+++ b/packages/custom_lint_visitor/README.md
@@ -0,0 +1,27 @@
+
+
custom_lint_core
+ An package exposing base classes for defining lint rules/fixes/assists.
+
+
+
+ License
+
+
+## About
+
+`custom_lint_visitor` is a dependency of `custom_lint`, for the sake of supporting
+multiple Analyzer versions without causing too many breaking changes.
+
+It exposes various ways to traverse the tree of `AstNode`s using callbacks.
+
+## Versioning
+
+One version of `custom_lint_visitor` is released for every `analyzer` version.
+
+The version `1.0.0+6.7.0` means "Version 1.0.0 of custom_lint_visitor, for analyzer's 6.7.0 version".
+
+Whenever `custom_lint_visitor` is updated, a new version may be published for the same `analyzer` version. Such as `1.0.1+6.7.0`
+
+Depending on `custom_lint_visitor: ^1.0.0` will therefore support
+any compatible Analyzer version.
+To require a specific analyzer version, specify `analyzer: ` explicitly.
\ No newline at end of file
diff --git a/packages/custom_lint_visitor/build.yaml b/packages/custom_lint_visitor/build.yaml
new file mode 100644
index 00000000..6cb1b3de
--- /dev/null
+++ b/packages/custom_lint_visitor/build.yaml
@@ -0,0 +1,12 @@
+targets:
+ $default:
+ builders:
+ lint_visitor_generator:
+ enabled: true
+ generate_for:
+ include:
+ - "**/node_lint_visitor.dart"
+ source_gen|combining_builder:
+ options:
+ ignore_for_file:
+ - "type=lint"
diff --git a/packages/custom_lint_visitor/lib/custom_lint_visitor.dart b/packages/custom_lint_visitor/lib/custom_lint_visitor.dart
new file mode 100644
index 00000000..daf78aad
--- /dev/null
+++ b/packages/custom_lint_visitor/lib/custom_lint_visitor.dart
@@ -0,0 +1 @@
+export 'src/node_lint_visitor.dart';
diff --git a/packages/custom_lint_core/lib/src/node_lint_visitor.dart b/packages/custom_lint_visitor/lib/src/node_lint_visitor.dart
similarity index 94%
rename from packages/custom_lint_core/lib/src/node_lint_visitor.dart
rename to packages/custom_lint_visitor/lib/src/node_lint_visitor.dart
index 45e6b13e..2c58302c 100644
--- a/packages/custom_lint_core/lib/src/node_lint_visitor.dart
+++ b/packages/custom_lint_visitor/lib/src/node_lint_visitor.dart
@@ -7,14 +7,11 @@ import 'dart:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
-import 'package:meta/meta.dart';
-
import 'pragmas.dart';
part 'node_lint_visitor.g.dart';
/// Manages lint timing.
-@internal
class LintRegistry {
/// Dictionary mapping lints (by name) to timers.
final Map timers = HashMap();
diff --git a/packages/custom_lint_core/lib/src/node_lint_visitor.g.dart b/packages/custom_lint_visitor/lib/src/node_lint_visitor.g.dart
similarity index 99%
rename from packages/custom_lint_core/lib/src/node_lint_visitor.g.dart
rename to packages/custom_lint_visitor/lib/src/node_lint_visitor.g.dart
index 4b9b0c92..76036518 100644
--- a/packages/custom_lint_core/lib/src/node_lint_visitor.g.dart
+++ b/packages/custom_lint_visitor/lib/src/node_lint_visitor.g.dart
@@ -9,10 +9,8 @@ part of 'node_lint_visitor.dart';
// **************************************************************************
/// The AST visitor that runs handlers for nodes from the [_registry].
-@internal
class LinterVisitor extends GeneralizingAstVisitor {
/// The AST visitor that runs handlers for nodes from the [_registry].
- @internal
LinterVisitor(this._registry);
final NodeLintRegistry _registry;
@@ -1268,10 +1266,8 @@ class _Subscription {
}
/// The container to register visitors for separate AST node types.
-@internal
class NodeLintRegistry {
/// The container to register visitors for separate AST node types.
- @internal
NodeLintRegistry(this._lintRegistry, {required bool enableTiming})
: _enableTiming = enableTiming;
@@ -2663,10 +2659,8 @@ class NodeLintRegistry {
class LintRuleNodeRegistry {
LintRuleNodeRegistry(this.nodeLintRegistry, this.name);
- @internal
final NodeLintRegistry nodeLintRegistry;
- @internal
final String name;
@preferInline
diff --git a/packages/custom_lint_visitor/lib/src/pragmas.dart b/packages/custom_lint_visitor/lib/src/pragmas.dart
new file mode 100644
index 00000000..5f169fb3
--- /dev/null
+++ b/packages/custom_lint_visitor/lib/src/pragmas.dart
@@ -0,0 +1,2 @@
+/// Alias for vm:prefer-inline
+const preferInline = pragma('vm:prefer-inline');
diff --git a/packages/custom_lint_visitor/pubspec.yaml b/packages/custom_lint_visitor/pubspec.yaml
new file mode 100644
index 00000000..a736789f
--- /dev/null
+++ b/packages/custom_lint_visitor/pubspec.yaml
@@ -0,0 +1,16 @@
+name: custom_lint_visitor
+version: 1.0.0+6.11.0
+description: A package that exports visitors for CustomLint.
+repository: https://github.com/invertase/dart_custom_lint
+
+environment:
+ sdk: ">=3.0.0 <4.0.0"
+
+dependencies:
+ analyzer: 6.11.0
+
+dev_dependencies:
+ build_runner: ^2.3.3
+ lint_visitor_generator:
+ path: ../lint_visitor_generator
+ test: ^1.22.2
diff --git a/packages/custom_lint_visitor/pubspec_overrides.yaml b/packages/custom_lint_visitor/pubspec_overrides.yaml
new file mode 100644
index 00000000..42255ad1
--- /dev/null
+++ b/packages/custom_lint_visitor/pubspec_overrides.yaml
@@ -0,0 +1,4 @@
+# melos_managed_dependency_overrides: lint_visitor_generator
+dependency_overrides:
+ lint_visitor_generator:
+ path: ../lint_visitor_generator
diff --git a/packages/lint_visitor_generator/lib/builder.dart b/packages/lint_visitor_generator/lib/builder.dart
index cde3676b..14d4a6ff 100644
--- a/packages/lint_visitor_generator/lib/builder.dart
+++ b/packages/lint_visitor_generator/lib/builder.dart
@@ -56,10 +56,8 @@ class _Subscription {
}
/// The container to register visitors for separate AST node types.
-@internal
class NodeLintRegistry {
/// The container to register visitors for separate AST node types.
- @internal
NodeLintRegistry(this._lintRegistry, {required bool enableTiming})
: _enableTiming = enableTiming;
@@ -110,10 +108,8 @@ class NodeLintRegistry {
) {
buffer.writeln('''
/// The AST visitor that runs handlers for nodes from the [_registry].
-@internal
class LinterVisitor extends GeneralizingAstVisitor {
/// The AST visitor that runs handlers for nodes from the [_registry].
- @internal
LinterVisitor(this._registry);
final NodeLintRegistry _registry;
@@ -161,10 +157,8 @@ class LinterVisitor extends GeneralizingAstVisitor {
class LintRuleNodeRegistry {
LintRuleNodeRegistry(this.nodeLintRegistry, this.name);
- @internal
final NodeLintRegistry nodeLintRegistry;
- @internal
final String name;
''');