blob: 66c8c45a770f1fe905af35cbdb0e5134e9b8a4d5 [file] [log] [blame]
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// 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 'dart:core';
import 'package:analysis_server/plugin/edit/fix/fix_dart.dart';
import 'package:analysis_server/src/services/correction/change_workspace.dart';
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
import 'package:analysis_server/src/services/correction/dart/add_await.dart';
import 'package:analysis_server/src/services/correction/dart/add_const.dart';
import 'package:analysis_server/src/services/correction/dart/add_diagnostic_property_reference.dart';
import 'package:analysis_server/src/services/correction/dart/add_override.dart';
import 'package:analysis_server/src/services/correction/dart/convert_add_all_to_spread.dart';
import 'package:analysis_server/src/services/correction/dart/convert_conditional_expression_to_if_element.dart';
import 'package:analysis_server/src/services/correction/dart/convert_documentation_into_line.dart';
import 'package:analysis_server/src/services/correction/dart/convert_map_from_iterable_to_for_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_quotes.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_contains.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_generic_function_syntax.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_if_null.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_int_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_list_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_map_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_null_aware.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_relative_import.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_set_literal.dart';
import 'package:analysis_server/src/services/correction/dart/convert_to_where_type.dart';
import 'package:analysis_server/src/services/correction/dart/create_method.dart';
import 'package:analysis_server/src/services/correction/dart/make_final.dart';
import 'package:analysis_server/src/services/correction/dart/remove_argument.dart';
import 'package:analysis_server/src/services/correction/dart/remove_await.dart';
import 'package:analysis_server/src/services/correction/dart/remove_const.dart';
import 'package:analysis_server/src/services/correction/dart/remove_duplicate_case.dart';
import 'package:analysis_server/src/services/correction/dart/remove_empty_catch.dart';
import 'package:analysis_server/src/services/correction/dart/remove_empty_constructor_body.dart';
import 'package:analysis_server/src/services/correction/dart/remove_empty_else.dart';
import 'package:analysis_server/src/services/correction/dart/remove_empty_statement.dart';
import 'package:analysis_server/src/services/correction/dart/remove_initializer.dart';
import 'package:analysis_server/src/services/correction/dart/remove_interpolation_braces.dart';
import 'package:analysis_server/src/services/correction/dart/remove_method_declaration.dart';
import 'package:analysis_server/src/services/correction/dart/remove_operator.dart';
import 'package:analysis_server/src/services/correction/dart/remove_this_expression.dart';
import 'package:analysis_server/src/services/correction/dart/remove_type_annotation.dart';
import 'package:analysis_server/src/services/correction/dart/remove_unnecessary_new.dart';
import 'package:analysis_server/src/services/correction/dart/rename_to_camel_case.dart';
import 'package:analysis_server/src/services/correction/dart/replace_cascade_with_dot.dart';
import 'package:analysis_server/src/services/correction/dart/replace_colon_with_equals.dart';
import 'package:analysis_server/src/services/correction/dart/replace_final_with_const.dart';
import 'package:analysis_server/src/services/correction/dart/replace_null_with_closure.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_conditional_assignment.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_is_empty.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_tear_off.dart';
import 'package:analysis_server/src/services/correction/dart/replace_with_var.dart';
import 'package:analysis_server/src/services/correction/dart/sort_child_property_last.dart';
import 'package:analysis_server/src/services/correction/dart/use_curly_braces.dart';
import 'package:analysis_server/src/services/correction/dart/use_is_not_empty.dart';
import 'package:analysis_server/src/services/correction/dart/use_rethrow.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/correction/fix_internal.dart';
import 'package:analysis_server/src/services/linter/lint_names.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
/// A fix producer that produces changes to fix multiple diagnostics.
class BulkFixProcessor {
/// A map from the name of a lint rule to a list of generators used to create
/// the correction producer used to build a fix for that diagnostic. Most
/// entries will have only one generator. In cases where there is more than
/// one, they will be applied in series and the expectation is that only one
/// will produce a change for a given fix. If more than one change is produced
/// the result will almost certainly be invalid code. The generators used for
/// non-lint diagnostics are in the [nonLintProducerMap].
static const Map<String, List<ProducerGenerator>> lintProducerMap = {
LintNames.annotate_overrides: [
AddOverride.newInstance,
],
LintNames.avoid_annotating_with_dynamic: [
RemoveTypeAnnotation.newInstance,
],
LintNames.avoid_empty_else: [
RemoveEmptyElse.newInstance,
],
LintNames.avoid_init_to_null: [
RemoveInitializer.newInstance,
],
LintNames.avoid_redundant_argument_values: [
RemoveArgument.newInstance,
],
LintNames.avoid_return_types_on_setters: [
RemoveTypeAnnotation.newInstance,
],
LintNames.avoid_single_cascade_in_expression_statements: [
ReplaceCascadeWithDot.newInstance,
],
LintNames.avoid_types_on_closure_parameters: [
RemoveTypeAnnotation.newInstance,
],
LintNames.await_only_futures: [
RemoveAwait.newInstance,
],
LintNames.curly_braces_in_flow_control_structures: [
UseCurlyBraces.newInstance,
],
LintNames.diagnostic_describe_all_properties: [
AddDiagnosticPropertyReference.newInstance,
],
LintNames.empty_catches: [
RemoveEmptyCatch.newInstance,
],
LintNames.empty_constructor_bodies: [
RemoveEmptyConstructorBody.newInstance,
],
LintNames.empty_statements: [
RemoveEmptyStatement.newInstance,
],
LintNames.hash_and_equals: [CreateMethod.equalsOrHashCode],
LintNames.no_duplicate_case_values: [
RemoveDuplicateCase.newInstance,
],
LintNames.non_constant_identifier_names: [
RenameToCamelCase.newInstance,
],
LintNames.null_closures: [
ReplaceNullWithClosure.newInstance,
],
LintNames.omit_local_variable_types: [
ReplaceWithVar.newInstance,
],
LintNames.prefer_adjacent_string_concatenation: [
RemoveOperator.newInstance,
],
LintNames.prefer_collection_literals: [
ConvertToListLiteral.newInstance,
ConvertToMapLiteral.newInstance,
ConvertToSetLiteral.newInstance,
],
LintNames.prefer_conditional_assignment: [
ReplaceWithConditionalAssignment.newInstance,
],
// TODO (pq): can produce results incompatible w/ `unnecessary_const`
// LintNames.prefer_const_constructors: [
// AddConst.newInstance,
// ReplaceNewWithConst.newInstance,
// ],
LintNames.prefer_const_constructors_in_immutables: [
AddConst.newInstance,
],
LintNames.prefer_const_declarations: [
ReplaceFinalWithConst.newInstance,
],
LintNames.prefer_contains: [
ConvertToContains.newInstance,
],
LintNames.prefer_equal_for_default_values: [
ReplaceColonWithEquals.newInstance,
],
LintNames.prefer_final_fields: [
MakeFinal.newInstance,
],
LintNames.prefer_final_locals: [
MakeFinal.newInstance,
],
LintNames.prefer_for_elements_to_map_fromIterable: [
ConvertMapFromIterableToForLiteral.newInstance,
],
LintNames.prefer_generic_function_type_aliases: [
ConvertToGenericFunctionSyntax.newInstance,
],
LintNames.prefer_if_elements_to_conditional_expressions: [
ConvertConditionalExpressionToIfElement.newInstance,
],
LintNames.prefer_if_null_operators: [
ConvertToIfNull.newInstance,
],
LintNames.prefer_int_literals: [
ConvertToIntLiteral.newInstance,
],
LintNames.prefer_is_empty: [
ReplaceWithIsEmpty.newInstance,
],
LintNames.prefer_is_not_empty: [
UesIsNotEmpty.newInstance,
],
LintNames.prefer_iterable_whereType: [
ConvertToWhereType.newInstance,
],
LintNames.prefer_null_aware_operators: [
ConvertToNullAware.newInstance,
],
LintNames.prefer_relative_imports: [
ConvertToRelativeImport.newInstance,
],
LintNames.prefer_single_quotes: [
ConvertToSingleQuotes.newInstance,
],
LintNames.prefer_spread_collections: [
ConvertAddAllToSpread.newInstance,
],
LintNames.slash_for_doc_comments: [
ConvertDocumentationIntoLine.newInstance,
],
LintNames.sort_child_properties_last: [
SortChildPropertyLast.newInstance,
],
LintNames.type_init_formals: [
RemoveTypeAnnotation.newInstance,
],
LintNames.unawaited_futures: [
AddAwait.newInstance,
],
LintNames.unnecessary_brace_in_string_interps: [
RemoveInterpolationBraces.newInstance,
],
LintNames.unnecessary_const: [
RemoveUnnecessaryConst.newInstance,
],
LintNames.unnecessary_lambdas: [
ReplaceWithTearOff.newInstance,
],
LintNames.unnecessary_new: [
RemoveUnnecessaryNew.newInstance,
],
LintNames.unnecessary_overrides: [
RemoveMethodDeclaration.newInstance,
],
LintNames.unnecessary_this: [
RemoveThisExpression.newInstance,
],
LintNames.use_rethrow_when_possible: [
UseRethrow.newInstance,
],
};
/// A map from an error code to a generator used to create the correction
/// producer used to build a fix for that diagnostic. The generators used for
/// lint rules are in the [lintProducerMap].
static const Map<ErrorCode, ProducerGenerator> nonLintProducerMap = {};
final DartChangeWorkspace workspace;
/// The change builder used to build the changes required to fix the
/// diagnostics.
ChangeBuilder builder;
BulkFixProcessor(this.workspace) {
builder = ChangeBuilder(workspace: workspace);
}
Future<ChangeBuilder> fixErrorsInLibraries(List<String> libraryPaths) async {
for (var path in libraryPaths) {
var session = workspace.getSession(path);
var libraryResult = await session.getResolvedLibrary(path);
await _fixErrorsInLibrary(libraryResult);
}
return builder;
}
Future<void> _fixErrorsInLibrary(ResolvedLibraryResult libraryResult) async {
for (var unitResult in libraryResult.units) {
final fixContext = DartFixContextImpl(
workspace,
unitResult,
null,
(name) => [],
);
for (var error in unitResult.errors) {
await _fixSingleError(fixContext, unitResult, error);
}
}
}
Future<void> _fixSingleError(DartFixContext fixContext,
ResolvedUnitResult unitResult, AnalysisError error) async {
var context = CorrectionProducerContext(
dartFixContext: fixContext,
diagnostic: error,
resolvedResult: unitResult,
selectionOffset: error.offset,
selectionLength: error.length,
workspace: workspace,
);
var setupSuccess = context.setupCompute();
if (!setupSuccess) {
return;
}
Future<void> compute(CorrectionProducer producer) async {
producer.configure(context);
await producer.compute(builder);
}
var errorCode = error.errorCode;
if (errorCode is LintCode) {
var generators = lintProducerMap[errorCode.name];
if (generators != null) {
for (var generator in generators) {
await compute(generator());
}
}
} else {
var generator = nonLintProducerMap[errorCode];
if (generator != null) {
await compute(generator());
}
}
}
}