From a61a911a70e175b332be66a0cc453644ee1d1eae Mon Sep 17 00:00:00 2001 From: daria-trusca-solid Date: Wed, 3 Jun 2026 16:32:30 +0300 Subject: [PATCH 1/5] Updated avoid_unrelated_type_assertions and its visitor to use AnalysisRule. --- .../avoid_unrelated_type_assertions_rule.dart | 65 ++++++++----------- ...oid_unrelated_type_assertions_visitor.dart | 16 +++-- 2 files changed, 38 insertions(+), 43 deletions(-) diff --git a/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart b/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart index 0b63cc37..75b73157 100644 --- a/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart +++ b/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart @@ -1,50 +1,41 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:analyzer/analysis_rule/analysis_rule.dart'; +import 'package:analyzer/analysis_rule/rule_context.dart'; +import 'package:analyzer/analysis_rule/rule_visitor_registry.dart'; +import 'package:analyzer/error/error.dart'; import 'package:solid_lints/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart'; -import 'package:solid_lints/src/models/rule_config.dart'; -import 'package:solid_lints/src/models/solid_lint_rule.dart'; /// A `avoid_unrelated_type_assertions` rule which /// warns about unnecessary usage of `as` operator -class AvoidUnrelatedTypeAssertionsRule extends SolidLintRule { - /// This lint rule represents - /// the error whether we use bad formatted double literals. +class AvoidUnrelatedTypeAssertionsRule extends AnalysisRule { + /// The name of the lint rule. static const lintName = 'avoid_unrelated_type_assertions'; - AvoidUnrelatedTypeAssertionsRule._(super.config); + /// The message shown when the lint rule is triggered. + static const lintMessage = + 'Avoid unrelated "is" assertion. The result is always "{0}".'; - /// Creates a new instance of [AvoidUnrelatedTypeAssertionsRule] - /// based on the lint configuration. - factory AvoidUnrelatedTypeAssertionsRule.createRule( - CustomLintConfigs configs, - ) { - final rule = RuleConfig( - configs: configs, - name: lintName, - problemMessage: (_) => - 'Avoid unrelated "is" assertion. The result is always "{0}".', - ); + /// Lint code for this rule. + static const LintCode _code = LintCode( + lintName, + lintMessage, + ); - return AvoidUnrelatedTypeAssertionsRule._(rule); - } + /// Creates a new instance of [AvoidUnrelatedTypeAssertionsRule]. + AvoidUnrelatedTypeAssertionsRule() + : super( + name: lintName, + description: lintMessage, + ); @override - void run( - CustomLintResolver resolver, - DiagnosticReporter reporter, - CustomLintContext context, - ) { - context.registry.addIsExpression((node) { - final visitor = AvoidUnrelatedTypeAssertionsVisitor(); - visitor.visitIsExpression(node); + LintCode get diagnosticCode => _code; - for (final element in visitor.expressions.entries) { - reporter.atNode( - element.key, - code, - arguments: [element.value.toString()], - ); - } - }); + @override + void registerNodeProcessors( + RuleVisitorRegistry registry, + RuleContext context, + ) { + final visitor = AvoidUnrelatedTypeAssertionsVisitor(this); + registry.addIsExpression(this, visitor); } } diff --git a/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart b/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart index e0b4aa6a..f1a796c5 100644 --- a/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart +++ b/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart @@ -26,14 +26,15 @@ import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:collection/collection.dart'; +import 'package:solid_lints/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart'; -/// AST Visitor which finds all is expressions and checks if they are -/// unrelated (result always false) +/// Visitor for [AvoidUnrelatedTypeAssertionsRule]. class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor { - final _expressions = {}; + /// The rule associated with this visitor. + final AvoidUnrelatedTypeAssertionsRule rule; - /// Map of unrelated type checks and their results - Map get expressions => _expressions; + /// Creates an instance of [AvoidUnrelatedTypeAssertionsVisitor]. + AvoidUnrelatedTypeAssertionsVisitor(this.rule); @override void visitIsExpression(IsExpression node) { @@ -47,7 +48,10 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor { final objectType = node.expression.staticType; if (_isUnrelatedTypeCheck(objectType, castedType)) { - _expressions[node] = node.notOperator != null; + rule.reportAtNode( + node, + arguments: [if (node.notOperator != null) 'false' else 'true'], + ); } } From c5862e410d8ee5c5c75fdba55a393d74979c628c Mon Sep 17 00:00:00 2001 From: daria-trusca-solid Date: Thu, 4 Jun 2026 10:51:16 +0300 Subject: [PATCH 2/5] Updated tests for avoid_unrelated_type_assertions rule --- ...oid_unrelated_type_assertions_visitor.dart | 2 +- ...d_unrelated_type_assertions_rule_test.dart | 148 ++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 test/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule_test.dart diff --git a/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart b/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart index f1a796c5..9ea62943 100644 --- a/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart +++ b/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart @@ -50,7 +50,7 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor { if (_isUnrelatedTypeCheck(objectType, castedType)) { rule.reportAtNode( node, - arguments: [if (node.notOperator != null) 'false' else 'true'], + arguments: [if (node.notOperator != null) 'true' else 'false'], ); } } diff --git a/test/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule_test.dart b/test/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule_test.dart new file mode 100644 index 00000000..10880115 --- /dev/null +++ b/test/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule_test.dart @@ -0,0 +1,148 @@ +import 'package:analyzer_testing/analysis_rule/analysis_rule.dart'; +import 'package:solid_lints/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(AvoidUnrelatedTypeAssertionsRuleTest); + }); +} + +@reflectiveTest +class AvoidUnrelatedTypeAssertionsRuleTest extends AnalysisRuleTest { + @override + void setUp() { + rule = AvoidUnrelatedTypeAssertionsRule(); + super.setUp(); + } + + @override + String get analysisRule => AvoidUnrelatedTypeAssertionsRule.lintName; + + void test_reports_unrelated_string_is_int() async { + await assertDiagnostics( + r''' +void fun() { + final testString = ''; + + final result = testString is int; +} +''', + [ + lint(56, 17, messageContainsAll: ['false']), + ], + ); + } + + void test_reports_unrelated_int_list_is_string_list() async { + await assertDiagnostics( + r''' +void fun() { + final testList = [1, 2, 3]; + + final result = testList is List; +} +''', + [ + lint(61, 24, messageContainsAll: ['false']), + ], + ); + } + + void test_reports_unrelated_string_map_is_double_map() async { + await assertDiagnostics( + r''' +void fun() { + final testMap = {'A': 'B'}; + + final result = testMap['A'] is double; +} +''', + [ + lint(61, 22, messageContainsAll: ['false']), + ], + ); + } + + void test_reports_unrelated_class_is_another_class() async { + await assertDiagnostics( + r''' +class Foo {} + +class Bar {} + +void fun() { + final Foo foo = Foo(); + + final result = foo is Bar; +} +''', + [ + lint(84, 10, messageContainsAll: ['false']), + ], + ); + } + + void test_reports_unrelated_child_class_is_another_class() async { + await assertDiagnostics( + r''' +class Foo {} + +class Bar {} + +class ChildFoo extends Foo {} + +void fun() { + final childFoo = ChildFoo(); + + final result = childFoo is Bar; +} +''', + [ + lint(121, 15, messageContainsAll: ['false']), + ], + ); + } + + void test_reports_unrelated_is_condition() async { + await assertDiagnostics( + r''' +class _A {} + +class _B extends _A {} + +class _C {} + +void lint() { + final _A a = _B(); + + if (a is _C) return; +} +''', + [ + lint(92, 7, messageContainsAll: ['false']), + ], + ); + } + + void test_reports_unrelated_is_not_condition() async { + await assertDiagnostics( + r''' +class _A {} + +class _B extends _A {} + +class _C {} + +void lint() { + final _A a = _B(); + + if (a is! _C) return; +} +''', + [ + lint(92, 8, messageContainsAll: ['true']), + ], + ); + } +} From 9e8679e46c2c07ccb382ff0f0eded5e684f81586 Mon Sep 17 00:00:00 2001 From: daria-trusca-solid Date: Thu, 4 Jun 2026 11:01:32 +0300 Subject: [PATCH 3/5] Delete previous test file for avoid_unrelated_type_assertions --- .../avoid_unrelated_type_assertions_test.dart | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 lint_test/avoid_unrelated_type_assertions_test.dart diff --git a/lint_test/avoid_unrelated_type_assertions_test.dart b/lint_test/avoid_unrelated_type_assertions_test.dart deleted file mode 100644 index 209a5615..00000000 --- a/lint_test/avoid_unrelated_type_assertions_test.dart +++ /dev/null @@ -1,49 +0,0 @@ -// ignore_for_file: prefer_const_declarations, prefer_match_file_name, unused_element -// ignore_for_file: unnecessary_nullable_for_final_variable_declarations -// ignore_for_file: unused_local_variable - -/// Check the `avoid_unrelated_type_assertions` rule -class Foo {} - -class Bar {} - -class ChildFoo extends Foo {} - -void fun() { - final testString = ''; - final testList = [1, 2, 3]; - final testMap = {'A': 'B'}; - final Foo foo = Foo(); - final childFoo = ChildFoo(); - - // expect_lint: avoid_unrelated_type_assertions - final result = testString is int; - - // expect_lint: avoid_unrelated_type_assertions - final result2 = testList is List; - - // expect_lint: avoid_unrelated_type_assertions - final result3 = foo is Bar; - - // expect_lint: avoid_unrelated_type_assertions - final result4 = childFoo is Bar; - - // expect_lint: avoid_unrelated_type_assertions - final result5 = testMap['A'] is double; -} - -class _A {} - -class _B extends _A {} - -class _C {} - -void lint() { - final _A a = _B(); - // Always false - // expect_lint: avoid_unrelated_type_assertions - if (a is _C) return; - // Always true - // expect_lint: avoid_unrelated_type_assertions - if (a is! _C) return; -} From 37c80853ecd93412c5bb54e1d29ed2fa610d6468 Mon Sep 17 00:00:00 2001 From: daria-trusca-solid Date: Thu, 4 Jun 2026 11:09:01 +0300 Subject: [PATCH 4/5] Update variable modifiers to private --- .../avoid_unrelated_type_assertions_rule.dart | 16 +++++------ ...oid_unrelated_type_assertions_visitor.dart | 27 +++++++++++-------- pubspec.yaml | 2 +- ...d_unrelated_type_assertions_rule_test.dart | 2 +- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart b/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart index 75b73157..6bb9c2d3 100644 --- a/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart +++ b/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart @@ -8,24 +8,24 @@ import 'package:solid_lints/src/lints/avoid_unrelated_type_assertions/visitors/a /// warns about unnecessary usage of `as` operator class AvoidUnrelatedTypeAssertionsRule extends AnalysisRule { /// The name of the lint rule. - static const lintName = 'avoid_unrelated_type_assertions'; + static const _lintName = 'avoid_unrelated_type_assertions'; /// The message shown when the lint rule is triggered. - static const lintMessage = + static const _lintMessage = 'Avoid unrelated "is" assertion. The result is always "{0}".'; /// Lint code for this rule. static const LintCode _code = LintCode( - lintName, - lintMessage, + _lintName, + _lintMessage, ); /// Creates a new instance of [AvoidUnrelatedTypeAssertionsRule]. AvoidUnrelatedTypeAssertionsRule() - : super( - name: lintName, - description: lintMessage, - ); + : super( + name: _lintName, + description: _lintMessage, + ); @override LintCode get diagnosticCode => _code; diff --git a/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart b/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart index 9ea62943..c5d40bcd 100644 --- a/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart +++ b/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart @@ -31,10 +31,10 @@ import 'package:solid_lints/src/lints/avoid_unrelated_type_assertions/avoid_unre /// Visitor for [AvoidUnrelatedTypeAssertionsRule]. class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor { /// The rule associated with this visitor. - final AvoidUnrelatedTypeAssertionsRule rule; + final AvoidUnrelatedTypeAssertionsRule _rule; /// Creates an instance of [AvoidUnrelatedTypeAssertionsVisitor]. - AvoidUnrelatedTypeAssertionsVisitor(this.rule); + AvoidUnrelatedTypeAssertionsVisitor(this._rule); @override void visitIsExpression(IsExpression node) { @@ -48,7 +48,7 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor { final objectType = node.expression.staticType; if (_isUnrelatedTypeCheck(objectType, castedType)) { - rule.reportAtNode( + _rule.reportAtNode( node, arguments: [if (node.notOperator != null) 'true' else 'false'], ); @@ -68,10 +68,14 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor { return false; } - final objectCastedType = - _foundCastedTypeInObjectTypeHierarchy(objectType, castedType); - final castedObjectType = - _foundCastedTypeInObjectTypeHierarchy(castedType, objectType); + final objectCastedType = _foundCastedTypeInObjectTypeHierarchy( + objectType, + castedType, + ); + final castedObjectType = _foundCastedTypeInObjectTypeHierarchy( + castedType, + objectType, + ); if (objectCastedType == null && castedObjectType == null) { return true; } @@ -98,8 +102,8 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor { final correctObjectType = objectType is InterfaceType && objectType.isDartAsyncFutureOr - ? objectType.typeArguments.first - : objectType; + ? objectType.typeArguments.first + : objectType; if ((correctObjectType.element == castedType.element) || castedType is DynamicType || @@ -109,8 +113,9 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor { } if (correctObjectType is InterfaceType) { - return correctObjectType.allSupertypes - .firstWhereOrNull((value) => value.element == castedType.element); + return correctObjectType.allSupertypes.firstWhereOrNull( + (value) => value.element == castedType.element, + ); } return null; diff --git a/pubspec.yaml b/pubspec.yaml index f85b1309..70168359 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ documentation: https://solid-software.github.io/solid_lints/docs/intro topics: [lints, linter, lint, analysis, analyzer] environment: - sdk: ">=3.5.0 <4.0.0" + sdk: ">=3.9.0 <4.0.0" dependencies: analyzer: ^10.0.1 diff --git a/test/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule_test.dart b/test/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule_test.dart index 10880115..a2260724 100644 --- a/test/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule_test.dart +++ b/test/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule_test.dart @@ -17,7 +17,7 @@ class AvoidUnrelatedTypeAssertionsRuleTest extends AnalysisRuleTest { } @override - String get analysisRule => AvoidUnrelatedTypeAssertionsRule.lintName; + String get analysisRule => rule.name; void test_reports_unrelated_string_is_int() async { await assertDiagnostics( From b14e54843e8c33b089498e2022e552434b1ba9cc Mon Sep 17 00:00:00 2001 From: daria-trusca-solid Date: Thu, 4 Jun 2026 11:50:22 +0300 Subject: [PATCH 5/5] Updated avoid_unrelared_type_assertions visitor from recursive to simple --- .../visitors/avoid_unrelated_type_assertions_visitor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart b/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart index c5d40bcd..2f0e33e3 100644 --- a/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart +++ b/lib/src/lints/avoid_unrelated_type_assertions/visitors/avoid_unrelated_type_assertions_visitor.dart @@ -29,7 +29,7 @@ import 'package:collection/collection.dart'; import 'package:solid_lints/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart'; /// Visitor for [AvoidUnrelatedTypeAssertionsRule]. -class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor { +class AvoidUnrelatedTypeAssertionsVisitor extends SimpleAstVisitor { /// The rule associated with this visitor. final AvoidUnrelatedTypeAssertionsRule _rule;