mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 09:01:42 +00:00
Add a quick fix for avoid_escaping_inner_quotes
Fixes #47158 Change-Id: I447cf68d2da682bab93fb43b0ad61546a2217755 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212825 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
7fb8c7afea
commit
a4d8f343ae
|
@ -6,14 +6,97 @@ import 'package:analysis_server/src/services/correction/assist.dart';
|
|||
import 'package:analysis_server/src/services/correction/dart/abstract_producer.dart';
|
||||
import 'package:analysis_server/src/services/correction/fix.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/token.dart';
|
||||
import 'package:analyzer/source/source_range.dart';
|
||||
import 'package:analyzer_plugin/utilities/assist/assist.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
|
||||
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
|
||||
|
||||
abstract class ConvertQuotes extends CorrectionProducer {
|
||||
ConvertQuotes();
|
||||
class ConvertQuotes extends _ConvertQuotes {
|
||||
@override
|
||||
late bool _fromDouble;
|
||||
|
||||
@override
|
||||
bool get canBeAppliedInBulk => true;
|
||||
|
||||
@override
|
||||
bool get canBeAppliedToFile => true;
|
||||
|
||||
@override
|
||||
FixKind get fixKind => DartFixKind.CONVERT_QUOTES;
|
||||
|
||||
@override
|
||||
FixKind get multiFixKind => DartFixKind.CONVERT_QUOTES_MULTI;
|
||||
|
||||
@override
|
||||
Future<void> compute(ChangeBuilder builder) async {
|
||||
final node = this.node;
|
||||
if (node is SimpleStringLiteral) {
|
||||
_fromDouble = !node.isSingleQuoted;
|
||||
await _simpleStringLiteral(builder, node);
|
||||
await removeBackslash(builder, node.literal);
|
||||
} else if (node is StringInterpolation) {
|
||||
_fromDouble = !node.isSingleQuoted;
|
||||
await _stringInterpolation(builder, node);
|
||||
|
||||
for (var child in node.childEntities.whereType<InterpolationString>()) {
|
||||
await removeBackslash(builder, child.contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeBackslash(ChangeBuilder builder, Token token) async {
|
||||
var quote = _fromDouble ? '"' : "'";
|
||||
var text = utils.getText(token.offset, token.length);
|
||||
for (var i = 0; i + 1 < text.length; i++) {
|
||||
if (text[i] == r'\' && text[i + 1] == quote) {
|
||||
await builder.addDartFileEdit(file, (builder) {
|
||||
builder.addDeletion(SourceRange(token.offset + i, 1));
|
||||
});
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
|
||||
static ConvertQuotes newInstance() => ConvertQuotes();
|
||||
}
|
||||
|
||||
class ConvertToDoubleQuotes extends _ConvertQuotes {
|
||||
@override
|
||||
AssistKind get assistKind => DartAssistKind.CONVERT_TO_DOUBLE_QUOTED_STRING;
|
||||
|
||||
@override
|
||||
bool get _fromDouble => false;
|
||||
|
||||
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
|
||||
static ConvertToDoubleQuotes newInstance() => ConvertToDoubleQuotes();
|
||||
}
|
||||
|
||||
class ConvertToSingleQuotes extends _ConvertQuotes {
|
||||
@override
|
||||
AssistKind get assistKind => DartAssistKind.CONVERT_TO_SINGLE_QUOTED_STRING;
|
||||
|
||||
@override
|
||||
bool get canBeAppliedInBulk => true;
|
||||
|
||||
@override
|
||||
bool get canBeAppliedToFile => true;
|
||||
|
||||
@override
|
||||
FixKind get fixKind => DartFixKind.CONVERT_TO_SINGLE_QUOTED_STRING;
|
||||
|
||||
@override
|
||||
FixKind get multiFixKind => DartFixKind.CONVERT_TO_SINGLE_QUOTED_STRING_MULTI;
|
||||
|
||||
@override
|
||||
bool get _fromDouble => true;
|
||||
|
||||
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
|
||||
static ConvertToSingleQuotes newInstance() => ConvertToSingleQuotes();
|
||||
}
|
||||
|
||||
abstract class _ConvertQuotes extends CorrectionProducer {
|
||||
/// Return `true` if this producer is converting from double quotes to single
|
||||
/// quotes, or `false` if it's converting from single quotes to double quotes.
|
||||
bool get _fromDouble;
|
||||
|
@ -31,9 +114,7 @@ abstract class ConvertQuotes extends CorrectionProducer {
|
|||
}
|
||||
|
||||
Future<void> _simpleStringLiteral(
|
||||
ChangeBuilder builder,
|
||||
SimpleStringLiteral node,
|
||||
) async {
|
||||
ChangeBuilder builder, SimpleStringLiteral node) async {
|
||||
if (_fromDouble ? !node.isSingleQuoted : node.isSingleQuoted) {
|
||||
var newQuote = node.isMultiline
|
||||
? (_fromDouble ? "'''" : '"""')
|
||||
|
@ -56,9 +137,7 @@ abstract class ConvertQuotes extends CorrectionProducer {
|
|||
}
|
||||
|
||||
Future<void> _stringInterpolation(
|
||||
ChangeBuilder builder,
|
||||
StringInterpolation node,
|
||||
) async {
|
||||
ChangeBuilder builder, StringInterpolation node) async {
|
||||
if (_fromDouble ? !node.isSingleQuoted : node.isSingleQuoted) {
|
||||
var newQuote = node.isMultiline
|
||||
? (_fromDouble ? "'''" : '"""')
|
||||
|
@ -87,41 +166,3 @@ abstract class ConvertQuotes extends CorrectionProducer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ConvertToDoubleQuotes extends ConvertQuotes {
|
||||
ConvertToDoubleQuotes();
|
||||
|
||||
@override
|
||||
AssistKind get assistKind => DartAssistKind.CONVERT_TO_DOUBLE_QUOTED_STRING;
|
||||
|
||||
@override
|
||||
bool get _fromDouble => false;
|
||||
|
||||
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
|
||||
static ConvertToDoubleQuotes newInstance() => ConvertToDoubleQuotes();
|
||||
}
|
||||
|
||||
class ConvertToSingleQuotes extends ConvertQuotes {
|
||||
ConvertToSingleQuotes();
|
||||
|
||||
@override
|
||||
AssistKind get assistKind => DartAssistKind.CONVERT_TO_SINGLE_QUOTED_STRING;
|
||||
|
||||
@override
|
||||
bool get canBeAppliedInBulk => true;
|
||||
|
||||
@override
|
||||
bool get canBeAppliedToFile => true;
|
||||
|
||||
@override
|
||||
FixKind get fixKind => DartFixKind.CONVERT_TO_SINGLE_QUOTED_STRING;
|
||||
|
||||
@override
|
||||
FixKind get multiFixKind => DartFixKind.CONVERT_TO_SINGLE_QUOTED_STRING_MULTI;
|
||||
|
||||
@override
|
||||
bool get _fromDouble => true;
|
||||
|
||||
/// Return an instance of this class. Used as a tear-off in `FixProcessor`.
|
||||
static ConvertToSingleQuotes newInstance() => ConvertToSingleQuotes();
|
||||
}
|
||||
|
|
|
@ -309,6 +309,16 @@ class DartFixKind {
|
|||
DartFixKindPriority.IN_FILE,
|
||||
'Convert to expression bodies everywhere in file',
|
||||
);
|
||||
static const CONVERT_QUOTES = FixKind(
|
||||
'dart.fix.convert.quotes',
|
||||
DartFixKindPriority.DEFAULT,
|
||||
'Convert the quotes and remove escapes',
|
||||
);
|
||||
static const CONVERT_QUOTES_MULTI = FixKind(
|
||||
'dart.fix.convert.quotes.multi',
|
||||
DartFixKindPriority.IN_FILE,
|
||||
'Convert the quotes and remove escapes everywhere in file',
|
||||
);
|
||||
static const CONVERT_TO_CONTAINS = FixKind(
|
||||
'dart.fix.convert.toContains',
|
||||
DartFixKindPriority.DEFAULT,
|
||||
|
|
|
@ -345,6 +345,9 @@ class FixProcessor extends BaseProcessor {
|
|||
LintNames.avoid_empty_else: [
|
||||
RemoveEmptyElse.newInstance,
|
||||
],
|
||||
LintNames.avoid_escaping_inner_quotes: [
|
||||
ConvertQuotes.newInstance,
|
||||
],
|
||||
LintNames.avoid_function_literals_in_foreach_calls: [
|
||||
ConvertForEachToForLoop.newInstance,
|
||||
],
|
||||
|
|
|
@ -13,6 +13,8 @@ class LintNames {
|
|||
static const String avoid_annotating_with_dynamic =
|
||||
'avoid_annotating_with_dynamic';
|
||||
static const String avoid_empty_else = 'avoid_empty_else';
|
||||
static const String avoid_escaping_inner_quotes =
|
||||
'avoid_escaping_inner_quotes';
|
||||
static const String avoid_function_literals_in_foreach_calls =
|
||||
'avoid_function_literals_in_foreach_calls';
|
||||
static const String avoid_init_to_null = 'avoid_init_to_null';
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) 2021, 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/src/services/correction/fix.dart';
|
||||
import 'package:analysis_server/src/services/linter/lint_names.dart';
|
||||
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
|
||||
import 'package:test/expect.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'fix_processor.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(ConvertQuotesBulkTest);
|
||||
defineReflectiveTests(ConvertQuotesInFileTest);
|
||||
defineReflectiveTests(ConvertQuotesTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class ConvertQuotesBulkTest extends BulkFixProcessorTest {
|
||||
@override
|
||||
String get lintCode => LintNames.avoid_escaping_inner_quotes;
|
||||
|
||||
Future<void> test_string_in_interpolation_string() async {
|
||||
await resolveTestCode(r'''
|
||||
void f() {
|
||||
print('a\'${'b\'c'}\'d');
|
||||
}
|
||||
''');
|
||||
await assertHasFix(r'''
|
||||
void f() {
|
||||
print("a'${"b'c"}'d");
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class ConvertQuotesInFileTest extends FixInFileProcessorTest {
|
||||
Future<void> test_File() async {
|
||||
createAnalysisOptionsFile(lints: [LintNames.avoid_escaping_inner_quotes]);
|
||||
await resolveTestCode(r'''
|
||||
void f() {
|
||||
print("a\"b\"c");
|
||||
print('d\'e\'f');
|
||||
}
|
||||
''');
|
||||
var fixes = await getFixesForFirstError();
|
||||
expect(fixes, hasLength(1));
|
||||
assertProduces(fixes.first, r'''
|
||||
void f() {
|
||||
print('a"b"c');
|
||||
print("d'e'f");
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class ConvertQuotesTest extends FixProcessorLintTest {
|
||||
@override
|
||||
FixKind get kind => DartFixKind.CONVERT_QUOTES;
|
||||
|
||||
@override
|
||||
String get lintCode => LintNames.avoid_escaping_inner_quotes;
|
||||
|
||||
Future<void> test_backslash() async {
|
||||
await resolveTestCode(r'''
|
||||
void f() {
|
||||
print('\\\'b\'\$');
|
||||
}
|
||||
''');
|
||||
await assertHasFix(r'''
|
||||
void f() {
|
||||
print("\\'b'\$");
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_double_quotes() async {
|
||||
await resolveTestCode(r'''
|
||||
void f() {
|
||||
print("a\"\"c");
|
||||
}
|
||||
''');
|
||||
await assertHasFix(r'''
|
||||
void f() {
|
||||
print('a""c');
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_interpolation() async {
|
||||
await resolveTestCode(r'''
|
||||
void f(String d) {
|
||||
print('a\'b\'c $d');
|
||||
}
|
||||
''');
|
||||
await assertHasFix(r'''
|
||||
void f(String d) {
|
||||
print("a'b'c $d");
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_interpolation_string() async {
|
||||
await resolveTestCode(r'''
|
||||
void f(String d) {
|
||||
print('a\'b\'c ${d.length}');
|
||||
}
|
||||
''');
|
||||
await assertHasFix(r'''
|
||||
void f(String d) {
|
||||
print("a'b'c ${d.length}");
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_single_quotes() async {
|
||||
await resolveTestCode(r'''
|
||||
void f() {
|
||||
print('a\'b\'c');
|
||||
}
|
||||
''');
|
||||
await assertHasFix(r'''
|
||||
void f() {
|
||||
print("a'b'c");
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_string_in_interpolation_string() async {
|
||||
await resolveTestCode(r'''
|
||||
void f() {
|
||||
print('a${'b\'c'}d');
|
||||
}
|
||||
''');
|
||||
await assertHasFix(r'''
|
||||
void f() {
|
||||
print('a${"b'c"}d');
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
|
@ -51,6 +51,7 @@ import 'convert_flutter_children_test.dart' as convert_flutter_children;
|
|||
import 'convert_for_each_to_for_loop_test.dart' as convert_for_each_to_for_loop;
|
||||
import 'convert_into_expression_body_test.dart' as convert_into_expression_body;
|
||||
import 'convert_into_is_not_test.dart' as convert_into_is_not;
|
||||
import 'convert_quotes_test.dart' as convert_quotes;
|
||||
import 'convert_to_contains_test.dart' as convert_to_contains;
|
||||
import 'convert_to_for_element_test.dart' as convert_to_for_element;
|
||||
import 'convert_to_generic_function_syntax_test.dart'
|
||||
|
@ -245,6 +246,7 @@ void main() {
|
|||
convert_for_each_to_for_loop.main();
|
||||
convert_into_expression_body.main();
|
||||
convert_into_is_not.main();
|
||||
convert_quotes.main();
|
||||
convert_to_contains.main();
|
||||
convert_to_for_element.main();
|
||||
convert_to_generic_function_syntax.main();
|
||||
|
|
Loading…
Reference in a new issue