Add support code to make fixes easier in plugins

R=scheglov@google.com

Review-Url: https://codereview.chromium.org/2962903002 .
This commit is contained in:
Brian Wilkerson 2017-06-28 10:52:05 -07:00
parent 563720617c
commit 6248e7cab2
6 changed files with 111 additions and 38 deletions

View file

@ -9,6 +9,7 @@ import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart'
show SourceChange;
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
/**
* A description of a single proposed fix for some problem.
@ -27,7 +28,7 @@ class Fix {
*/
static final Comparator<Fix> SORT_BY_RELEVANCE =
(Fix firstFix, Fix secondFix) =>
firstFix.kind.relevance - secondFix.kind.relevance;
firstFix.kind.priority - secondFix.kind.priority;
/**
* A description of the fix being proposed.
@ -85,40 +86,3 @@ abstract class FixContributor {
*/
Future<List<Fix>> computeFixes(FixContext context);
}
/**
* A description of a class of fixes. Instances are intended to hold the
* information that is common across a number of fixes and to be shared by those
* fixes. For example, if an unnecessary cast is found then one of the suggested
* fixes will be to remove the cast. If there are multiple unnecessary casts in
* a single file, then there will be multiple fixes, one per occurrence, but
* they will all share the same kind.
*
* Clients may not extend, implement or mix-in this class.
*/
class FixKind {
/**
* The name of this kind of fix, used for debugging.
*/
final String name;
/**
* The relevance of this kind of fix for the kind of error being addressed.
*/
final int relevance;
/**
* A human-readable description of the changes that will be applied by this
* kind of fix.
*/
final String message;
/**
* Initialize a newly created kind of fix to have the given [name],
* [relevance] and [message].
*/
const FixKind(this.name, this.relevance, this.message);
@override
String toString() => name;
}

View file

@ -9,6 +9,7 @@ import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/parser.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
/**
* Return true if this [errorCode] is likely to have a fix associated with it.

View file

@ -44,6 +44,7 @@ import 'package:analyzer_plugin/protocol/protocol_common.dart'
hide AnalysisError, Element, ElementKind;
import 'package:analyzer_plugin/src/utilities/string_utilities.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart' hide FixContributor;
import 'package:analyzer_plugin/utilities/range_factory.dart';
import 'package:path/path.dart';

View file

@ -21,6 +21,7 @@ import 'package:analyzer/src/generated/parser.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart'
hide AnalysisError;
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';

View file

@ -0,0 +1,67 @@
// Copyright (c) 2017, 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 'package:analyzer/error/error.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart'
hide AnalysisError;
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
/**
* A partial implementation of a [FixContributor] that iterates over the list of
* errors and provides a utility method to make it easier to add fixes.
*
* Clients may not extend or implement this class, but are allowed to use it as
* a mix-in when creating a subclass of [FixContributor].
*/
abstract class FixContributorMixin implements FixContributor {
/**
* The request that specifies the fixes that are to be built.
*/
DartFixesRequest request;
/**
* The collector to which fixes should be added.
*/
FixCollector collector;
/**
* Add a fix for the given [error]. Use the [kind] of the fix to get the
* message and priority, and use the change [builder] to get the edits that
* comprise the fix. If the message has parameters, then use the list of
* [args] to populate the message.
*/
void addFix(AnalysisError error, FixKind kind, ChangeBuilder builder,
{List<Object> args: null}) {
SourceChange change = builder.sourceChange;
if (change.edits.isEmpty) {
return;
}
change.message = formatList(kind.message, args);
collector.addFix(error,
new PrioritizedSourceChange(kind.priority, builder.sourceChange));
}
@override
void computeFixes(DartFixesRequest request, FixCollector collector) {
this.request = request;
this.collector = collector;
try {
for (AnalysisError error in request.errorsToFix) {
computeFixesForError(error);
}
} finally {
this.request = null;
this.collector = null;
}
}
/**
* Compute the fixes that are appropriate for the given [error] and add them
* to the fix [collector].
*/
void computeFixesForError(AnalysisError error);
}

View file

@ -108,3 +108,42 @@ class FixGenerator {
return new GeneratorResult(result, notifications);
}
}
/**
* A description of a class of fixes. Instances are intended to hold the
* information that is common across a number of fixes and to be shared by those
* fixes. For example, if an unnecessary cast is found then one of the suggested
* fixes will be to remove the cast. If there are multiple unnecessary casts in
* a single file, then there will be multiple fixes, one per occurrence, but
* they will all share the same kind.
*
* Clients may not extend, implement or mix-in this class.
*/
class FixKind {
/**
* The name of this kind of fix, used for debugging.
*/
final String name;
/**
* The priority of this kind of fix for the kind of error being addressed.
*/
final int priority;
/**
* A human-readable description of the changes that will be applied by this
* kind of fix. The message can contain parameters, where each parameter is
* represented by a zero-based index inside curly braces. For example, the
* message `"Create a component named '{0}' in '{1}'"` contains two parameters.
*/
final String message;
/**
* Initialize a newly created kind of fix to have the given [name],
* [priority] and [message].
*/
const FixKind(this.name, this.priority, this.message);
@override
String toString() => name;
}