Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
static const lintName = 'avoid_unrelated_type_assertions';
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor<void> {
final _expressions = <IsExpression, bool>{};
/// Visitor for [AvoidUnrelatedTypeAssertionsRule].
class AvoidUnrelatedTypeAssertionsVisitor extends SimpleAstVisitor<void> {
/// The rule associated with this visitor.
final AvoidUnrelatedTypeAssertionsRule _rule;

/// Map of unrelated type checks and their results
Map<IsExpression, bool> get expressions => _expressions;
/// Creates an instance of [AvoidUnrelatedTypeAssertionsVisitor].
AvoidUnrelatedTypeAssertionsVisitor(this._rule);

@override
void visitIsExpression(IsExpression node) {
Expand All @@ -47,7 +48,10 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor<void> {
final objectType = node.expression.staticType;

if (_isUnrelatedTypeCheck(objectType, castedType)) {
_expressions[node] = node.notOperator != null;
_rule.reportAtNode(
node,
arguments: [if (node.notOperator != null) 'true' else 'false'],
);
}
}

Expand All @@ -64,10 +68,14 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor<void> {
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;
}
Expand All @@ -94,8 +102,8 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor<void> {

final correctObjectType =
objectType is InterfaceType && objectType.isDartAsyncFutureOr
? objectType.typeArguments.first
: objectType;
? objectType.typeArguments.first
: objectType;

if ((correctObjectType.element == castedType.element) ||
castedType is DynamicType ||
Expand All @@ -105,8 +113,9 @@ class AvoidUnrelatedTypeAssertionsVisitor extends RecursiveAstVisitor<void> {
}

if (correctObjectType is InterfaceType) {
return correctObjectType.allSupertypes
.firstWhereOrNull((value) => value.element == castedType.element);
return correctObjectType.allSupertypes.firstWhereOrNull(
(value) => value.element == castedType.element,
);
}

return null;
Expand Down
49 changes: 0 additions & 49 deletions lint_test/avoid_unrelated_type_assertions_test.dart

This file was deleted.

2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 => rule.name;

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<String>;
}
''',
[
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']),
],
);
}
}
Loading