[analysis_server] Don't produce fix-all-in-file fixes when there's no individual fix

Fixes https://github.com/dart-lang/sdk/issues/53021

Change-Id: I674430c5e01eaeaee7867e85795eca2250ec14f1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/318840
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Danny Tuppeny 2023-08-08 18:20:39 +00:00 committed by Commit Queue
parent 651a9ba155
commit 1bae1b3dd5
3 changed files with 66 additions and 7 deletions

View file

@ -238,6 +238,7 @@ import 'package:analyzer/src/dart/error/ffi_code.g.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/parser.dart';
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart' hide FixContributor;
@ -295,6 +296,16 @@ class FixInFileProcessor {
return const <Fix>[];
}
/// Helper to create a [DartFixContextImpl] for a given error.
DartFixContextImpl createFixContext(AnalysisError error) {
return DartFixContextImpl(
instrumentationService,
workspace,
resolveResult,
error,
);
}
var generators = _getGenerators(error.errorCode);
var fixes = <Fix>[];
@ -303,13 +314,21 @@ class FixInFileProcessor {
_FixState fixState = _EmptyFixState(
ChangeBuilder(workspace: workspace),
);
for (var error in errors) {
var fixContext = DartFixContextImpl(
instrumentationService,
workspace,
resolveResult,
error,
);
// First try to fix the specific error we started from. We should only
// include fix-all-in-file when we produce an individual fix at this
// location.
fixState = await _fixError(
createFixContext(error), fixState, generator(), error);
// The original error was not fixable, don't continue.
if (!(fixState.builder as ChangeBuilderImpl).hasEdits) {
continue;
}
// Compute fixes for the rest of the errors.
for (var error in errors.where((item) => item != error)) {
var fixContext = createFixContext(error);
fixState = await _fixError(fixContext, fixState, generator(), error);
}
if (fixState is _NotEmptyFixState) {

View file

@ -367,6 +367,39 @@ void f(String a) {
);
}
/// Ensure the "fix all in file" action doesn't appear against an unfixable
/// item just because the diagnostic is also reported in a location that
/// is fixable.
///
/// https://github.com/dart-lang/sdk/issues/53021
Future<void> test_fixAll_unfixable() async {
registerLintRules();
newFile(analysisOptionsPath, '''
linter:
rules:
- non_constant_identifier_names
''');
const content = '''
/// This is unfixable because it's a top-level. It should not have a "fix all
/// in file" action.
var aaa_a^aa = '';
void f() {
/// These are here to ensure there's > 1 instance of this diagnostic to
/// allow "fix all in file" to appear.
final bbb_bbb = 0;
final ccc_ccc = 0;
}
''';
await expectNoAction(
content,
kind: CodeActionKind('quickfix.rename.toCamelCase.multi'),
title: 'Rename to camel case everywhere in file',
);
}
Future<void> test_fixAll_whenMultiple() async {
const content = '''
void f(String a) {

View file

@ -80,6 +80,13 @@ class ChangeBuilderImpl implements ChangeBuilder {
]);
}
/// Return `true` if this builder has edits to be applied.
bool get hasEdits {
return _dartFileEditBuilders.isNotEmpty ||
_genericFileEditBuilders.isNotEmpty ||
_yamlFileEditBuilders.isNotEmpty;
}
@override
SourceRange? get selectionRange => _selectionRange;