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>
diff --git a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
index 8821d75..3a8e344 100644
--- a/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_dartfix.dart
@@ -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 @@
}
// 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 @@
}
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 @@
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 @@
// 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);
- }
- }
-}
diff --git a/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart b/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart
new file mode 100644
index 0000000..81758f9
--- /dev/null
+++ b/pkg/analysis_server/lib/src/edit/fix/prefer_int_literals_fix.dart
@@ -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);
+ }
+ }
+}
diff --git a/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
new file mode 100644
index 0000000..5e2aad1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/edit/fix/prefer_mixin_fix.dart
@@ -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);
+ }
+ }
+}