blob: 7ff90d2502591104cb1f38ccc4d7317e99c4562f [file] [log] [blame]
// Copyright (c) 2014, 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.
library services.correction.fix;
import 'package:analysis_services/correction/change.dart';
import 'package:analysis_services/correction/source_range_factory.dart' as rf;
import 'package:analysis_services/search/search_engine.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/source.dart';
/**
* Computes [Fix]s for the given [AnalysisError].
*
* Returns the computed [Fix]s, not `null`.
*/
List<Fix> computeFixes(SearchEngine searchEngine, String file,
CompilationUnit unit, AnalysisError error) {
var processor = new _FixProcessor(searchEngine, file, unit, error);
return processor.compute();
}
/**
* A description of a single proposed fix for some problem.
*/
class Fix {
final FixKind kind;
final Change change;
Fix(this.kind, this.change);
@override
String toString() {
return '[kind=$kind, change=$change]';
}
}
/**
* An enumeration of possible quick fix kinds.
*/
class FixKind {
static const ADD_PACKAGE_DEPENDENCY =
const FixKind(
'QF_ADD_PACKAGE_DEPENDENCY',
50,
"Add dependency on package '%s'");
static const ADD_SUPER_CONSTRUCTOR_INVOCATION =
const FixKind(
'QF_ADD_SUPER_CONSTRUCTOR_INVOCATION',
50,
"Add super constructor %s invocation");
static const CHANGE_TO = const FixKind('QF_CHANGE_TO', 51, "Change to '%s'");
static const CHANGE_TO_STATIC_ACCESS =
const FixKind(
'QF_CHANGE_TO_STATIC_ACCESS',
50,
"Change access to static using '%s'");
static const CREATE_CLASS =
const FixKind('QF_CREATE_CLASS', 50, "Create class '%s'");
static const CREATE_CONSTRUCTOR =
const FixKind('QF_CREATE_CONSTRUCTOR', 50, "Create constructor '%s'");
static const CREATE_CONSTRUCTOR_SUPER =
const FixKind(
'QF_CREATE_CONSTRUCTOR_SUPER',
50,
"Create constructor to call %s");
static const CREATE_FUNCTION =
const FixKind('QF_CREATE_FUNCTION', 49, "Create function '%s'");
static const CREATE_METHOD =
const FixKind('QF_CREATE_METHOD', 50, "Create method '%s'");
static const CREATE_MISSING_OVERRIDES =
const FixKind(
'QF_CREATE_MISSING_OVERRIDES',
50,
"Create %d missing override(s)");
static const CREATE_NO_SUCH_METHOD =
const FixKind('QF_CREATE_NO_SUCH_METHOD', 49, "Create 'noSuchMethod' method");
static const CREATE_PART =
const FixKind('QF_CREATE_PART', 50, "Create part '%s'");
static const IMPORT_LIBRARY_PREFIX =
const FixKind(
'QF_IMPORT_LIBRARY_PREFIX',
51,
"Use imported library '%s' with prefix '%s'");
static const IMPORT_LIBRARY_PROJECT =
const FixKind('QF_IMPORT_LIBRARY_PROJECT', 51, "Import library '%s'");
static const IMPORT_LIBRARY_SDK =
const FixKind('QF_IMPORT_LIBRARY_SDK', 51, "Import library '%s'");
static const IMPORT_LIBRARY_SHOW =
const FixKind('QF_IMPORT_LIBRARY_SHOW', 51, "Update library '%s' import");
static const INSERT_SEMICOLON =
const FixKind('QF_INSERT_SEMICOLON', 50, "Insert ';'");
static const MAKE_CLASS_ABSTRACT =
const FixKind('QF_MAKE_CLASS_ABSTRACT', 50, "Make class '%s' abstract");
static const REMOVE_PARAMETERS_IN_GETTER_DECLARATION =
const FixKind(
'QF_REMOVE_PARAMETERS_IN_GETTER_DECLARATION',
50,
"Remove parameters in getter declaration");
static const REMOVE_PARENTHESIS_IN_GETTER_INVOCATION =
const FixKind(
'QF_REMOVE_PARENTHESIS_IN_GETTER_INVOCATION',
50,
"Remove parentheses in getter invocation");
static const REMOVE_UNNECASSARY_CAST =
const FixKind('QF_REMOVE_UNNECASSARY_CAST', 50, "Remove unnecessary cast");
static const REMOVE_UNUSED_IMPORT =
const FixKind('QF_REMOVE_UNUSED_IMPORT', 50, "Remove unused import");
static const REPLACE_BOOLEAN_WITH_BOOL =
const FixKind(
'QF_REPLACE_BOOLEAN_WITH_BOOL',
50,
"Replace 'boolean' with 'bool'");
static const USE_CONST =
const FixKind('QF_USE_CONST', 50, "Change to constant");
static const USE_EFFECTIVE_INTEGER_DIVISION =
const FixKind(
'QF_USE_EFFECTIVE_INTEGER_DIVISION',
50,
"Use effective integer division ~/");
static const USE_EQ_EQ_NULL =
const FixKind('QF_USE_EQ_EQ_NULL', 50, "Use == null instead of 'is Null'");
static const USE_NOT_EQ_NULL =
const FixKind('QF_USE_NOT_EQ_NULL', 50, "Use != null instead of 'is! Null'");
final name;
final int relevance;
final String message;
const FixKind(this.name, this.relevance, this.message);
}
/**
* The computer for Dart fixes.
*/
class _FixProcessor {
final SearchEngine searchEngine;
final String file;
final CompilationUnit unit;
final AnalysisError error;
final List<Fix> fixes = <Fix>[];
final List<Edit> edits = <Edit>[];
_FixProcessor(this.searchEngine, this.file, this.unit, this.error);
List<Fix> compute() {
ErrorCode errorCode = error.errorCode;
if (errorCode == StaticWarningCode.UNDEFINED_CLASS_BOOLEAN) {
_addFix_boolInsteadOfBoolean();
}
return fixes;
}
void _addFix(FixKind kind, List args) {
FileEdit fileEdit = new FileEdit(file);
edits.forEach((edit) => fileEdit.add(edit));
// prepare Change
String message = JavaString.format(kind.message, args);
Change change = new Change(message);
change.add(fileEdit);
// add Fix
var fix = new Fix(kind, change);
fixes.add(fix);
}
void _addFix_boolInsteadOfBoolean() {
SourceRange range = rf.rangeError(error);
_addReplaceEdit(range, "bool");
_addFix(FixKind.REPLACE_BOOLEAN_WITH_BOOL, []);
}
/**
* Adds a new [Edit] to [edits].
*/
void _addReplaceEdit(SourceRange range, String text) {
Edit edit = new Edit.range(range, text);
edits.add(edit);
}
}
///**
// * An enumeration of possible quick assist kinds.
// */
//class AssistKind {
// static const QA_ADD_PART_DIRECTIVE =
// const AssistKind('QA_ADD_PART_DIRECTIVE', 30, "Add 'part' directive");
// static const QA_ADD_TYPE_ANNOTATION =
// const AssistKind('QA_ADD_TYPE_ANNOTATION', 30, "Add type annotation");
// static const QA_ASSIGN_TO_LOCAL_VARIABLE =
// const AssistKind(
// 'QA_ASSIGN_TO_LOCAL_VARIABLE',
// 30,
// "Assign value to new local variable");
// static const QA_CONVERT_INTO_BLOCK_BODY =
// const AssistKind('QA_CONVERT_INTO_BLOCK_BODY', 30, "Convert into block body");
// static const QA_CONVERT_INTO_EXPRESSION_BODY =
// const AssistKind(
// 'QA_CONVERT_INTO_EXPRESSION_BODY',
// 30,
// "Convert into expression body");
// static const QA_CONVERT_INTO_IS_NOT =
// const AssistKind('QA_CONVERT_INTO_IS_NOT', 30, "Convert into is!");
// static const QA_CONVERT_INTO_IS_NOT_EMPTY =
// const AssistKind(
// 'QA_CONVERT_INTO_IS_NOT_EMPTY',
// 30,
// "Convert into 'isNotEmpty'");
// static const QA_EXCHANGE_OPERANDS =
// const AssistKind('QA_EXCHANGE_OPERANDS', 30, "Exchange operands");
// static const QA_EXTRACT_CLASS =
// const AssistKind('QA_EXTRACT_CLASS', 30, "Extract class into file '%s'");
// static const QA_IMPORT_ADD_SHOW =
// const AssistKind('QA_IMPORT_ADD_SHOW', 30, "Add explicit 'show' combinator");
// static const QA_INVERT_IF_STATEMENT =
// const AssistKind('QA_INVERT_IF_STATEMENT', 30, "Invert 'if' statement");
// static const QA_JOIN_IF_WITH_INNER =
// const AssistKind(
// 'QA_JOIN_IF_WITH_INNER',
// 30,
// "Join 'if' statement with inner 'if' statement");
// static const QA_JOIN_IF_WITH_OUTER =
// const AssistKind(
// 'QA_JOIN_IF_WITH_OUTER',
// 30,
// "Join 'if' statement with outer 'if' statement");
// static const QA_JOIN_VARIABLE_DECLARATION =
// const AssistKind(
// 'QA_JOIN_VARIABLE_DECLARATION',
// 30,
// "Join variable declaration");
// static const QA_REMOVE_TYPE_ANNOTATION =
// const AssistKind('QA_REMOVE_TYPE_ANNOTATION', 29, "Remove type annotation");
// static const QA_REPLACE_CONDITIONAL_WITH_IF_ELSE =
// const AssistKind(
// 'QA_REPLACE_CONDITIONAL_WITH_IF_ELSE',
// 30,
// "Replace conditional with 'if-else'");
// static const QA_REPLACE_IF_ELSE_WITH_CONDITIONAL =
// const AssistKind(
// 'QA_REPLACE_IF_ELSE_WITH_CONDITIONAL',
// 30,
// "Replace 'if-else' with conditional ('c ? x : y')");
// static const QA_SPLIT_AND_CONDITION =
// const AssistKind('QA_SPLIT_AND_CONDITION', 30, "Split && condition");
// static const QA_SPLIT_VARIABLE_DECLARATION =
// const AssistKind(
// 'QA_SPLIT_VARIABLE_DECLARATION',
// 30,
// "Split variable declaration");
// static const QA_SURROUND_WITH_BLOCK =
// const AssistKind('QA_SURROUND_WITH_BLOCK', 30, "Surround with block");
// static const QA_SURROUND_WITH_DO_WHILE =
// const AssistKind('QA_SURROUND_WITH_DO_WHILE', 30, "Surround with 'do-while'");
// static const QA_SURROUND_WITH_FOR =
// const AssistKind('QA_SURROUND_WITH_FOR', 30, "Surround with 'for'");
// static const QA_SURROUND_WITH_FOR_IN =
// const AssistKind('QA_SURROUND_WITH_FOR_IN', 30, "Surround with 'for-in'");
// static const QA_SURROUND_WITH_IF =
// const AssistKind('QA_SURROUND_WITH_IF', 30, "Surround with 'if'");
// static const QA_SURROUND_WITH_TRY_CATCH =
// const AssistKind('QA_SURROUND_WITH_TRY_CATCH', 30, "Surround with 'try-catch'");
// static const QA_SURROUND_WITH_TRY_FINALLY =
// const AssistKind(
// 'QA_SURROUND_WITH_TRY_FINALLY',
// 30,
// "Surround with 'try-finally'");
// static const QA_SURROUND_WITH_WHILE =
// const AssistKind('QA_SURROUND_WITH_WHILE', 30, "Surround with 'while'");
//
// final name;
// final int relevance;
// final String message;
//
// const AssistKind(this.name, this.relevance, this.message);
//}