Merge pull request #2024 from dart-lang/avoid-returning-null-for-future-highlight

Improve the highlight range for avoid_returning_null_for_future
diff --git a/example/all.yaml b/example/all.yaml
index 97cb311..423bc3c 100644
--- a/example/all.yaml
+++ b/example/all.yaml
@@ -67,6 +67,7 @@
     - invariant_booleans
     - iterable_contains_unrelated_type
     - join_return_with_assignment
+    - leading_newlines_in_multiline_strings
     - library_names
     - library_prefixes
     - lines_longer_than_80_chars
diff --git a/lib/src/rules.dart b/lib/src/rules.dart
index d277fd0..286ec02 100644
--- a/lib/src/rules.dart
+++ b/lib/src/rules.dart
@@ -68,6 +68,7 @@
 import 'rules/invariant_booleans.dart';
 import 'rules/iterable_contains_unrelated_type.dart';
 import 'rules/join_return_with_assignment.dart';
+import 'rules/leading_newlines_in_multiline_strings.dart';
 import 'rules/library_names.dart';
 import 'rules/library_prefixes.dart';
 import 'rules/lines_longer_than_80_chars.dart';
@@ -238,6 +239,7 @@
     ..register(InvariantBooleans())
     ..register(IterableContainsUnrelatedType())
     ..register(JoinReturnWithAssignment())
+    ..register(LeadingNewlinesInMultilineStrings())
     ..register(LibraryNames())
     ..register(LibraryPrefixes())
     ..register(LinesLongerThan80Chars())
diff --git a/lib/src/rules/leading_newlines_in_multiline_strings.dart b/lib/src/rules/leading_newlines_in_multiline_strings.dart
new file mode 100644
index 0000000..70765f9
--- /dev/null
+++ b/lib/src/rules/leading_newlines_in_multiline_strings.dart
@@ -0,0 +1,90 @@
+// 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 '../analyzer.dart';
+
+const _desc = r'Start multiline strings with a newline.';
+
+const _details = r"""
+Multiline strings are easier to read when they start with a newline (a newline
+starting a multiline string is ignored).
+
+**BAD:**
+```
+var s1 = '''{
+  "a": 1,
+  "b": 2
+}''';
+```
+
+**GOOD:**
+```
+var s1 = '''
+{
+  "a": 1,
+  "b": 2
+}''';
+
+var s2 = '''This onliner multiline string is ok. It usually allows to escape both ' and " in the string.''';
+```
+
+""";
+
+class LeadingNewlinesInMultilineStrings extends LintRule
+    implements NodeLintRule {
+  LeadingNewlinesInMultilineStrings()
+      : super(
+            name: 'leading_newlines_in_multiline_strings',
+            description: _desc,
+            details: _details,
+            group: Group.style);
+
+  @override
+  void registerNodeProcessors(NodeLintRegistry registry,
+      [LinterContext context]) {
+    final visitor = _Visitor(this);
+    registry.addCompilationUnit(this, visitor);
+    registry.addSimpleStringLiteral(this, visitor);
+    registry.addStringInterpolation(this, visitor);
+  }
+}
+
+class _Visitor extends SimpleAstVisitor<void> {
+  _Visitor(this.rule);
+
+  final LintRule rule;
+
+  LineInfo lineInfo;
+
+  @override
+  void visitCompilationUnit(CompilationUnit node) {
+    lineInfo = node.lineInfo;
+  }
+
+  @override
+  void visitSimpleStringLiteral(SimpleStringLiteral node) {
+    _visitSingleStringLiteral(node, node.literal.lexeme);
+  }
+
+  @override
+  void visitStringInterpolation(StringInterpolation node) {
+    _visitSingleStringLiteral(
+        node, (node.elements.first as InterpolationString).contents.lexeme);
+  }
+
+  void _visitSingleStringLiteral(SingleStringLiteral node, String lexeme) {
+    if (node.isMultiline &&
+        lineInfo.getLocation(node.offset).lineNumber !=
+            lineInfo.getLocation(node.end).lineNumber) {
+      bool startWithNewLine(int index) =>
+          lexeme.startsWith('\n', index) || lexeme.startsWith('\r', index);
+      if (!startWithNewLine(node.isRaw ? 4 : 3)) {
+        rule.reportLint(node);
+      }
+    }
+  }
+}
diff --git a/test/rules/leading_newlines_in_multiline_strings.dart b/test/rules/leading_newlines_in_multiline_strings.dart
new file mode 100644
index 0000000..b9ca997
--- /dev/null
+++ b/test/rules/leading_newlines_in_multiline_strings.dart
@@ -0,0 +1,26 @@
+// 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 leading_newlines_in_multiline_strings`
+
+f(o) {
+  f(''''''); // OK
+  f('''this is a multiline string'''); // OK
+  f('''$o'''); // OK
+  f("""uses double quotes"""); // OK
+  // OK
+  f('''
+this is a multiline string''');
+  // LINT [+1]
+  f('''this
+ is a multiline string''');
+
+  f('''this is a multiline string $f'''); // OK
+  // OK
+  f('''
+this is a multiline string $f''');
+  // LINT [+1]
+  f('''this
+ is a multiline string$f''');
+}