diff --git a/pkg/linter/test/rules/always_specify_types_test.dart b/pkg/linter/test/rules/always_specify_types_test.dart index f5d2cef2d49b..d6570233da28 100644 --- a/pkg/linter/test/rules/always_specify_types_test.dart +++ b/pkg/linter/test/rules/always_specify_types_test.dart @@ -270,7 +270,7 @@ class C { await assertNoDiagnostics(r''' class S { S({int? p1, required int p2}); -} +} class C extends S { C({super.p1, required super.p2}); } diff --git a/pkg/linter/test/rules/annotate_redeclares_test.dart b/pkg/linter/test/rules/annotate_redeclares_test.dart index 6c2628320828..0d3dbcd6a84d 100644 --- a/pkg/linter/test/rules/annotate_redeclares_test.dart +++ b/pkg/linter/test/rules/annotate_redeclares_test.dart @@ -82,7 +82,7 @@ extension type E(A a) implements A { test_method_annotated() async { await assertNoDiagnostics(r''' -import 'package:meta/meta.dart'; +import 'package:meta/meta.dart'; class A { void m() {} } diff --git a/pkg/linter/test/rules/avoid_classes_with_only_static_members_test.dart b/pkg/linter/test/rules/avoid_classes_with_only_static_members_test.dart index b5f84a9ad3ed..24e9cad5d785 100644 --- a/pkg/linter/test/rules/avoid_classes_with_only_static_members_test.dart +++ b/pkg/linter/test/rules/avoid_classes_with_only_static_members_test.dart @@ -30,7 +30,7 @@ class A { var b = newFile('$testPackageLibPath/b.dart', r''' part of 'a.dart'; -augment class A { +augment class A { int a = 1; } '''); @@ -52,7 +52,7 @@ class A {} var b = newFile('$testPackageLibPath/b.dart', r''' part of 'a.dart'; -augment class A { +augment class A { static int f = 1; } '''); @@ -76,7 +76,7 @@ class A {} var b = newFile('$testPackageLibPath/b.dart', r''' part of 'a.dart'; -augment class A { +augment class A { static void m() {} } '''); diff --git a/pkg/linter/test/rules/avoid_field_initializers_in_non_const_classes_test.dart b/pkg/linter/test/rules/avoid_field_initializers_in_non_const_classes_test.dart index 20631bdbd88a..ee72cc0c2e4f 100644 --- a/pkg/linter/test/rules/avoid_field_initializers_in_non_const_classes_test.dart +++ b/pkg/linter/test/rules/avoid_field_initializers_in_non_const_classes_test.dart @@ -30,7 +30,7 @@ class A { var b = newFile('$testPackageLibPath/b.dart', r''' part of 'a.dart'; -augment class A { +augment class A { A.aa() : a = 1; } '''); @@ -105,7 +105,7 @@ augment class A { newFile('$testPackageLibPath/a.dart', r''' part 'test.dart'; -class A { +class A { A.aa(); } '''); @@ -124,7 +124,7 @@ augment class A { newFile('$testPackageLibPath/a.dart', r''' part 'test.dart'; -class A { +class A { const A(); } '''); diff --git a/pkg/linter/test/rules/avoid_renaming_method_parameters_test.dart b/pkg/linter/test/rules/avoid_renaming_method_parameters_test.dart index d11ff1366571..d34980790d49 100644 --- a/pkg/linter/test/rules/avoid_renaming_method_parameters_test.dart +++ b/pkg/linter/test/rules/avoid_renaming_method_parameters_test.dart @@ -57,7 +57,7 @@ part of 'a.dart'; augment class A { augment void m(int q) {} - augment void m(int q) {} + augment void m(int q) {} } ''', [ lint(58, 1), // Only the first augmentation gets linted. diff --git a/pkg/linter/test/rules/overridden_fields_test.dart b/pkg/linter/test/rules/overridden_fields_test.dart index 3c8203fb3a4e..43d66cb9d5ce 100644 --- a/pkg/linter/test/rules/overridden_fields_test.dart +++ b/pkg/linter/test/rules/overridden_fields_test.dart @@ -31,7 +31,7 @@ class A extends O { } var b = newFile('$testPackageLibPath/b.dart', r''' part of 'a.dart'; -augment class A { +augment class A { final a = ''; } '''); @@ -41,7 +41,7 @@ augment class A { result = await resolveFile(b.path); await assertDiagnosticsIn(errors, [ - lint(46, 1), + lint(45, 1), ]); } @@ -54,7 +54,7 @@ class O { } class A extends O { - @override + @override final a = ''; } '''); @@ -62,14 +62,14 @@ class A extends O { var b = newFile('$testPackageLibPath/b.dart', r''' part of 'a.dart'; -augment class A { +augment class A { augment final a = ''; } '''); result = await resolveFile(a.path); await assertDiagnosticsIn(errors, [ - lint(86, 1), + lint(85, 1), ]); result = await resolveFile(b.path); diff --git a/pkg/linter/test/rules/prefer_void_to_null_test.dart b/pkg/linter/test/rules/prefer_void_to_null_test.dart index eae8d0ce3eea..398c9f5ecd81 100644 --- a/pkg/linter/test/rules/prefer_void_to_null_test.dart +++ b/pkg/linter/test/rules/prefer_void_to_null_test.dart @@ -23,7 +23,7 @@ part 'test.dart'; class A { Future? f; -} +} '''); await assertNoDiagnostics(r''' @@ -55,7 +55,7 @@ part 'test.dart'; class A { Future? get v => null; -} +} '''); await assertNoDiagnostics(r''' diff --git a/pkg/linter/tool/checks/driver.dart b/pkg/linter/tool/checks/driver.dart index 4caeb9398d0b..64447836dff9 100644 --- a/pkg/linter/tool/checks/driver.dart +++ b/pkg/linter/tool/checks/driver.dart @@ -22,6 +22,7 @@ import 'package:path/path.dart' as path; import '../../test/mocks.dart'; import 'rules/no_solo_tests.dart'; +import 'rules/no_trailing_spaces.dart'; import 'rules/visit_registered_nodes.dart'; Future main() async { @@ -31,7 +32,11 @@ Future main() async { } } -var customChecks = [VisitRegisteredNodes(), NoSoloTests()]; +var customChecks = [ + VisitRegisteredNodes(), + NoSoloTests(), + NoTrailingSpaces(), +]; Future> runChecks() async { var rules = diff --git a/pkg/linter/tool/checks/rules/no_trailing_spaces.dart b/pkg/linter/tool/checks/rules/no_trailing_spaces.dart new file mode 100644 index 000000000000..42a566139403 --- /dev/null +++ b/pkg/linter/tool/checks/rules/no_trailing_spaces.dart @@ -0,0 +1,54 @@ +// Copyright (c) 2024, 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:linter/src/analyzer.dart'; + +const _desc = r"Don't create string literals with trailing spaces in tests."; + +class NoTrailingSpaces extends LintRule { + static const LintCode code = LintCode('no_trailing_spaces', _desc, + correctionMessage: 'Try removing the trailing spaces.', + hasPublishedDocs: true); + + NoTrailingSpaces() + : super( + name: 'no_trailing_spaces', + description: _desc, + ); + + @override + LintCode get lintCode => code; + + @override + void registerNodeProcessors( + NodeLintRegistry registry, LinterContext context) { + if (context.definingUnit.unit.inTestDir) { + var visitor = _Visitor(this); + registry.addMethodInvocation(this, visitor); + } + } +} + +class _Visitor extends SimpleAstVisitor { + final LintRule rule; + + _Visitor(this.rule); + + @override + void visitMethodInvocation(MethodInvocation node) { + var arguments = node.argumentList.arguments; + for (var sourceString in arguments) { + if (sourceString is! SimpleStringLiteral) return; + + var literal = sourceString.literal; + if (literal is! StringToken) return; + + if (literal.lexeme.contains(' \n')) { + rule.reportLintForToken(literal); + } + } + } +}