verify linter source sorting Fixes: https://github.com/dart-lang/linter/issues/2368 Change-Id: I498e271dc32348115a34472b44688be8de2169a3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/323901 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analysis_server/test/verify_sorted_test.dart b/pkg/analysis_server/test/verify_sorted_test.dart index 827cba3..853aa00 100644 --- a/pkg/analysis_server/test/verify_sorted_test.dart +++ b/pkg/analysis_server/test/verify_sorted_test.dart
@@ -28,6 +28,10 @@ buildTestsForAnalyzerPlugin(); }); + group('linter', () { + buildTestsForLinter(); + }); + group('nnbd_migration', () { buildTestsForNnbdMigration(); }); @@ -50,13 +54,10 @@ excludedPaths: excludedPaths, resourceProvider: provider, ); - var contexts = collection.contexts; - if (contexts.length != 1) { - fail('The directory $packagePath contains multiple analysis contexts.'); + for (var context in collection.contexts) { + buildTestsIn(context.currentSession, packagePath, excludedPaths, + provider.getFolder(packagePath)); } - - buildTestsIn(contexts[0].currentSession, packagePath, excludedPaths, - provider.getFolder(packagePath)); } void buildTestsForAnalysisServer() { @@ -120,6 +121,12 @@ ); } +void buildTestsForLinter() { + buildTests(packagePath: 'linter', excludedPaths: [ + 'test_data', + ]); +} + void buildTestsForNnbdMigration() { buildTests( packagePath: 'nnbd_migration',
diff --git a/pkg/linter/lib/src/rules/avoid_empty_else.dart b/pkg/linter/lib/src/rules/avoid_empty_else.dart index 16ea88c..eb06395 100644 --- a/pkg/linter/lib/src/rules/avoid_empty_else.dart +++ b/pkg/linter/lib/src/rules/avoid_empty_else.dart
@@ -28,9 +28,6 @@ correctionMessage: 'Try removing the empty statement or removing the else clause.'); - @override - LintCode get lintCode => code; - AvoidEmptyElse() : super( name: 'avoid_empty_else', @@ -39,6 +36,9 @@ group: Group.errors); @override + LintCode get lintCode => code; + + @override void registerNodeProcessors( NodeLintRegistry registry, LinterContext context) { var visitor = _Visitor(this);
diff --git a/pkg/linter/lib/src/rules/avoid_positional_boolean_parameters.dart b/pkg/linter/lib/src/rules/avoid_positional_boolean_parameters.dart index 31613ec..f26f1927 100644 --- a/pkg/linter/lib/src/rules/avoid_positional_boolean_parameters.dart +++ b/pkg/linter/lib/src/rules/avoid_positional_boolean_parameters.dart
@@ -94,6 +94,11 @@ } @override + void visitGenericFunctionType(GenericFunctionType node) { + checkParams(node.parameters.parameters); + } + + @override void visitMethodDeclaration(MethodDeclaration node) { var declaredElement = node.declaredElement; if (declaredElement != null && @@ -106,11 +111,6 @@ } } - @override - void visitGenericFunctionType(GenericFunctionType node) { - checkParams(node.parameters.parameters); - } - bool _isOverridingMember(Element member) { var classElement = member.thisOrAncestorOfType<ClassElement>(); if (classElement == null) return false;
diff --git a/pkg/linter/lib/src/rules/avoid_relative_lib_imports.dart b/pkg/linter/lib/src/rules/avoid_relative_lib_imports.dart index e1e4788..484f18e 100644 --- a/pkg/linter/lib/src/rules/avoid_relative_lib_imports.dart +++ b/pkg/linter/lib/src/rules/avoid_relative_lib_imports.dart
@@ -47,9 +47,6 @@ "Try fixing the relative path or changing the import to a 'package:' " 'import.'); - @override - LintCode get lintCode => code; - AvoidRelativeLibImports() : super( name: 'avoid_relative_lib_imports', @@ -58,6 +55,9 @@ group: Group.errors); @override + LintCode get lintCode => code; + + @override void registerNodeProcessors( NodeLintRegistry registry, LinterContext context) { var visitor = _Visitor(this);
diff --git a/pkg/linter/lib/src/rules/avoid_return_types_on_setters.dart b/pkg/linter/lib/src/rules/avoid_return_types_on_setters.dart index 50b17c2..942ac69 100644 --- a/pkg/linter/lib/src/rules/avoid_return_types_on_setters.dart +++ b/pkg/linter/lib/src/rules/avoid_return_types_on_setters.dart
@@ -27,9 +27,6 @@ '''; class AvoidReturnTypesOnSetters extends LintRule { - @override - bool get canUseParsedResult => true; - static const LintCode code = LintCode( 'avoid_return_types_on_setters', 'Unnecessary return type on a setter.', correctionMessage: 'Try removing the return type.'); @@ -42,6 +39,9 @@ group: Group.style); @override + bool get canUseParsedResult => true; + + @override LintCode get lintCode => code; @override
diff --git a/pkg/linter/lib/src/rules/collection_methods_unrelated_type.dart b/pkg/linter/lib/src/rules/collection_methods_unrelated_type.dart index d424499..5a14b9c 100644 --- a/pkg/linter/lib/src/rules/collection_methods_unrelated_type.dart +++ b/pkg/linter/lib/src/rules/collection_methods_unrelated_type.dart
@@ -89,6 +89,16 @@ _Visitor(super.rule, super.typeSystem, super.typeProvider); @override + List<MethodDefinition> get indexOperators => [ + // Argument to `Map<K, V>.[]` should be assignable to `K`. + MethodDefinitionForElement( + typeProvider.mapElement, + '[]', + ExpectedArgumentKind.assignableToCollectionTypeArgument, + ), + ]; + + @override List<MethodDefinition> get methods => [ // Argument to `Iterable<E>.contains` should be assignable to `E`. MethodDefinitionForElement( @@ -141,14 +151,4 @@ ExpectedArgumentKind.assignableToCollectionTypeArgument, ), ]; - - @override - List<MethodDefinition> get indexOperators => [ - // Argument to `Map<K, V>.[]` should be assignable to `K`. - MethodDefinitionForElement( - typeProvider.mapElement, - '[]', - ExpectedArgumentKind.assignableToCollectionTypeArgument, - ), - ]; }
diff --git a/pkg/linter/lib/src/rules/curly_braces_in_flow_control_structures.dart b/pkg/linter/lib/src/rules/curly_braces_in_flow_control_structures.dart index 184a013..5e5f77f 100644 --- a/pkg/linter/lib/src/rules/curly_braces_in_flow_control_structures.dart +++ b/pkg/linter/lib/src/rules/curly_braces_in_flow_control_structures.dart
@@ -51,9 +51,6 @@ '''; class CurlyBracesInFlowControlStructures extends LintRule { - @override - bool get canUseParsedResult => true; - static const LintCode code = LintCode( 'curly_braces_in_flow_control_structures', 'Statements in {0} should be enclosed in a block.', @@ -67,6 +64,9 @@ group: Group.style); @override + bool get canUseParsedResult => true; + + @override LintCode get lintCode => code; @override
diff --git a/pkg/linter/lib/src/rules/library_annotations.dart b/pkg/linter/lib/src/rules/library_annotations.dart index 54521b6..efbe134 100644 --- a/pkg/linter/lib/src/rules/library_annotations.dart +++ b/pkg/linter/lib/src/rules/library_annotations.dart
@@ -68,10 +68,10 @@ class _Visitor extends SimpleAstVisitor<void> { final LibraryAnnotations rule; - _Visitor(this.rule); - Directive? firstDirective; + _Visitor(this.rule); + @override void visitCompilationUnit(CompilationUnit node) { if (node.directives.isNotEmpty) {
diff --git a/pkg/linter/lib/src/rules/no_literal_bool_comparisons.dart b/pkg/linter/lib/src/rules/no_literal_bool_comparisons.dart index e6fbb0c..a25c680 100644 --- a/pkg/linter/lib/src/rules/no_literal_bool_comparisons.dart +++ b/pkg/linter/lib/src/rules/no_literal_bool_comparisons.dart
@@ -61,6 +61,11 @@ _Visitor(this.rule, this.context); + bool isBool(DartType? type) => + type != null && + type.isDartCoreBool && + context.typeSystem.isNonNullable(type); + @override void visitBinaryExpression(BinaryExpression node) { if (node.operator.type == TokenType.EQ_EQ || @@ -74,9 +79,4 @@ } } } - - bool isBool(DartType? type) => - type != null && - type.isDartCoreBool && - context.typeSystem.isNonNullable(type); }
diff --git a/pkg/linter/lib/src/rules/no_self_assignments.dart b/pkg/linter/lib/src/rules/no_self_assignments.dart index d7aea6d..df79acf 100644 --- a/pkg/linter/lib/src/rules/no_self_assignments.dart +++ b/pkg/linter/lib/src/rules/no_self_assignments.dart
@@ -5,6 +5,7 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/dart/ast/visitor.dart'; + import '../analyzer.dart'; const _desc = r"Don't assign a variable to itself.";
diff --git a/pkg/linter/lib/src/rules/prefer_generic_function_type_aliases.dart b/pkg/linter/lib/src/rules/prefer_generic_function_type_aliases.dart index af7770c..c290e60 100644 --- a/pkg/linter/lib/src/rules/prefer_generic_function_type_aliases.dart +++ b/pkg/linter/lib/src/rules/prefer_generic_function_type_aliases.dart
@@ -33,9 +33,6 @@ '''; class PreferGenericFunctionTypeAliases extends LintRule { - @override - bool get canUseParsedResult => true; - static const LintCode code = LintCode('prefer_generic_function_type_aliases', "Use the generic function type syntax in 'typedef's.", correctionMessage: "Try using the generic function type syntax ('{0}')."); @@ -48,6 +45,9 @@ group: Group.style); @override + bool get canUseParsedResult => true; + + @override LintCode get lintCode => code; @override
diff --git a/pkg/linter/lib/src/rules/slash_for_doc_comments.dart b/pkg/linter/lib/src/rules/slash_for_doc_comments.dart index 990cb89..72b6333 100644 --- a/pkg/linter/lib/src/rules/slash_for_doc_comments.dart +++ b/pkg/linter/lib/src/rules/slash_for_doc_comments.dart
@@ -43,9 +43,6 @@ } class SlashForDocComments extends LintRule { - @override - bool get canUseParsedResult => true; - static const LintCode code = LintCode('slash_for_doc_comments', "Use the end-of-line form ('///') for doc comments.", correctionMessage: "Try rewriting the comment to use '///'."); @@ -58,6 +55,9 @@ group: Group.style); @override + bool get canUseParsedResult => true; + + @override LintCode get lintCode => code; @override
diff --git a/pkg/linter/lib/src/rules/unnecessary_const.dart b/pkg/linter/lib/src/rules/unnecessary_const.dart index 646704f..6f9ebf1 100644 --- a/pkg/linter/lib/src/rules/unnecessary_const.dart +++ b/pkg/linter/lib/src/rules/unnecessary_const.dart
@@ -34,9 +34,6 @@ '''; class UnnecessaryConst extends LintRule { - @override - bool get canUseParsedResult => true; - static const LintCode code = LintCode( 'unnecessary_const', "Unnecessary 'const' keyword.", correctionMessage: 'Try removing the keyword.'); @@ -49,6 +46,9 @@ group: Group.style); @override + bool get canUseParsedResult => true; + + @override LintCode get lintCode => code; @override
diff --git a/pkg/linter/lib/src/rules/unnecessary_final.dart b/pkg/linter/lib/src/rules/unnecessary_final.dart index 7992cd8..cf8ef5e 100644 --- a/pkg/linter/lib/src/rules/unnecessary_final.dart +++ b/pkg/linter/lib/src/rules/unnecessary_final.dart
@@ -85,6 +85,16 @@ LintCode getErrorCode(Object? type) => type == null ? UnnecessaryFinal.withoutType : UnnecessaryFinal.withType; + (Token?, AstNode?) getParameterDetails(FormalParameter node) { + var parameter = node is DefaultFormalParameter ? node.parameter : node; + return switch (parameter) { + FieldFormalParameter() => (parameter.keyword, parameter.type), + SimpleFormalParameter() => (parameter.keyword, parameter.type), + SuperFormalParameter() => (parameter.keyword, parameter.type), + _ => (null, null), + }; + } + @override void visitDeclaredVariablePattern(DeclaredVariablePattern node) { var keyword = node.keyword; @@ -138,14 +148,4 @@ rule.reportLintForToken(node.variables.keyword, errorCode: errorCode); } } - - (Token?, AstNode?) getParameterDetails(FormalParameter node) { - var parameter = node is DefaultFormalParameter ? node.parameter : node; - return switch (parameter) { - FieldFormalParameter() => (parameter.keyword, parameter.type), - SimpleFormalParameter() => (parameter.keyword, parameter.type), - SuperFormalParameter() => (parameter.keyword, parameter.type), - _ => (null, null), - }; - } }
diff --git a/pkg/linter/lib/src/rules/unnecessary_library_directive.dart b/pkg/linter/lib/src/rules/unnecessary_library_directive.dart index 34eed3c..e59294b 100644 --- a/pkg/linter/lib/src/rules/unnecessary_library_directive.dart +++ b/pkg/linter/lib/src/rules/unnecessary_library_directive.dart
@@ -7,8 +7,6 @@ import '../analyzer.dart'; -const _name = 'unnecessary_library_directive'; - const _desc = 'Avoid library directives unless they have documentation comments or ' 'annotations.'; @@ -37,6 +35,8 @@ flagged for unnecessary library directives. '''; +const _name = 'unnecessary_library_directive'; + class UnnecessaryLibraryDirective extends LintRule { static const LintCode code = LintCode( _name,
diff --git a/pkg/linter/lib/src/rules/unnecessary_new.dart b/pkg/linter/lib/src/rules/unnecessary_new.dart index 44bbf5a..a493ef3 100644 --- a/pkg/linter/lib/src/rules/unnecessary_new.dart +++ b/pkg/linter/lib/src/rules/unnecessary_new.dart
@@ -32,9 +32,6 @@ '''; class UnnecessaryNew extends LintRule { - @override - bool get canUseParsedResult => true; - static const LintCode code = LintCode( 'unnecessary_new', "Unnecessary 'new' keyword.", correctionMessage: "Try removing the 'new' keyword."); @@ -47,6 +44,9 @@ group: Group.style); @override + bool get canUseParsedResult => true; + + @override LintCode get lintCode => code; @override
diff --git a/pkg/linter/lib/src/rules/unnecessary_string_escapes.dart b/pkg/linter/lib/src/rules/unnecessary_string_escapes.dart index dd7c85a..d1ef00c 100644 --- a/pkg/linter/lib/src/rules/unnecessary_string_escapes.dart +++ b/pkg/linter/lib/src/rules/unnecessary_string_escapes.dart
@@ -28,9 +28,6 @@ '''; class UnnecessaryStringEscapes extends LintRule { - @override - bool get canUseParsedResult => true; - static const LintCode code = LintCode( 'unnecessary_string_escapes', 'Unnecessary escape in string literal.', correctionMessage: "Remove the '\\' escape."); @@ -43,6 +40,9 @@ group: Group.style); @override + bool get canUseParsedResult => true; + + @override LintCode get lintCode => code; @override
diff --git a/pkg/linter/lib/src/rules/unreachable_from_main.dart b/pkg/linter/lib/src/rules/unreachable_from_main.dart index c852b0d..272e888 100644 --- a/pkg/linter/lib/src/rules/unreachable_from_main.dart +++ b/pkg/linter/lib/src/rules/unreachable_from_main.dart
@@ -595,16 +595,6 @@ } extension on NamedType { - TypeAnnotation get topmostTypeAnnotation { - TypeAnnotation topTypeAnnotation = this; - var parent = this.parent; - while (parent is TypeAnnotation) { - topTypeAnnotation = parent; - parent = topTypeAnnotation.parent; - } - return topTypeAnnotation; - } - bool get isInExternalVariableTypeOrFunctionReturnType { var topTypeAnnotation = topmostTypeAnnotation; @@ -621,4 +611,14 @@ } return false; } + + TypeAnnotation get topmostTypeAnnotation { + TypeAnnotation topTypeAnnotation = this; + var parent = this.parent; + while (parent is TypeAnnotation) { + topTypeAnnotation = parent; + parent = topTypeAnnotation.parent; + } + return topTypeAnnotation; + } }
diff --git a/pkg/linter/lib/src/rules/use_build_context_synchronously.dart b/pkg/linter/lib/src/rules/use_build_context_synchronously.dart index 8e98529..350ca15 100644 --- a/pkg/linter/lib/src/rules/use_build_context_synchronously.dart +++ b/pkg/linter/lib/src/rules/use_build_context_synchronously.dart
@@ -50,41 +50,6 @@ ``` '''; -class UseBuildContextSynchronously extends LintRule { - static const LintCode code = LintCode('use_build_context_synchronously', - "Don't use 'BuildContext's across async gaps.", - correctionMessage: - "Try rewriting the code to not reference the 'BuildContext'."); - - /// Flag to short-circuit `inTestDir` checking when running tests. - final bool inTestMode; - - UseBuildContextSynchronously({this.inTestMode = false}) - : super( - name: 'use_build_context_synchronously', - description: _desc, - details: _details, - group: Group.errors, - state: State.experimental(), - ); - - @override - LintCode get lintCode => code; - - @override - void registerNodeProcessors( - NodeLintRegistry registry, LinterContext context) { - var unit = context.currentUnit.unit; - if (inTestMode || !context.inTestDir(unit)) { - var visitor = _Visitor(this); - registry.addMethodInvocation(this, visitor); - registry.addInstanceCreationExpression(this, visitor); - registry.addFunctionExpressionInvocation(this, visitor); - registry.addPrefixedIdentifier(this, visitor); - } - } -} - /// An enum whose values describe the state of asynchrony that a certain node /// has in the syntax tree, with respect to another node. /// @@ -813,6 +778,41 @@ } } +class UseBuildContextSynchronously extends LintRule { + static const LintCode code = LintCode('use_build_context_synchronously', + "Don't use 'BuildContext's across async gaps.", + correctionMessage: + "Try rewriting the code to not reference the 'BuildContext'."); + + /// Flag to short-circuit `inTestDir` checking when running tests. + final bool inTestMode; + + UseBuildContextSynchronously({this.inTestMode = false}) + : super( + name: 'use_build_context_synchronously', + description: _desc, + details: _details, + group: Group.errors, + state: State.experimental(), + ); + + @override + LintCode get lintCode => code; + + @override + void registerNodeProcessors( + NodeLintRegistry registry, LinterContext context) { + var unit = context.currentUnit.unit; + if (inTestMode || !context.inTestDir(unit)) { + var visitor = _Visitor(this); + registry.addMethodInvocation(this, visitor); + registry.addInstanceCreationExpression(this, visitor); + registry.addFunctionExpressionInvocation(this, visitor); + registry.addPrefixedIdentifier(this, visitor); + } + } +} + class _Visitor extends SimpleAstVisitor { static const mountedName = 'mounted';
diff --git a/pkg/linter/lib/src/rules/use_function_type_syntax_for_parameters.dart b/pkg/linter/lib/src/rules/use_function_type_syntax_for_parameters.dart index 6918434..aac2b84 100644 --- a/pkg/linter/lib/src/rules/use_function_type_syntax_for_parameters.dart +++ b/pkg/linter/lib/src/rules/use_function_type_syntax_for_parameters.dart
@@ -25,9 +25,6 @@ '''; class UseFunctionTypeSyntaxForParameters extends LintRule { - @override - bool get canUseParsedResult => true; - static const LintCode code = LintCode( 'use_function_type_syntax_for_parameters', "Use the generic function type syntax to declare the parameter '{0}'.", @@ -41,6 +38,9 @@ group: Group.style); @override + bool get canUseParsedResult => true; + + @override LintCode get lintCode => code; @override
diff --git a/pkg/linter/lib/src/util/charcodes.dart b/pkg/linter/lib/src/util/charcodes.dart index 3966d23..25153faa 100644 --- a/pkg/linter/lib/src/util/charcodes.dart +++ b/pkg/linter/lib/src/util/charcodes.dart
@@ -2,26 +2,26 @@ // 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. -/// Character `.`. -const int $dot = 0x2e; - /// Character `0`. const int $0 = 0x30; /// Character `9`. const int $9 = 0x39; -/// Character `\`. -const int $backslash = 0x5c; - /// Character `_`. const int $_ = 0x5f; /// Character `a`. const int $a = 0x61; -/// Character `z`. -const int $z = 0x7a; +/// Character `\`. +const int $backslash = 0x5c; + +/// Character `.`. +const int $dot = 0x2e; /// Character `|`. const int $pipe = 0x7c; + +/// Character `z`. +const int $z = 0x7a;
diff --git a/pkg/linter/lib/src/util/score_utils.dart b/pkg/linter/lib/src/util/score_utils.dart index 143880c..dc507e4 100644 --- a/pkg/linter/lib/src/util/score_utils.dart +++ b/pkg/linter/lib/src/util/score_utils.dart
@@ -7,43 +7,20 @@ import '../utils.dart'; -Future<List<String>> _readCoreLints() async => _fetchLints( - 'https://raw.githubusercontent.com/dart-lang/lints/main/lib/core.yaml'); - -/// todo(pq): de-duplicate these fetches / URIs -Future<List<String>> _readFlutterLints() async => _fetchLints( - 'https://raw.githubusercontent.com/flutter/packages/main/packages/flutter_lints/lib/flutter.yaml'); - -Future<List<String>> _readRecommendedLints() async => _fetchLints( - 'https://raw.githubusercontent.com/dart-lang/lints/main/lib/recommended.yaml'); - -/// todo(pq): update `scorecard.dart` to reuse these fetch functions. -Future<List<String>> _fetchLints(String url) async { - var req = await http.get(Uri.parse(url)); - return _readLints(req.body); -} - -List<String> _readLints(String contents) { - var lintConfigs = processAnalysisOptionsFile(contents); - if (lintConfigs == null) { - return []; - } - return lintConfigs.ruleConfigs.map((c) => c.name ?? '<unknown>').toList(); -} - List<String>? _coreRules; -List<String>? _recommendedRules; + List<String>? _flutterRules; +List<String>? _recommendedRules; + Future<List<String>> get coreRules async => _coreRules ??= await _readCoreLints(); -Future<List<String>> get recommendedRules async => - _recommendedRules ??= await _readRecommendedLints(); - Future<List<String>> get flutterRules async => _flutterRules ??= await _readFlutterLints(); +Future<List<String>> get recommendedRules async => + _recommendedRules ??= await _readRecommendedLints(); Future<List<String>> fetchRules(Uri optionsUrl) async { var config = await _fetchConfig(optionsUrl); if (config == null) { @@ -65,3 +42,27 @@ var req = await http.get(url); return processAnalysisOptionsFile(req.body); } + +/// todo(pq): update `scorecard.dart` to reuse these fetch functions. +Future<List<String>> _fetchLints(String url) async { + var req = await http.get(Uri.parse(url)); + return _readLints(req.body); +} + +Future<List<String>> _readCoreLints() async => _fetchLints( + 'https://raw.githubusercontent.com/dart-lang/lints/main/lib/core.yaml'); + +/// todo(pq): de-duplicate these fetches / URIs +Future<List<String>> _readFlutterLints() async => _fetchLints( + 'https://raw.githubusercontent.com/flutter/packages/main/packages/flutter_lints/lib/flutter.yaml'); + +List<String> _readLints(String contents) { + var lintConfigs = processAnalysisOptionsFile(contents); + if (lintConfigs == null) { + return []; + } + return lintConfigs.ruleConfigs.map((c) => c.name ?? '<unknown>').toList(); +} + +Future<List<String>> _readRecommendedLints() async => _fetchLints( + 'https://raw.githubusercontent.com/dart-lang/lints/main/lib/recommended.yaml');
diff --git a/pkg/linter/lib/src/util/unrelated_types_visitor.dart b/pkg/linter/lib/src/util/unrelated_types_visitor.dart index a5bfe0f..aea2a28 100644 --- a/pkg/linter/lib/src/util/unrelated_types_visitor.dart +++ b/pkg/linter/lib/src/util/unrelated_types_visitor.dart
@@ -11,143 +11,18 @@ import '../analyzer.dart'; import '../util/dart_type_utilities.dart'; -/// Base class for visitor used in rules where we want to lint about invoking -/// methods on generic classes where the type of the singular argument is -/// unrelated to the singular type argument of the class. Extending this -/// visitor is as simple as knowing the methods, classes and libraries that -/// uniquely define the target, i.e. implement only [methods]. -abstract class UnrelatedTypesProcessors extends SimpleAstVisitor<void> { - final LintRule rule; - final TypeSystem typeSystem; - final TypeProvider typeProvider; +/// The kind of the expected argument. +enum ExpectedArgumentKind { + /// An argument is expected to be assignable to a type argument on the + /// collection type. + assignableToCollectionTypeArgument, - UnrelatedTypesProcessors(this.rule, this.typeSystem, this.typeProvider); + /// An argument is expected to be assignable to the collection type. + assignableToCollection, - /// The method definitions which this [UnrelatedTypesProcessors] is concerned - /// with. - List<MethodDefinition> get methods; - - List<MethodDefinition> get indexOperators => []; - - @override - void visitIndexExpression(IndexExpression node) { - var matchingMethods = - indexOperators.where((method) => '[]' == method.methodName); - if (matchingMethods.isEmpty) { - return; - } - - var targetType = _getTargetType(node, node.realTarget); - if (targetType is! InterfaceType) { - return; - } - - for (var methodDefinition in matchingMethods) { - var collectionType = methodDefinition.collectionTypeFor(targetType); - if (collectionType != null) { - _checkMethod(node.index, methodDefinition, collectionType); - return; - } - } - } - - @override - void visitMethodInvocation(MethodInvocation node) { - if (node.argumentList.arguments.length != 1) { - return; - } - - var matchingMethods = - methods.where((method) => node.methodName.name == method.methodName); - if (matchingMethods.isEmpty) { - return; - } - - // At this point, we know that [node] is an invocation of a method which - // has the same name as the method that this [UnrelatedTypesProcessors] is - // concerned with, and that the method call has a single argument. - // - // We've completed the "cheap" checks, and must now continue with the - // arduous task of determining whether the method target implements - // [definition]. - var targetType = _getTargetType(node, node.realTarget); - if (targetType is! InterfaceType) { - return; - } - - for (var methodDefinition in matchingMethods) { - var collectionType = methodDefinition.collectionTypeFor(targetType); - if (collectionType != null) { - _checkMethod(node.argumentList.arguments.first, methodDefinition, - collectionType); - return; - } - } - } - - DartType? _getTargetType(Expression node, Expression? target) { - if (target != null) { - return target.staticType; - } - - // Look for an implicit receiver, starting with [node]'s parent's parent. - for (AstNode? parent = node.parent?.parent; - parent != null; - parent = parent.parent) { - if (parent is ClassDeclaration) { - return parent.declaredElement?.thisType; - } else if (parent is MixinDeclaration) { - return parent.declaredElement?.thisType; - } else if (parent is EnumDeclaration) { - return parent.declaredElement?.thisType; - } else if (parent is ExtensionDeclaration) { - return parent.extendedType.type; - } - } - return null; - } - - /// Checks a [MethodInvocation] or [IndexExpression] which has a singular - /// [argument] and matches [methodDefinition], with a target with a static - /// type of [collectionType]. - void _checkMethod(Expression argument, MethodDefinition methodDefinition, - InterfaceType collectionType) { - // Finally, determine whether the type of the argument is related to the - // type of the method target. - var argumentType = argument.staticType; - if (argumentType == null) return; - - switch (methodDefinition.expectedArgumentKind) { - case ExpectedArgumentKind.assignableToCollectionTypeArgument: - var typeArgument = - collectionType.typeArguments[methodDefinition.typeArgumentIndex]; - if (typesAreUnrelated(typeSystem, argumentType, typeArgument)) { - rule.reportLint(argument, arguments: [ - argumentType.getDisplayString(withNullability: true), - typeArgument.getDisplayString(withNullability: true), - ]); - } - - case ExpectedArgumentKind.assignableToCollection: - if (!typeSystem.isAssignableTo(argumentType, collectionType)) { - rule.reportLint(argument, arguments: [ - argumentType.getDisplayString(withNullability: true), - collectionType.getDisplayString(withNullability: true), - ]); - } - - case ExpectedArgumentKind.assignableToIterableOfTypeArgument: - var iterableType = - collectionType.asInstanceOf(typeProvider.iterableElement); - if (iterableType != null && - !typeSystem.isAssignableTo(argumentType, iterableType)) { - rule.reportLint(argument, arguments: [ - argumentType.getDisplayString(withNullability: true), - iterableType.getDisplayString(withNullability: true), - ]); - } - } - } + /// An argument is expected to be assignable to `Iterable<E>` where `E` is the + /// (only) type argument on the collection type. + assignableToIterableOfTypeArgument, } /// A definition of a method and the expected characteristics of the first @@ -211,16 +86,141 @@ } } -/// The kind of the expected argument. -enum ExpectedArgumentKind { - /// An argument is expected to be assignable to a type argument on the - /// collection type. - assignableToCollectionTypeArgument, +/// Base class for visitor used in rules where we want to lint about invoking +/// methods on generic classes where the type of the singular argument is +/// unrelated to the singular type argument of the class. Extending this +/// visitor is as simple as knowing the methods, classes and libraries that +/// uniquely define the target, i.e. implement only [methods]. +abstract class UnrelatedTypesProcessors extends SimpleAstVisitor<void> { + final LintRule rule; + final TypeSystem typeSystem; + final TypeProvider typeProvider; - /// An argument is expected to be assignable to the collection type. - assignableToCollection, + UnrelatedTypesProcessors(this.rule, this.typeSystem, this.typeProvider); - /// An argument is expected to be assignable to `Iterable<E>` where `E` is the - /// (only) type argument on the collection type. - assignableToIterableOfTypeArgument, + List<MethodDefinition> get indexOperators => []; + + /// The method definitions which this [UnrelatedTypesProcessors] is concerned + /// with. + List<MethodDefinition> get methods; + + @override + void visitIndexExpression(IndexExpression node) { + var matchingMethods = + indexOperators.where((method) => '[]' == method.methodName); + if (matchingMethods.isEmpty) { + return; + } + + var targetType = _getTargetType(node, node.realTarget); + if (targetType is! InterfaceType) { + return; + } + + for (var methodDefinition in matchingMethods) { + var collectionType = methodDefinition.collectionTypeFor(targetType); + if (collectionType != null) { + _checkMethod(node.index, methodDefinition, collectionType); + return; + } + } + } + + @override + void visitMethodInvocation(MethodInvocation node) { + if (node.argumentList.arguments.length != 1) { + return; + } + + var matchingMethods = + methods.where((method) => node.methodName.name == method.methodName); + if (matchingMethods.isEmpty) { + return; + } + + // At this point, we know that [node] is an invocation of a method which + // has the same name as the method that this [UnrelatedTypesProcessors] is + // concerned with, and that the method call has a single argument. + // + // We've completed the "cheap" checks, and must now continue with the + // arduous task of determining whether the method target implements + // [definition]. + var targetType = _getTargetType(node, node.realTarget); + if (targetType is! InterfaceType) { + return; + } + + for (var methodDefinition in matchingMethods) { + var collectionType = methodDefinition.collectionTypeFor(targetType); + if (collectionType != null) { + _checkMethod(node.argumentList.arguments.first, methodDefinition, + collectionType); + return; + } + } + } + + /// Checks a [MethodInvocation] or [IndexExpression] which has a singular + /// [argument] and matches [methodDefinition], with a target with a static + /// type of [collectionType]. + void _checkMethod(Expression argument, MethodDefinition methodDefinition, + InterfaceType collectionType) { + // Finally, determine whether the type of the argument is related to the + // type of the method target. + var argumentType = argument.staticType; + if (argumentType == null) return; + + switch (methodDefinition.expectedArgumentKind) { + case ExpectedArgumentKind.assignableToCollectionTypeArgument: + var typeArgument = + collectionType.typeArguments[methodDefinition.typeArgumentIndex]; + if (typesAreUnrelated(typeSystem, argumentType, typeArgument)) { + rule.reportLint(argument, arguments: [ + argumentType.getDisplayString(withNullability: true), + typeArgument.getDisplayString(withNullability: true), + ]); + } + + case ExpectedArgumentKind.assignableToCollection: + if (!typeSystem.isAssignableTo(argumentType, collectionType)) { + rule.reportLint(argument, arguments: [ + argumentType.getDisplayString(withNullability: true), + collectionType.getDisplayString(withNullability: true), + ]); + } + + case ExpectedArgumentKind.assignableToIterableOfTypeArgument: + var iterableType = + collectionType.asInstanceOf(typeProvider.iterableElement); + if (iterableType != null && + !typeSystem.isAssignableTo(argumentType, iterableType)) { + rule.reportLint(argument, arguments: [ + argumentType.getDisplayString(withNullability: true), + iterableType.getDisplayString(withNullability: true), + ]); + } + } + } + + DartType? _getTargetType(Expression node, Expression? target) { + if (target != null) { + return target.staticType; + } + + // Look for an implicit receiver, starting with [node]'s parent's parent. + for (AstNode? parent = node.parent?.parent; + parent != null; + parent = parent.parent) { + if (parent is ClassDeclaration) { + return parent.declaredElement?.thisType; + } else if (parent is MixinDeclaration) { + return parent.declaredElement?.thisType; + } else if (parent is EnumDeclaration) { + return parent.declaredElement?.thisType; + } else if (parent is ExtensionDeclaration) { + return parent.extendedType.type; + } + } + return null; + } }
diff --git a/pkg/linter/test/rules/always_use_package_imports_test.dart b/pkg/linter/test/rules/always_use_package_imports_test.dart index 7d51e46..8535fe9 100644 --- a/pkg/linter/test/rules/always_use_package_imports_test.dart +++ b/pkg/linter/test/rules/always_use_package_imports_test.dart
@@ -17,6 +17,23 @@ @override String get lintRule => 'always_use_package_imports'; + test_internalPackage() async { + var packageConfigBuilder = PackageConfigFileBuilder(); + packageConfigBuilder.add( + name: 'internal_package', + rootPath: '$testPackageRootPath/vendor/internal_package', + ); + writeTestPackageConfig(packageConfigBuilder); + + newFile('$testPackageRootPath/vendor/internal_package/lib/lib.dart', r''' +class C {} +'''); + await assertNoDiagnostics(r''' +/// This provides [C]. +import 'package:internal_package/lib.dart'; +'''); + } + test_samePackage_packageSchema() async { newFile('$testPackageLibPath/lib.dart', r''' class C {} @@ -51,21 +68,4 @@ lint(30, 10), ]); } - - test_internalPackage() async { - var packageConfigBuilder = PackageConfigFileBuilder(); - packageConfigBuilder.add( - name: 'internal_package', - rootPath: '$testPackageRootPath/vendor/internal_package', - ); - writeTestPackageConfig(packageConfigBuilder); - - newFile('$testPackageRootPath/vendor/internal_package/lib/lib.dart', r''' -class C {} -'''); - await assertNoDiagnostics(r''' -/// This provides [C]. -import 'package:internal_package/lib.dart'; -'''); - } }
diff --git a/pkg/linter/test/rules/avoid_annotating_with_dynamic_test.dart b/pkg/linter/test/rules/avoid_annotating_with_dynamic_test.dart index 109e8a4..54e326a 100644 --- a/pkg/linter/test/rules/avoid_annotating_with_dynamic_test.dart +++ b/pkg/linter/test/rules/avoid_annotating_with_dynamic_test.dart
@@ -33,28 +33,20 @@ ]); } - test_returnType() async { - await assertNoDiagnostics(r''' -dynamic f() { - return null; -} -'''); - } - - test_requiredParameter() async { - await assertDiagnostics(r''' -void f(dynamic p) {} -''', [ - lint(7, 9), - ]); - } - test_implicitDynamic() async { await assertNoDiagnostics(r''' void f(p) {} '''); } + test_optionalNamedParameter() async { + await assertDiagnostics(r''' +void f({dynamic p}) {} +''', [ + lint(8, 9), + ]); + } + test_optionalParameter() async { await assertDiagnostics(r''' void f([dynamic p]) {} @@ -63,14 +55,22 @@ ]); } - test_optionalNamedParameter() async { + test_requiredParameter() async { await assertDiagnostics(r''' -void f({dynamic p}) {} +void f(dynamic p) {} ''', [ - lint(8, 9), + lint(7, 9), ]); } + test_returnType() async { + await assertNoDiagnostics(r''' +dynamic f() { + return null; +} +'''); + } + test_super() async { await assertDiagnostics(r''' class A {
diff --git a/pkg/linter/test/rules/avoid_function_literals_in_foreach_calls_test.dart b/pkg/linter/test/rules/avoid_function_literals_in_foreach_calls_test.dart index f632612..7782b15 100644 --- a/pkg/linter/test/rules/avoid_function_literals_in_foreach_calls_test.dart +++ b/pkg/linter/test/rules/avoid_function_literals_in_foreach_calls_test.dart
@@ -56,16 +56,6 @@ ]); } - test_functionExpression_targetHasMethodChain() async { - await assertNoDiagnostics(r''' -void f(List<String> people) { - people - .map((person) => person.toUpperCase()) - .forEach((person) => print('$person!')); -} -'''); - } - test_functionExpression_targetHasCascade() async { await assertNoDiagnostics(r''' void f(List<List<List<String>>> lists) { @@ -90,6 +80,16 @@ '''); } + test_functionExpression_targetHasMethodChain() async { + await assertNoDiagnostics(r''' +void f(List<String> people) { + people + .map((person) => person.toUpperCase()) + .forEach((person) => print('$person!')); +} +'''); + } + test_functionExpression_targetInIrrelevantNestedCascade() async { await assertDiagnostics(r''' void f(List<List<List<String>>> lists) {
diff --git a/pkg/linter/test/rules/collection_methods_unrelated_type_test.dart b/pkg/linter/test/rules/collection_methods_unrelated_type_test.dart index e4c64ac..3e73aa5 100644 --- a/pkg/linter/test/rules/collection_methods_unrelated_type_test.dart +++ b/pkg/linter/test/rules/collection_methods_unrelated_type_test.dart
@@ -122,18 +122,18 @@ ''', [lint(66, 3)]); } - test_contains_unrelated_records() async { - await assertDiagnostics("var x = <(int, int)>[].contains(('hi', 'hey'));", [ - lint(32, 13), - ]); - } - test_contains_unrelated_recordAndNonRecord() async { await assertDiagnostics("var x = <(int, int)>[].contains('hi');", [ lint(32, 4), ]); } + test_contains_unrelated_records() async { + await assertDiagnostics("var x = <(int, int)>[].contains(('hi', 'hey'));", [ + lint(32, 13), + ]); + } + test_contains_unrelated_subclassOfList() async { await assertDiagnostics(''' abstract class C implements List<num> {}
diff --git a/pkg/linter/test/rules/directives_ordering_test.dart b/pkg/linter/test/rules/directives_ordering_test.dart index f15f2c5..0c48715 100644 --- a/pkg/linter/test/rules/directives_ordering_test.dart +++ b/pkg/linter/test/rules/directives_ordering_test.dart
@@ -201,6 +201,26 @@ ]); } + test_sortDirectiveSectionsAlphabetically_dotInRelativePath_import() async { + await assertDiagnostics(r''' +import './foo1.dart'; +import '../../foo2.dart'; +import '../foo3.dart'; +import 'foo4.dart'; +// ignore_for_file: unused_import, uri_does_not_exist +''', [lint(22, 25)]); + } + + test_sortDirectiveSectionsAlphabetically_dotInRelativePath_import_ok() async { + await assertNoDiagnostics(r''' +import '../../foo4.dart'; +import '../foo3.dart'; +import './foo2.dart'; +import 'foo1.dart'; +// ignore_for_file: unused_import, uri_does_not_exist +'''); + } + test_sortDirectiveSectionsAlphabetically_packageSchema_export() async { newFile2('$testPackageLibPath/a.dart', ''); newFile2('$testPackageLibPath/b.dart', ''); @@ -274,24 +294,4 @@ lint(102, 16), ]); } - - test_sortDirectiveSectionsAlphabetically_dotInRelativePath_import() async { - await assertDiagnostics(r''' -import './foo1.dart'; -import '../../foo2.dart'; -import '../foo3.dart'; -import 'foo4.dart'; -// ignore_for_file: unused_import, uri_does_not_exist -''', [lint(22, 25)]); - } - - test_sortDirectiveSectionsAlphabetically_dotInRelativePath_import_ok() async { - await assertNoDiagnostics(r''' -import '../../foo4.dart'; -import '../foo3.dart'; -import './foo2.dart'; -import 'foo1.dart'; -// ignore_for_file: unused_import, uri_does_not_exist -'''); - } }
diff --git a/pkg/linter/test/rules/lines_longer_than_80_chars_test.dart b/pkg/linter/test/rules/lines_longer_than_80_chars_test.dart index 688b1e4..2f36a37 100644 --- a/pkg/linter/test/rules/lines_longer_than_80_chars_test.dart +++ b/pkg/linter/test/rules/lines_longer_than_80_chars_test.dart
@@ -17,25 +17,32 @@ @override String get lintRule => 'lines_longer_than_80_chars'; - test_endOfLineComment_shorterThan80() async { + test_blockComment_noSpaceAfter80() async { await assertNoDiagnostics( - '// 5 10 15 20 25 30 35 40 45 50', + '/* 5 10 15 20 25 30 35 40 50 55 60 ' + 'http://url.com/abcde/abcde/abcde/abcde.dart */', ); } - test_endOfLineComment_spaceAfter80() async { + test_blockComment_shorterThan80() async { + await assertNoDiagnostics( + '/* 5 10 15 20 25 30 35 40 45 50 */', + ); + } + + test_blockComment_spaceAfter80() async { await assertDiagnostics( - '// 5 10 15 20 25 30 35 40 50 55 60' - ' 65 70 75 80 85 90 95 100', + '/* 5 10 15 20 25 30 35 40 50 55 60' + ' 65 70 75 80 85 90 95 100 */', [ - lint(80, 15), + lint(80, 18), ], ); } - test_endOfLineComment_noSpaceAfter80() async { + test_docComment_noSpaceAfter80() async { await assertNoDiagnostics( - '// 5 10 15 20 25 30 35 40 50 55 60 ' + '/// 5 10 15 20 25 30 35 40 50 55 60 ' 'http://url.com/abcde/abcde/abcde/abcde.dart', ); } @@ -56,33 +63,35 @@ ); } - test_docComment_noSpaceAfter80() async { + test_endOfLineComment_noSpaceAfter80() async { await assertNoDiagnostics( - '/// 5 10 15 20 25 30 35 40 50 55 60 ' + '// 5 10 15 20 25 30 35 40 50 55 60 ' 'http://url.com/abcde/abcde/abcde/abcde.dart', ); } - test_blockComment_shorterThan80() async { + test_endOfLineComment_shorterThan80() async { await assertNoDiagnostics( - '/* 5 10 15 20 25 30 35 40 45 50 */', + '// 5 10 15 20 25 30 35 40 45 50', ); } - test_blockComment_spaceAfter80() async { + test_endOfLineComment_spaceAfter80() async { await assertDiagnostics( - '/* 5 10 15 20 25 30 35 40 50 55 60' - ' 65 70 75 80 85 90 95 100 */', + '// 5 10 15 20 25 30 35 40 50 55 60' + ' 65 70 75 80 85 90 95 100', [ - lint(80, 18), + lint(80, 15), ], ); } - test_blockComment_noSpaceAfter80() async { + test_multilineBlockComment_noSpaceAfter80() async { await assertNoDiagnostics( - '/* 5 10 15 20 25 30 35 40 50 55 60 ' - 'http://url.com/abcde/abcde/abcde/abcde.dart */', + '/*\n' + ' * 5 10 15 20 25 30 35 40 50 55 60 ' + 'http://url.com/abcde/abcde/abcde/abcde.dart\n' + ' */', ); } @@ -114,13 +123,4 @@ ], ); } - - test_multilineBlockComment_noSpaceAfter80() async { - await assertNoDiagnostics( - '/*\n' - ' * 5 10 15 20 25 30 35 40 50 55 60 ' - 'http://url.com/abcde/abcde/abcde/abcde.dart\n' - ' */', - ); - } }
diff --git a/pkg/linter/test/rules/matching_super_parameters_test.dart b/pkg/linter/test/rules/matching_super_parameters_test.dart index 1164af2..31143de 100644 --- a/pkg/linter/test/rules/matching_super_parameters_test.dart +++ b/pkg/linter/test/rules/matching_super_parameters_test.dart
@@ -38,7 +38,7 @@ '''); } - test_explicitSuperInvocation_matchingWithOffset_withNamedSuper() async { + test_explicitSuperInvocation_matchingWithOffset_withNamedExplicit() async { await assertNoDiagnostics(r''' class D extends C { final String w; @@ -46,9 +46,8 @@ D( this.w, super.x, - super.y, { - required super.z, - }) : super.named(); + super.y, + ) : super.named(z: 1); } class C { @@ -61,7 +60,7 @@ '''); } - test_explicitSuperInvocation_matchingWithOffset_withNamedExplicit() async { + test_explicitSuperInvocation_matchingWithOffset_withNamedSuper() async { await assertNoDiagnostics(r''' class D extends C { final String w; @@ -69,8 +68,9 @@ D( this.w, super.x, - super.y, - ) : super.named(z: 1); + super.y, { + required super.z, + }) : super.named(); } class C {
diff --git a/pkg/linter/test/rules/no_adjacent_strings_in_list_test.dart b/pkg/linter/test/rules/no_adjacent_strings_in_list_test.dart index 4dab8c4..127fcd5 100644 --- a/pkg/linter/test/rules/no_adjacent_strings_in_list_test.dart +++ b/pkg/linter/test/rules/no_adjacent_strings_in_list_test.dart
@@ -17,18 +17,6 @@ @override String get lintRule => 'no_adjacent_strings_in_list'; - test_listLiteral() async { - await assertDiagnostics(r''' -var list = [ - 'a' - 'b', - 'c', -]; -''', [ - lint(15, 9), - ]); - } - test_adjacentStrings_three() async { await assertDiagnostics(r''' var list = [ @@ -41,38 +29,6 @@ ]); } - test_ifElement() async { - await assertDiagnostics(r''' -var list = [ - if (1 == 2) 'a' - 'b' -]; -''', [ - lint(27, 9), - ]); - } - - test_ifElementWithElse_inThen() async { - await assertNoDiagnostics(r''' -var list = [ - if (1 == 2) 'a' - 'b' - else 'c' -]; -'''); - } - - test_ifElementWithElse_inElse() async { - await assertDiagnostics(r''' -var list = [ - if (1 == 2) 'a' - else 'b' 'c' -]; -''', [ - lint(38, 7), - ]); - } - test_forElement() async { await assertDiagnostics(r''' var list = [ @@ -84,15 +40,47 @@ ]); } - test_setLiteral() async { + test_ifElement() async { await assertDiagnostics(r''' -var set = { +var list = [ + if (1 == 2) 'a' + 'b' +]; +''', [ + lint(27, 9), + ]); + } + + test_ifElementWithElse_inElse() async { + await assertDiagnostics(r''' +var list = [ + if (1 == 2) 'a' + else 'b' 'c' +]; +''', [ + lint(38, 7), + ]); + } + + test_ifElementWithElse_inThen() async { + await assertNoDiagnostics(r''' +var list = [ + if (1 == 2) 'a' + 'b' + else 'c' +]; +'''); + } + + test_listLiteral() async { + await assertDiagnostics(r''' +var list = [ 'a' 'b', 'c', -}; +]; ''', [ - lint(14, 9), + lint(15, 9), ]); } @@ -106,6 +94,18 @@ '''); } + test_setLiteral() async { + await assertDiagnostics(r''' +var set = { + 'a' + 'b', + 'c', +}; +''', [ + lint(14, 9), + ]); + } + test_setLiteral_plusOperator() async { await assertNoDiagnostics(r''' var set = {
diff --git a/pkg/linter/test/rules/no_self_assignments_test.dart b/pkg/linter/test/rules/no_self_assignments_test.dart index 255b734..88d94f8 100644 --- a/pkg/linter/test/rules/no_self_assignments_test.dart +++ b/pkg/linter/test/rules/no_self_assignments_test.dart
@@ -17,6 +17,32 @@ @override String get lintRule => 'no_self_assignments'; + test_classMemberAssignment() async { + await assertDiagnostics(r''' +class C { + static String foo = "foo"; +} + +void main() { + C.foo = C.foo; +} +''', [lint(58, 13)]); + } + + test_classMemberAssignmentUnrelated() async { + await assertNoDiagnostics(r''' +class C { + static String foo = "foo"; +} + +void main() { + String foo; + foo = C.foo; // OK + print(foo); +} +'''); + } + test_fieldAssignment() async { await assertDiagnostics(r''' class C { @@ -29,6 +55,18 @@ ''', [lint(41, 5)]); } + test_fieldAssignmentDifferentTargets() async { + await assertNoDiagnostics(r''' +class C { + String hello = 'ok'; +} + +void test(C one, C two) { + one.hello = two.hello; +} +'''); + } + test_fieldAssignmentDifferentVar() async { await assertNoDiagnostics(r''' class C { @@ -77,18 +115,6 @@ '''); } - test_fieldAssignmentDifferentTargets() async { - await assertNoDiagnostics(r''' -class C { - String hello = 'ok'; -} - -void test(C one, C two) { - one.hello = two.hello; -} -'''); - } - test_fieldInitialization() async { await assertNoDiagnostics(r''' class C { @@ -116,30 +142,4 @@ } ''', [lint(102, 5)]); } - - test_classMemberAssignment() async { - await assertDiagnostics(r''' -class C { - static String foo = "foo"; -} - -void main() { - C.foo = C.foo; -} -''', [lint(58, 13)]); - } - - test_classMemberAssignmentUnrelated() async { - await assertNoDiagnostics(r''' -class C { - static String foo = "foo"; -} - -void main() { - String foo; - foo = C.foo; // OK - print(foo); -} -'''); - } }
diff --git a/pkg/linter/test/rules/only_throw_errors_test.dart b/pkg/linter/test/rules/only_throw_errors_test.dart index 575c6d0..03671735 100644 --- a/pkg/linter/test/rules/only_throw_errors_test.dart +++ b/pkg/linter/test/rules/only_throw_errors_test.dart
@@ -91,6 +91,16 @@ ]); } + test_never() async { + await assertNoDiagnostics(r''' +void f() { + throw e(); +} + +Never e() => throw Exception(); +'''); + } + test_nullInPreNullSafe() async { await assertDiagnostics(r''' // @dart=2.9 @@ -121,14 +131,4 @@ lint(19, 7), ]); } - - test_never() async { - await assertNoDiagnostics(r''' -void f() { - throw e(); -} - -Never e() => throw Exception(); -'''); - } }
diff --git a/pkg/linter/test/rules/prefer_asserts_in_initializer_lists_test.dart b/pkg/linter/test/rules/prefer_asserts_in_initializer_lists_test.dart index 4c6ed7a..3ec25a4 100644 --- a/pkg/linter/test/rules/prefer_asserts_in_initializer_lists_test.dart +++ b/pkg/linter/test/rules/prefer_asserts_in_initializer_lists_test.dart
@@ -41,6 +41,40 @@ @override String get lintRule => 'prefer_asserts_in_initializer_lists'; + test_afterFirstStatement() async { + await assertNoDiagnostics(r''' +class A { + A.named(a) { + print(''); + assert(a != null); + } +} + +'''); + } + + test_firstStatement() async { + await assertDiagnostics(r''' +class A { + A.named(a) { + assert(a != null); + } +} + +''', [ + lint(29, 6), + ]); + } + + test_initializer() async { + await assertNoDiagnostics(r''' +class A { + A.named(a) : assert(a != null); +} + +'''); + } + test_nonBoolExpression() async { await assertDiagnostics(r''' class A { @@ -58,38 +92,4 @@ error(CompileTimeErrorCode.NON_BOOL_EXPRESSION, 40, 50), ]); } - - test_firstStatement() async { - await assertDiagnostics(r''' -class A { - A.named(a) { - assert(a != null); - } -} - -''', [ - lint(29, 6), - ]); - } - - test_afterFirstStatement() async { - await assertNoDiagnostics(r''' -class A { - A.named(a) { - print(''); - assert(a != null); - } -} - -'''); - } - - test_initializer() async { - await assertNoDiagnostics(r''' -class A { - A.named(a) : assert(a != null); -} - -'''); - } }
diff --git a/pkg/linter/test/rules/prefer_relative_imports_test.dart b/pkg/linter/test/rules/prefer_relative_imports_test.dart index 28b3d8c..ee48405 100644 --- a/pkg/linter/test/rules/prefer_relative_imports_test.dart +++ b/pkg/linter/test/rules/prefer_relative_imports_test.dart
@@ -15,10 +15,10 @@ @reflectiveTest class PreferRelativeImportsTest extends LintRuleTest { @override - String get lintRule => 'prefer_relative_imports'; + bool get addJsPackageDep => true; @override - bool get addJsPackageDep => true; + String get lintRule => 'prefer_relative_imports'; test_externalPackage() async { await assertNoDiagnostics(r''' @@ -27,6 +27,23 @@ '''); } + test_internalPackage() async { + var packageConfigBuilder = PackageConfigFileBuilder(); + packageConfigBuilder.add( + name: 'internal_package', + rootPath: '$testPackageRootPath/vendor/internal_package', + ); + writeTestPackageConfig(packageConfigBuilder); + + newFile('$testPackageRootPath/vendor/internal_package/lib/lib.dart', r''' +class C {} +'''); + await assertNoDiagnostics(r''' +/// This provides [C]. +import 'package:internal_package/lib.dart'; +'''); + } + test_samePackage_packageSchema() async { newFile('$testPackageLibPath/lib.dart', r''' class C {} @@ -61,21 +78,4 @@ import 'lib.dart'; '''); } - - test_internalPackage() async { - var packageConfigBuilder = PackageConfigFileBuilder(); - packageConfigBuilder.add( - name: 'internal_package', - rootPath: '$testPackageRootPath/vendor/internal_package', - ); - writeTestPackageConfig(packageConfigBuilder); - - newFile('$testPackageRootPath/vendor/internal_package/lib/lib.dart', r''' -class C {} -'''); - await assertNoDiagnostics(r''' -/// This provides [C]. -import 'package:internal_package/lib.dart'; -'''); - } }
diff --git a/pkg/linter/test/rules/provide_deprecation_message_test.dart b/pkg/linter/test/rules/provide_deprecation_message_test.dart index fad8189..7e38eeb 100644 --- a/pkg/linter/test/rules/provide_deprecation_message_test.dart +++ b/pkg/linter/test/rules/provide_deprecation_message_test.dart
@@ -17,6 +17,13 @@ @override String get lintRule => 'provide_deprecation_message'; + test_withMessage() async { + await assertNoDiagnostics(r''' +@Deprecated("Text.") +class C {} +'''); + } + test_withoutMessage() async { await assertDiagnostics(r''' @deprecated @@ -25,11 +32,4 @@ lint(0, 11), ]); } - - test_withMessage() async { - await assertNoDiagnostics(r''' -@Deprecated("Text.") -class C {} -'''); - } }
diff --git a/pkg/linter/test/rules/sort_pub_dependencies_test.dart b/pkg/linter/test/rules/sort_pub_dependencies_test.dart index fef6462..670e8d2 100644 --- a/pkg/linter/test/rules/sort_pub_dependencies_test.dart +++ b/pkg/linter/test/rules/sort_pub_dependencies_test.dart
@@ -20,19 +20,6 @@ @override String get lintRule => 'sort_pub_dependencies'; - test_dependencies_sorted() async { - await assertNoPubspecDiagnostics(r''' -name: fancy -version: 1.1.1 - -dependencies: - aaa: ^0.1.1 - bbb: ^0.15.8 - flutter: - sdk: flutter -'''); - } - test_dependencies_duplicates() async { await assertNoPubspecDiagnostics(r''' name: fancy @@ -47,6 +34,19 @@ '''); } + test_dependencies_sorted() async { + await assertNoPubspecDiagnostics(r''' +name: fancy +version: 1.1.1 + +dependencies: + aaa: ^0.1.1 + bbb: ^0.15.8 + flutter: + sdk: flutter +'''); + } + test_dependencies_unsorted() async { await assertPubspecDiagnostics(r''' name: fancy
diff --git a/pkg/linter/test/rules/test_types_in_equals_test.dart b/pkg/linter/test/rules/test_types_in_equals_test.dart index 72c8625..2e77222 100644 --- a/pkg/linter/test/rules/test_types_in_equals_test.dart +++ b/pkg/linter/test/rules/test_types_in_equals_test.dart
@@ -17,22 +17,6 @@ @override String get lintRule => 'test_types_in_equals'; - test_usesIs() async { - await assertNoDiagnostics(r''' -class C { - int? x; - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - return other is C && this.x == other.x; - } -} -'''); - } - test_doesNotUseIs() async { await assertDiagnostics(r''' class C { @@ -48,4 +32,20 @@ lint(83, 10), ]); } + + test_usesIs() async { + await assertNoDiagnostics(r''' +class C { + int? x; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + return other is C && this.x == other.x; + } +} +'''); + } }
diff --git a/pkg/linter/test/rules/unnecessary_lambdas_test.dart b/pkg/linter/test/rules/unnecessary_lambdas_test.dart index a86a4f1..276c69d 100644 --- a/pkg/linter/test/rules/unnecessary_lambdas_test.dart +++ b/pkg/linter/test/rules/unnecessary_lambdas_test.dart
@@ -17,6 +17,12 @@ @override String get lintRule => 'unnecessary_lambdas'; + test_asyncLambda() async { + await assertNoDiagnostics(r''' +var x = [].forEach((x) async => print(x)); +'''); + } + test_constructorCall_const() async { await assertNoDiagnostics(r''' class C { @@ -55,13 +61,6 @@ ]); } - test_functionCall_explicitTypeArg() async { - await assertNoDiagnostics(r''' -void f<T>() {} -var x = [() => f<int>()]; -'''); - } - test_constructorCall_noParameter_oneArg() async { await assertNoDiagnostics(r''' class C { @@ -80,6 +79,19 @@ '''); } + test_emptyLambda() async { + await assertNoDiagnostics(r''' +var f = () {}; +'''); + } + + test_functionCall_explicitTypeArg() async { + await assertNoDiagnostics(r''' +void f<T>() {} +var x = [() => f<int>()]; +'''); + } + test_functionCall_matchingArg() async { await assertDiagnostics(r''' var x = [].forEach((x) => print(x)); @@ -88,10 +100,15 @@ ]); } - test_asyncLambda() async { - await assertNoDiagnostics(r''' -var x = [].forEach((x) async => print(x)); -'''); + test_functionCall_singleStatement() async { + await assertDiagnostics(r''' +final f = () {}; +final l = () { + f(); +}; +''', [ + lint(27, 13), + ]); } test_functionTearoff() async { @@ -145,6 +162,22 @@ '''); } + test_methodCallOnFinalLocal_closureParameterIsPartOfTarget() async { + await assertNoDiagnostics(r''' +void f() { + [].where((e) => (e + e).contains(e)); +} +'''); + } + + test_methodCallOnFinalLocal_closureParameterIsTarget() async { + await assertNoDiagnostics(r''' +void f() { + [].where((e) => e.contains(e)); +} +'''); + } + test_methodCallOnFinalLocal_matchingArg() async { await assertDiagnostics(r''' void f() { @@ -166,28 +199,6 @@ '''); } - test_methodCallOnFinalLocal_closureParameterIsTarget() async { - await assertNoDiagnostics(r''' -void f() { - [].where((e) => e.contains(e)); -} -'''); - } - - test_emptyLambda() async { - await assertNoDiagnostics(r''' -var f = () {}; -'''); - } - - test_methodCallOnFinalLocal_closureParameterIsPartOfTarget() async { - await assertNoDiagnostics(r''' -void f() { - [].where((e) => (e + e).contains(e)); -} -'''); - } - test_methodCallOnNonFinalLocal_matchingArg() async { await assertNoDiagnostics(r''' void f() { @@ -206,15 +217,6 @@ '''); } - test_nonFunctionCall() async { - await assertNoDiagnostics(r''' -void f() { - final l = []; - [].where((e) => l.contains(e) || e is int); -} -'''); - } - test_multipleStatements() async { await assertNoDiagnostics(r''' void f() { @@ -226,15 +228,13 @@ '''); } - test_functionCall_singleStatement() async { - await assertDiagnostics(r''' -final f = () {}; -final l = () { - f(); -}; -''', [ - lint(27, 13), - ]); + test_nonFunctionCall() async { + await assertNoDiagnostics(r''' +void f() { + final l = []; + [].where((e) => l.contains(e) || e is int); +} +'''); } test_noParameter_targetIsFinalField() async {
diff --git a/pkg/linter/test/rules/unnecessary_string_escapes_test.dart b/pkg/linter/test/rules/unnecessary_string_escapes_test.dart index 1b75185..f2b5b1d 100644 --- a/pkg/linter/test/rules/unnecessary_string_escapes_test.dart +++ b/pkg/linter/test/rules/unnecessary_string_escapes_test.dart
@@ -51,6 +51,12 @@ '''); } + test_escapedDoubleQuotes_inThree_inThreeDoubleQuotes() async { + await assertNoDiagnostics(r''' +var x = """text"\""text"""; +'''); + } + test_escapedDoubleQuotes_inThreeDoubleQuotes() async { await assertDiagnostics(r''' var x = """\"text"""; @@ -125,6 +131,12 @@ '''); } + test_escapedSingleQuote_inThree_inThreeSingleQuotes() async { + await assertNoDiagnostics(r""" +var x = '''text'\''text'''; +"""); + } + test_escapedSingleQuote_inThreeDoubleQuotes() async { await assertDiagnostics(r''' var x = """\'"""; @@ -141,42 +153,6 @@ ]); } - test_threeEscapedSingleQuote_inThreeSingleQuotes() async { - await assertNoDiagnostics(r""" -var x = '''text\'\'\'text'''; -"""); - } - - test_moreThanThreeEscapedSingleQuote_inThreeSingleQuotes() async { - await assertNoDiagnostics(r""" -var x = '''text\'\'\'\'\'\'text'''; -"""); - } - - test_escapedSingleQuote_inThree_inThreeSingleQuotes() async { - await assertNoDiagnostics(r""" -var x = '''text'\''text'''; -"""); - } - - test_threeEscapedDoubleQuote_inThreeDoubleQuotes() async { - await assertNoDiagnostics(r''' -var x = """text\"\"\"text"""; -'''); - } - - test_moreThanThreeEscapedDoubleQuotes_inThreeDoubleQuotes() async { - await assertNoDiagnostics(r''' -var x = """text\"\"\"\"\"\"text"""; -'''); - } - - test_escapedDoubleQuotes_inThree_inThreeDoubleQuotes() async { - await assertNoDiagnostics(r''' -var x = """text"\""text"""; -'''); - } - test_escapedSingleQuote_inThreeSingleQuotes_atEnd() async { await assertNoDiagnostics(r""" var x = '''text\''''; @@ -195,6 +171,30 @@ '''); } + test_moreThanThreeEscapedDoubleQuotes_inThreeDoubleQuotes() async { + await assertNoDiagnostics(r''' +var x = """text\"\"\"\"\"\"text"""; +'''); + } + + test_moreThanThreeEscapedSingleQuote_inThreeSingleQuotes() async { + await assertNoDiagnostics(r""" +var x = '''text\'\'\'\'\'\'text'''; +"""); + } + + test_threeEscapedDoubleQuote_inThreeDoubleQuotes() async { + await assertNoDiagnostics(r''' +var x = """text\"\"\"text"""; +'''); + } + + test_threeEscapedSingleQuote_inThreeSingleQuotes() async { + await assertNoDiagnostics(r""" +var x = '''text\'\'\'text'''; +"""); + } + test_unterminatedStringLiteral() async { // Note that putting `''` on the new line is important to get a token // with `'\` with no closing quote.
diff --git a/pkg/linter/test/rules/use_setters_to_change_properties_test.dart b/pkg/linter/test/rules/use_setters_to_change_properties_test.dart index e3d92d7..9f0cc6f 100644 --- a/pkg/linter/test/rules/use_setters_to_change_properties_test.dart +++ b/pkg/linter/test/rules/use_setters_to_change_properties_test.dart
@@ -19,28 +19,12 @@ @override String get lintRule => 'use_setters_to_change_properties'; - test_setterLike_expressionBody() async { - await assertDiagnostics(r''' + test_abstract() async { + await assertNoDiagnostics(r''' abstract class A { - int x = 0; - void setX(int x) => this.x = x; + void setX(int x); } -''', [ - lint(39, 4), - ]); - } - - test_setterLike_blockBody() async { - await assertDiagnostics(r''' -abstract class A { - int x = 0; - void setX(int x) { - this.x = x; - } -} -''', [ - lint(39, 4), - ]); +'''); } test_combo() async { @@ -52,6 +36,22 @@ '''); } + test_extension() async { + await assertDiagnostics(r''' +class A { + int x = 0; +} + +extension E on A { + void setX(int x) { + this.x = x; + } +} +''', [ + lint(52, 4), + ]); + } + test_inheritedFromSuperclass() async { await assertNoDiagnostics(r''' abstract class A { @@ -68,14 +68,6 @@ '''); } - test_abstract() async { - await assertNoDiagnostics(r''' -abstract class A { - void setX(int x); -} -'''); - } - test_inheritedFromSuperInterface() async { await assertNoDiagnostics(r''' abstract class A { @@ -92,19 +84,27 @@ '''); } - test_extension() async { + test_setterLike_blockBody() async { await assertDiagnostics(r''' -class A { +abstract class A { int x = 0; -} - -extension E on A { void setX(int x) { this.x = x; } } ''', [ - lint(52, 4), + lint(39, 4), + ]); + } + + test_setterLike_expressionBody() async { + await assertDiagnostics(r''' +abstract class A { + int x = 0; + void setX(int x) => this.x = x; +} +''', [ + lint(39, 4), ]); } }
diff --git a/pkg/linter/test/rules/use_string_in_part_of_directives.dart b/pkg/linter/test/rules/use_string_in_part_of_directives.dart index 3ddca8a..b1f2290 100644 --- a/pkg/linter/test/rules/use_string_in_part_of_directives.dart +++ b/pkg/linter/test/rules/use_string_in_part_of_directives.dart
@@ -20,15 +20,6 @@ @override String get lintRule => 'use_string_in_part_of_directives'; - test_part_of_with_string() async { - newFile2('$testPackageRootPath/lib/lib.dart', ''' -part '$testFileName'; -'''); - await assertNoDiagnostics(r''' -part of 'lib.dart'; -'''); - } - test_part_of_with_library_name() async { newFile2('$testPackageRootPath/lib/lib.dart', ''' library lib; @@ -43,4 +34,13 @@ ], ); } + + test_part_of_with_string() async { + newFile2('$testPackageRootPath/lib/lib.dart', ''' +part '$testFileName'; +'''); + await assertNoDiagnostics(r''' +part of 'lib.dart'; +'''); + } }
diff --git a/pkg/linter/tool/legacy_test_move.dart b/pkg/linter/tool/legacy_test_move.dart index d936389..680bd21 100644 --- a/pkg/linter/tool/legacy_test_move.dart +++ b/pkg/linter/tool/legacy_test_move.dart
@@ -23,52 +23,30 @@ LegacyTestMover(this.ruleName); - void move() { - var legacyTest = File('test_data/rules/$ruleName.dart'); - if (!legacyTest.existsSync()) { - print('Did not find ${legacyTest.path}!!'); - return; - } + String get newTestFileHeader { + var ruleNameCamelCase = ruleName.splitMapJoin(RegExp('(?:^|_)([a-z])'), + onMatch: (m) => m[1]!.toUpperCase()); + return ''' +// Copyright (c) 2023, 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. - var newTest = File('test/rules/${ruleName}_test.dart'); - var newTestExists = newTest.existsSync(); +import 'package:test_reflective_loader/test_reflective_loader.dart'; - var legacyLines = legacyTest.readAsLinesSync(); +import '../rule_test_support.dart'; - var snippets = gatherSnippets(legacyLines); +main() { + defineReflectiveSuite(() { + defineReflectiveTests(${ruleNameCamelCase}Test); + }); +} - // [snippets] is now our complete list of snippets. +@reflectiveTest +class ${ruleNameCamelCase}Test extends LintRuleTest { + @override + String get lintRule => '$ruleName'; - var buffer = StringBuffer(); - if (newTestExists) { - var existingNewTestLines = newTest.readAsLinesSync(); - var classCloseIndex = existingNewTestLines - .lastIndexWhere((element) => element.startsWith(RegExp('^}'))); - existingNewTestLines.take(classCloseIndex).forEach(buffer.writeln); - buffer.writeln(); - } else { - buffer.write(newTestFileHeader); - } - for (var testIndex = 0; testIndex < snippets.length; testIndex++) { - var snippet = snippets[testIndex]; - buffer.writeln(testBody(testIndex.toString(), snippet)); - buffer.writeln(); - } - buffer.writeln('}'); - newTest.writeAsStringSync(buffer.toString()); - - if (!newTestExists) { - updateAllTestFile(); - } - - print(''' -You probably want to run these: - - dart format test/rules/all.dart - git rm ${legacyTest.path} - git add test - dart ${newTest.path} -'''); +'''; } List<String> gatherSnippets(List<String> legacyLines) { @@ -139,30 +117,52 @@ return ranges; } - String get newTestFileHeader { - var ruleNameCamelCase = ruleName.splitMapJoin(RegExp('(?:^|_)([a-z])'), - onMatch: (m) => m[1]!.toUpperCase()); - return ''' -// Copyright (c) 2023, 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. + void move() { + var legacyTest = File('test_data/rules/$ruleName.dart'); + if (!legacyTest.existsSync()) { + print('Did not find ${legacyTest.path}!!'); + return; + } -import 'package:test_reflective_loader/test_reflective_loader.dart'; + var newTest = File('test/rules/${ruleName}_test.dart'); + var newTestExists = newTest.existsSync(); -import '../rule_test_support.dart'; + var legacyLines = legacyTest.readAsLinesSync(); -main() { - defineReflectiveSuite(() { - defineReflectiveTests(${ruleNameCamelCase}Test); - }); -} + var snippets = gatherSnippets(legacyLines); -@reflectiveTest -class ${ruleNameCamelCase}Test extends LintRuleTest { - @override - String get lintRule => '$ruleName'; + // [snippets] is now our complete list of snippets. -'''; + var buffer = StringBuffer(); + if (newTestExists) { + var existingNewTestLines = newTest.readAsLinesSync(); + var classCloseIndex = existingNewTestLines + .lastIndexWhere((element) => element.startsWith(RegExp('^}'))); + existingNewTestLines.take(classCloseIndex).forEach(buffer.writeln); + buffer.writeln(); + } else { + buffer.write(newTestFileHeader); + } + for (var testIndex = 0; testIndex < snippets.length; testIndex++) { + var snippet = snippets[testIndex]; + buffer.writeln(testBody(testIndex.toString(), snippet)); + buffer.writeln(); + } + buffer.writeln('}'); + newTest.writeAsStringSync(buffer.toString()); + + if (!newTestExists) { + updateAllTestFile(); + } + + print(''' +You probably want to run these: + + dart format test/rules/all.dart + git rm ${legacyTest.path} + git add test + dart ${newTest.path} +'''); } String testBody(String name, String code) => code.contains('LINT')