Skip to content

Commit

Permalink
Support --fix and fix-all assist (#223)
Browse files Browse the repository at this point in the history
  • Loading branch information
rrousselGit authored Feb 4, 2024
1 parent f6f020a commit f0834db
Show file tree
Hide file tree
Showing 47 changed files with 1,512 additions and 308 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ To create a custom lint, you will need two things:
# pubspec.yaml
name: my_custom_lint_package
environment:
sdk: ">=2.16.0 <3.0.0"
sdk: ">=3.0.0 <4.0.0"

dependencies:
# we will use analyzer for inspecting Dart files
Expand Down Expand Up @@ -166,7 +166,7 @@ For users to run custom_lint packages, there are a few steps:
# The pubspec.yaml of an application using our lints
name: example_app
environment:
sdk: ">=2.16.0 <3.0.0"
sdk: ">=3.0.0 <4.0.0"
dev_dependencies:
custom_lint:
Expand Down
4 changes: 4 additions & 0 deletions packages/custom_lint/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Unreleased patch

- Added support for `--fix`

## 0.5.11 - 2024-01-27

- Added support for `analysis_options.yaml` that are nt at the root of the project (thanks to @mrgnhnt96)
Expand Down
7 changes: 7 additions & 0 deletions packages/custom_lint/bin/custom_lint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ Future<void> entrypoint([List<String> args = const []]) async {
help: "Watches plugins' sources and perform a hot-reload on change",
negatable: false,
)
..addFlag(
'fix',
help: 'Apply all possible fixes to the lint issues found.',
negatable: false,
)
..addFlag(
'help',
abbr: 'h',
Expand All @@ -55,6 +60,7 @@ Future<void> entrypoint([List<String> args = const []]) async {
}

final watchMode = result['watch'] as bool;
final fix = result['fix'] as bool;
final fatalInfos = result['fatal-infos'] as bool;
final fatalWarnings = result['fatal-warnings'] as bool;
final format = result['format'] as String;
Expand All @@ -64,6 +70,7 @@ Future<void> entrypoint([List<String> args = const []]) async {
watchMode: watchMode,
fatalInfos: fatalInfos,
fatalWarnings: fatalWarnings,
fix: fix,
format: OutputFormatEnum.fromName(format),
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class _MakeProviderFinalFix extends DartFix {
message: 'Make provider final',
// This represents how high-low should this quick-fix show-up in the list
// of quick-fixes.
priority: 1,
priority: 10,
);

// Our edit will consist of editing a Dart file, so we invoke "addDartFileEdit".
Expand Down
4 changes: 4 additions & 0 deletions packages/custom_lint/lib/custom_lint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Future<void> customLint({
bool fatalInfos = true,
bool fatalWarnings = true,
OutputFormatEnum format = OutputFormatEnum.plain,
bool fix = false,
}) async {
// Reset the code
exitCode = 0;
Expand All @@ -53,6 +54,7 @@ Future<void> customLint({
fatalInfos: fatalInfos,
fatalWarnings: fatalWarnings,
format: format,
fix: fix,
);
} catch (_) {
exitCode = 1;
Expand All @@ -68,10 +70,12 @@ Future<void> _runServer(
required bool fatalInfos,
required bool fatalWarnings,
required OutputFormatEnum format,
required bool fix,
}) async {
final customLintServer = await CustomLintServer.start(
sendPort: channel.receivePort.sendPort,
watchMode: watchMode,
fix: fix,
workingDirectory: workingDirectory,
// In the CLI, only show user defined lints. Errors & logs will be
// rendered separately
Expand Down
5 changes: 4 additions & 1 deletion packages/custom_lint/lib/src/analyzer_plugin_starter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ void start(Iterable<String> _, SendPort sendPort) {
CustomLintServer.start(
sendPort: sendPort,
includeBuiltInLints: true,
// The IDE client should write to files, as what's visible in the editor
// may not be the same as what's on disk.
fix: false,
// "start" may be run by `dart analyze`, in which case we don't want to
// enable watch mode. There's no way to detect this, but this only matters
// in the CI. So we disble watch mode if we detect that we're in CI.
// in the CI. So we disable watch mode if we detect that we're in CI.
// TODO enable hot-restart only if running plugin from source (excluding pub cache)
watchMode: !isInCI,
delegate: AnalyzerPluginCustomLintDelegate(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class CustomLintServer {
required this.includeBuiltInLints,
required this.delegate,
required this.workingDirectory,
required this.fix,
});

/// Start the server while also capturing prints and errors.
Expand All @@ -38,6 +39,7 @@ class CustomLintServer {
required SendPort sendPort,
required bool watchMode,
required bool includeBuiltInLints,
required bool fix,
required CustomLintDelegate delegate,
required Directory workingDirectory,
}) {
Expand All @@ -48,6 +50,7 @@ class CustomLintServer {
() {
server = CustomLintServer._(
watchMode: watchMode,
fix: fix,
includeBuiltInLints: includeBuiltInLints,
delegate: delegate,
workingDirectory: workingDirectory,
Expand Down Expand Up @@ -96,6 +99,10 @@ class CustomLintServer {
/// Whether plugins should be started in watch mode
final bool watchMode;

/// If enabled, attempt to fix all issues found before reporting them.
/// Can only be enabled in the CLI.
final bool fix;

/// Whether plugins should include lints used for debugging.
final bool includeBuiltInLints;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ void main(List<String> args) async {
runSocket(
port: port,
host: host,
fix: ${_server.fix},
includeBuiltInLints: ${_server.includeBuiltInLints},
{$plugins},
);
Expand Down
2 changes: 1 addition & 1 deletion packages/custom_lint/lib/src/workspace.dart
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ publish_to: 'none'
.toList();

final constraintCompatibleWithAllProjects = projectMeta.fold(
VersionConstraint.any,
VersionConstraint.parse('^3.0.0'),
(acc, constraint) => acc.intersect(constraint.constraint),
);

Expand Down
1 change: 1 addition & 0 deletions packages/custom_lint/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ dev_dependencies:
build_runner: ^2.3.2
file: ^7.0.0
freezed: ^2.3.2
glob: ^2.1.2
json_serializable: ^6.5.4
test: ^1.20.2
test_process: ^2.1.0
Expand Down
127 changes: 127 additions & 0 deletions packages/custom_lint/test/cli_process_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -521,4 +521,131 @@ So, because custom_lint_client depends on test_lint from path, version solving f
});
});
});

group('--fix', () {
final fixedPlugin = createPluginSource([
TestLintRule(
code: 'oy',
message: 'Oy',
onVariable: 'if (node.name.toString().endsWith("fixed")) return;',
fixes: [TestLintFix(name: 'OyFix')],
),
]);

test('Applies possible fixes and return remaining lints', () async {
final fixedPlugin = createPluginSource([
TestLintRule(
code: 'oy',
message: 'Oy',
onVariable: 'if (node.name.toString().endsWith("fixed")) return;',
fixes: [TestLintFix(name: 'OyFix')],
),
]);

final plugin = createPlugin(name: 'test_lint', main: fixedPlugin);
final plugin2 = createPlugin(
name: 'test_lint2',
main: helloWordPluginSource,
);

final app = createLintUsage(
name: 'test_app',
source: {'lib/main.dart': 'void fn() {}'},
plugins: {
'test_lint': plugin.uri,
'test_lint2': plugin2.uri,
},
);

final process = await Process.run(
'dart',
[customLintBinPath, '--fix'],
workingDirectory: app.path,
);

expect(trimDependencyOverridesWarning(process.stderr), isEmpty);

expect(
app.file('lib', 'main.dart').readAsStringSync(),
'void fnfixed() {}',
);

expect(process.stdout, '''
Analyzing...
lib/main.dart:1:6 • Hello world • hello_world • INFO
1 issue found.
''');
expect(process.exitCode, 1);
});

test('Can fix all lints', () async {
final plugin = createPlugin(name: 'test_lint', main: fixedPlugin);

final app = createLintUsage(
name: 'test_app',
source: {'lib/main.dart': 'void fn() {}'},
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(),
'void fnfixed() {}',
);

expect(process.stdout, '''
Analyzing...
No issues found!
''');
expect(process.exitCode, 0);
});

test(
'Does not attempt at fixing the same lints again if a fix did not work',
() async {
final uselessFix = createPluginSource([
TestLintRule(
code: 'oy',
message: 'Oy',
onVariable: 'if (node.name.toString().endsWith("fixed")) return;',
fixes: [TestLintFix(name: 'OyFix', nodeVisitor: '')],
),
]);

final plugin = createPlugin(name: 'test_lint', main: uselessFix);

final app = createLintUsage(
name: 'test_app',
source: {'lib/main.dart': 'void fn() {}'},
plugins: {'test_lint': plugin.uri},
);

final process = await Process.run(
'dart',
[customLintBinPath, '--fix'],
workingDirectory: app.path,
);

expect(trimDependencyOverridesWarning(process.stderr), isEmpty);

expect(process.stdout, '''
Analyzing...
lib/main.dart:1:6 • Oy • oy • INFO
1 issue found.
''');
expect(process.exitCode, 1);
});
});
}
4 changes: 2 additions & 2 deletions packages/custom_lint/test/cli_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ invalid;
^^^^^^^
'''),
matchIgnoringAnsi(contains, '''
lib/custom_lint_client.dart:15:29: Error: Undefined name 'createPlugin'.
lib/custom_lint_client.dart:16:29: Error: Undefined name 'createPlugin'.
{'test_lint': test_lint.createPlugin,
^^^^^^^^^^^^
'''),
Expand Down Expand Up @@ -377,7 +377,7 @@ int x = 'oy';
^
'''),
matchIgnoringAnsi(contains, '''
lib/custom_lint_client.dart:17:26: Error: Undefined name 'createPlugin'.
lib/custom_lint_client.dart:18:26: Error: Undefined name 'createPlugin'.
'test_lint2': test_lint2.createPlugin,
^^^^^^^^^^^^
'''),
Expand Down
Loading

0 comments on commit f0834db

Please sign in to comment.