add avoid_escaping_inner_quotes (#2000)
* add change_quotes
* rename to avoid_escaping_inner_quotes
diff --git a/example/all.yaml b/example/all.yaml
index 74665ec..70c84a4 100644
--- a/example/all.yaml
+++ b/example/all.yaml
@@ -17,6 +17,7 @@
- avoid_double_and_int_checks
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
+ - avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
- avoid_implementing_value_types
diff --git a/lib/src/rules.dart b/lib/src/rules.dart
index 03d40b4..c37d3e9 100644
--- a/lib/src/rules.dart
+++ b/lib/src/rules.dart
@@ -18,6 +18,7 @@
import 'rules/avoid_double_and_int_checks.dart';
import 'rules/avoid_empty_else.dart';
import 'rules/avoid_equals_and_hash_code_on_mutable_classes.dart';
+import 'rules/avoid_escaping_inner_quotes.dart';
import 'rules/avoid_field_initializers_in_const_classes.dart';
import 'rules/avoid_function_literals_in_foreach_calls.dart';
import 'rules/avoid_implementing_value_types.dart';
@@ -185,6 +186,7 @@
..register(AvoidClassesWithOnlyStaticMembers())
..register(AvoidDoubleAndIntChecks())
..register(AvoidEmptyElse())
+ ..register(AvoidEscapingInnerQuotes())
..register(AvoidFieldInitializersInConstClasses())
..register(AvoidFunctionLiteralInForeachMethod())
..register(AvoidImplementingValueTypes())
diff --git a/lib/src/rules/avoid_escaping_inner_quotes.dart b/lib/src/rules/avoid_escaping_inner_quotes.dart
new file mode 100644
index 0000000..30b4ac6
--- /dev/null
+++ b/lib/src/rules/avoid_escaping_inner_quotes.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2020, 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/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:meta/meta.dart';
+
+import '../analyzer.dart';
+
+const _desc = r'Avoid escaping inner quotes by converting surrounding quotes.';
+
+const _details = r'''
+
+Avoid escaping inner quotes by converting surrounding quotes.
+
+**BAD:**
+```
+var s = 'It\'s not fun';
+```
+
+**GOOD:**
+```
+var s = "It's not fun";
+```
+
+''';
+
+class AvoidEscapingInnerQuotes extends LintRule implements NodeLintRule {
+ AvoidEscapingInnerQuotes()
+ : super(
+ name: 'avoid_escaping_inner_quotes',
+ description: _desc,
+ details: _details,
+ group: Group.style);
+
+ @override
+ void registerNodeProcessors(NodeLintRegistry registry,
+ [LinterContext context]) {
+ final visitor = _Visitor(this);
+ registry.addSimpleStringLiteral(this, visitor);
+ registry.addStringInterpolation(this, visitor);
+ }
+}
+
+class _Visitor extends SimpleAstVisitor<void> {
+ final LintRule rule;
+
+ _Visitor(this.rule);
+
+ @override
+ void visitSimpleStringLiteral(SimpleStringLiteral node) {
+ if (node.isRaw || node.isMultiline) return;
+
+ if (isChangeable(node.value, isSingleQuoted: node.isSingleQuoted)) {
+ rule.reportLint(node);
+ }
+ }
+
+ @override
+ void visitStringInterpolation(StringInterpolation node) {
+ if (node.isRaw || node.isMultiline) return;
+
+ final text = node.elements
+ .whereType<InterpolationString>()
+ .map((e) => e.value)
+ .reduce((a, b) => '$a$b');
+ if (isChangeable(text, isSingleQuoted: node.isSingleQuoted)) {
+ rule.reportLint(node);
+ }
+ }
+
+ bool isChangeable(String text, {@required bool isSingleQuoted}) =>
+ text.contains(isSingleQuoted ? "'" : '"') &&
+ !text.contains(isSingleQuoted ? '"' : "'");
+}
diff --git a/test/rules/avoid_escaping_inner_quotes.dart b/test/rules/avoid_escaping_inner_quotes.dart
new file mode 100644
index 0000000..f98e0e3
--- /dev/null
+++ b/test/rules/avoid_escaping_inner_quotes.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2020, 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.
+
+// test w/ `pub run test -N avoid_escaping_inner_quotes`
+
+f(o) {
+ f(""); // OK
+ f(''); // OK
+ f("\""); // LINT
+ f('\''); // LINT
+ f("\"'"); // OK
+ f('\'"'); // OK
+ f("\"$f"); // LINT
+ f('\'$f'); // LINT
+ f("\"'$f"); // OK
+ f('\'"$f'); // OK
+}
\ No newline at end of file