Add "convert to int literal" to dartfix
This updates the analysis server edit.dartfix request to find and convert double literals to int literals. Under the covers it uses the prefer_int_literal lint and gracefully degrades if the lint is not found (e.g. third_party/linter needs to be rolled). Change-Id: I0236af9c19667e543541fef18836bfeeba8c67d7 Reviewed-on: https://dart-review.googlesource.com/c/81302 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Dan Rubel <danrubel@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
0009ca7b1b
commit
4695b5f138
@@ -2,14 +2,13 @@
|
||||
// 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:analysis_server/plugin/edit/assist/assist_core.dart';
|
||||
import 'package:analysis_server/plugin/edit/assist/assist_dart.dart';
|
||||
import 'package:analysis_server/plugin/edit/fix/fix_core.dart';
|
||||
import 'package:analysis_server/protocol/protocol.dart';
|
||||
import 'package:analysis_server/protocol/protocol_generated.dart';
|
||||
import 'package:analysis_server/src/analysis_server.dart';
|
||||
import 'package:analysis_server/src/services/correction/assist.dart';
|
||||
import 'package:analysis_server/src/services/correction/assist_internal.dart';
|
||||
import 'package:analysis_server/src/edit/fix/prefer_int_literals_fix.dart';
|
||||
import 'package:analysis_server/src/edit/fix/prefer_mixin_fix.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix_internal.dart';
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
@@ -78,30 +77,36 @@ class EditDartFix {
|
||||
}
|
||||
|
||||
// Get the desired lints
|
||||
final LintRule preferMixin = Registry.ruleRegistry['prefer_mixin'];
|
||||
if (preferMixin == null) {
|
||||
return new Response.serverError(
|
||||
request, 'Missing prefer_mixin lint', null);
|
||||
}
|
||||
final lintRules = Registry.ruleRegistry;
|
||||
|
||||
final preferMixin = lintRules['prefer_mixin'];
|
||||
final preferMixinFix = new PreferMixinFix(this);
|
||||
preferMixin.reporter = preferMixinFix;
|
||||
|
||||
final preferIntLiterals = lintRules['prefer_int_literals'];
|
||||
final preferIntLiteralsFix = new PreferIntLiteralsFix(this);
|
||||
preferIntLiterals?.reporter = preferIntLiteralsFix;
|
||||
|
||||
// Setup
|
||||
final linters = <Linter>[
|
||||
preferMixin,
|
||||
preferIntLiterals,
|
||||
];
|
||||
final fixes = <LinterFix>[
|
||||
preferMixinFix,
|
||||
preferIntLiteralsFix,
|
||||
];
|
||||
final visitors = <AstVisitor>[];
|
||||
final registry = new NodeLintRegistry(false);
|
||||
for (Linter linter in linters) {
|
||||
final visitor = linter.getVisitor();
|
||||
if (visitor != null) {
|
||||
visitors.add(visitor);
|
||||
}
|
||||
if (linter is NodeLintRule) {
|
||||
(linter as NodeLintRule).registerNodeProcessors(registry);
|
||||
if (linter != null) {
|
||||
final visitor = linter.getVisitor();
|
||||
if (visitor != null) {
|
||||
visitors.add(visitor);
|
||||
}
|
||||
if (linter is NodeLintRule) {
|
||||
(linter as NodeLintRule).registerNodeProcessors(registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
final AstVisitor astVisitor = visitors.isNotEmpty
|
||||
@@ -149,23 +154,36 @@ class EditDartFix {
|
||||
}
|
||||
Source source = result.sourceFactory.forUri2(result.uri);
|
||||
for (Linter linter in linters) {
|
||||
linter.reporter.source = source;
|
||||
if (linter != null) {
|
||||
linter.reporter.source = source;
|
||||
}
|
||||
}
|
||||
if (astVisitor != null) {
|
||||
unit.accept(astVisitor);
|
||||
}
|
||||
unit.accept(linterVisitor);
|
||||
for (LinterFix fix in fixes) {
|
||||
await fix.applyLocalFixes(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
for (Linter linter in linters) {
|
||||
linter.reporter = null;
|
||||
if (linter != null) {
|
||||
linter.reporter.source = null;
|
||||
linter.reporter = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply distributed fixes
|
||||
if (preferIntLiterals == null) {
|
||||
// TODO(danrubel): Remove this once linter rolled into sdk/third_party.
|
||||
addRecommendation('*** Convert double literal not available'
|
||||
' because prefer_int_literal not found. May need to roll linter');
|
||||
}
|
||||
for (LinterFix fix in fixes) {
|
||||
await fix.applyFix();
|
||||
await fix.applyRemainingFixes();
|
||||
}
|
||||
|
||||
return new EditDartfixResult(descriptionOfFixes, otherRecommendations,
|
||||
@@ -279,7 +297,11 @@ abstract class LinterFix implements ErrorReporter {
|
||||
|
||||
LinterFix(this.dartFix);
|
||||
|
||||
Future<void> applyFix();
|
||||
/// Apply fixes for the current compilation unit.
|
||||
Future<void> applyLocalFixes(AnalysisResult result);
|
||||
|
||||
/// Apply any fixes remaining after analysis is complete.
|
||||
Future<void> applyRemainingFixes();
|
||||
|
||||
@override
|
||||
void reportError(AnalysisError error) {
|
||||
@@ -328,57 +350,3 @@ abstract class LinterFix implements ErrorReporter {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
class PreferMixinFix extends LinterFix {
|
||||
final classesToConvert = new Set<Element>();
|
||||
|
||||
PreferMixinFix(EditDartFix dartFix) : super(dartFix);
|
||||
|
||||
@override
|
||||
Future<void> applyFix() async {
|
||||
for (Element elem in classesToConvert) {
|
||||
await convertClassToMixin(elem);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> convertClassToMixin(Element elem) async {
|
||||
AnalysisResult result =
|
||||
await dartFix.server.getAnalysisResult(elem.source?.fullName);
|
||||
|
||||
for (CompilationUnitMember declaration in result.unit.declarations) {
|
||||
if (declaration is ClassOrMixinDeclaration &&
|
||||
declaration.name.name == elem.name) {
|
||||
AssistProcessor processor = new AssistProcessor(
|
||||
new EditDartFixAssistContext(
|
||||
dartFix, elem.source, result.unit, declaration.name));
|
||||
List<Assist> assists = await processor
|
||||
.computeAssist(DartAssistKind.CONVERT_CLASS_TO_MIXIN);
|
||||
final location = dartFix.locationDescription(result, elem.nameOffset);
|
||||
if (assists.isNotEmpty) {
|
||||
for (Assist assist in assists) {
|
||||
dartFix.addFix(
|
||||
'Convert ${elem.displayName} to a mixin in $location',
|
||||
assist.change);
|
||||
}
|
||||
} else {
|
||||
// TODO(danrubel): If assists is empty, then determine why
|
||||
// assist could not be performed and report that in the description.
|
||||
dartFix.addRecommendation(
|
||||
'Could not convert ${elem.displayName} to a mixin'
|
||||
' because the class contains a constructor in $location');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void reportErrorForNode(ErrorCode errorCode, AstNode node,
|
||||
[List<Object> arguments]) {
|
||||
TypeName type = node;
|
||||
Element element = type.name.staticElement;
|
||||
String filePath = element.source?.fullName;
|
||||
if (filePath != null && dartFix.isIncluded(filePath)) {
|
||||
classesToConvert.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2018, 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:analysis_server/plugin/edit/assist/assist_core.dart';
|
||||
import 'package:analysis_server/src/edit/edit_dartfix.dart';
|
||||
import 'package:analysis_server/src/services/correction/assist.dart';
|
||||
import 'package:analysis_server/src/services/correction/assist_internal.dart';
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/src/dart/analysis/driver.dart';
|
||||
|
||||
class PreferIntLiteralsFix extends LinterFix {
|
||||
final literalsToConvert = <DoubleLiteral>[];
|
||||
|
||||
PreferIntLiteralsFix(EditDartFix dartFix) : super(dartFix);
|
||||
|
||||
@override
|
||||
Future<void> applyLocalFixes(AnalysisResult result) async {
|
||||
while (literalsToConvert.isNotEmpty) {
|
||||
DoubleLiteral literal = literalsToConvert.removeLast();
|
||||
AssistProcessor processor = new AssistProcessor(
|
||||
new EditDartFixAssistContext(dartFix, source, result.unit, literal));
|
||||
List<Assist> assists =
|
||||
await processor.computeAssist(DartAssistKind.CONVERT_TO_INT_LITERAL);
|
||||
final location = dartFix.locationDescription(result, literal.offset);
|
||||
if (assists.isNotEmpty) {
|
||||
for (Assist assist in assists) {
|
||||
dartFix.addFix(
|
||||
'Replace a double literal with an int literal in $location',
|
||||
assist.change);
|
||||
}
|
||||
} else {
|
||||
// TODO(danrubel): If assists is empty, then determine why
|
||||
// assist could not be performed and report that in the description.
|
||||
dartFix.addRecommendation('Could not replace'
|
||||
' a double literal with an int literal in $location');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> applyRemainingFixes() {
|
||||
// All fixes applied in [applyLocalFixes]
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
void reportErrorForNode(ErrorCode errorCode, AstNode node,
|
||||
[List<Object> arguments]) {
|
||||
String filePath = source.fullName;
|
||||
if (filePath != null && dartFix.isIncluded(filePath)) {
|
||||
literalsToConvert.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2018, 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:analysis_server/plugin/edit/assist/assist_core.dart';
|
||||
import 'package:analysis_server/src/edit/edit_dartfix.dart';
|
||||
import 'package:analysis_server/src/services/correction/assist.dart';
|
||||
import 'package:analysis_server/src/services/correction/assist_internal.dart';
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/src/dart/analysis/driver.dart';
|
||||
|
||||
class PreferMixinFix extends LinterFix {
|
||||
final classesToConvert = new Set<Element>();
|
||||
|
||||
PreferMixinFix(EditDartFix dartFix) : super(dartFix);
|
||||
|
||||
@override
|
||||
Future<void> applyLocalFixes(AnalysisResult result) {
|
||||
// All fixes applied in [applyRemainingFixes]
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> applyRemainingFixes() async {
|
||||
for (Element elem in classesToConvert) {
|
||||
await convertClassToMixin(elem);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> convertClassToMixin(Element elem) async {
|
||||
AnalysisResult result =
|
||||
await dartFix.server.getAnalysisResult(elem.source?.fullName);
|
||||
|
||||
for (CompilationUnitMember declaration in result.unit.declarations) {
|
||||
if (declaration is ClassOrMixinDeclaration &&
|
||||
declaration.name.name == elem.name) {
|
||||
AssistProcessor processor = new AssistProcessor(
|
||||
new EditDartFixAssistContext(
|
||||
dartFix, elem.source, result.unit, declaration.name));
|
||||
List<Assist> assists = await processor
|
||||
.computeAssist(DartAssistKind.CONVERT_CLASS_TO_MIXIN);
|
||||
final location = dartFix.locationDescription(result, elem.nameOffset);
|
||||
if (assists.isNotEmpty) {
|
||||
for (Assist assist in assists) {
|
||||
dartFix.addFix(
|
||||
'Convert ${elem.displayName} to a mixin in $location',
|
||||
assist.change);
|
||||
}
|
||||
} else {
|
||||
// TODO(danrubel): If assists is empty, then determine why
|
||||
// assist could not be performed and report that in the description.
|
||||
dartFix.addRecommendation(
|
||||
'Could not convert ${elem.displayName} to a mixin'
|
||||
' because the class contains a constructor in $location');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void reportErrorForNode(ErrorCode errorCode, AstNode node,
|
||||
[List<Object> arguments]) {
|
||||
TypeName type = node;
|
||||
Element element = type.name.staticElement;
|
||||
String filePath = element.source?.fullName;
|
||||
if (filePath != null && dartFix.isIncluded(filePath)) {
|
||||
classesToConvert.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user