diff --git a/lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart b/lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart index 5110bbf7..492749c3 100644 --- a/lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart +++ b/lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart @@ -1,10 +1,10 @@ +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/dart/ast/ast.dart'; -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; -import 'package:solid_lints/src/lints/avoid_late_keyword/models/avoid_late_keyword_parameters.dart'; -import 'package:solid_lints/src/models/rule_config.dart'; -import 'package:solid_lints/src/models/solid_lint_rule.dart'; -import 'package:solid_lints/src/utils/types_utils.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:analyzer/error/error.dart'; +import 'package:solid_lints/src/lints/avoid_late_keyword/visitors/avoid_late_keyword_visitor.dart'; /// Avoid `late` keyword /// @@ -47,63 +47,29 @@ import 'package:solid_lints/src/utils/types_utils.dart'; /// } /// } /// ``` -class AvoidLateKeywordRule extends SolidLintRule { - /// This lint rule represents - /// the error whether we use `late` keyword. - static const lintName = 'avoid_late_keyword'; +class AvoidLateKeywordRule extends AnalysisRule { + static const String _lintName = 'avoid_late_keyword'; - AvoidLateKeywordRule._(super.config); + static const LintCode _code = LintCode( + _lintName, + 'Avoid using the "late" keyword. It may result in runtime exceptions.', + ); - /// Creates a new instance of [AvoidLateKeywordRule] - /// based on the lint configuration. - factory AvoidLateKeywordRule.createRule(CustomLintConfigs configs) { - final rule = RuleConfig( - configs: configs, - name: lintName, - paramsParser: AvoidLateKeywordParameters.fromJson, - problemMessage: (_) => 'Avoid using the "late" keyword. ' - 'It may result in runtime exceptions.', - ); + /// Creates an instance of [AvoidLateKeywordRule]. + AvoidLateKeywordRule() + : super( + name: _lintName, + description: 'Warns against using the late keyword.', + ); - return AvoidLateKeywordRule._(rule); - } + @override + LintCode get diagnosticCode => _code; @override - void run( - CustomLintResolver resolver, - DiagnosticReporter reporter, - CustomLintContext context, + void registerNodeProcessors( + RuleVisitorRegistry registry, + RuleContext context, ) { - context.registry.addVariableDeclaration((node) { - if (_shouldLint(node)) { - reporter.atNode(node, code); - } - }); - } - - bool _shouldLint(VariableDeclaration node) { - final isLateDeclaration = node.isLate; - if (!isLateDeclaration) return false; - - final hasIgnoredType = _hasIgnoredType(node); - if (hasIgnoredType) return false; - - final allowInitialized = config.parameters.allowInitialized; - if (!allowInitialized) return true; - - final hasInitializer = node.initializer != null; - return !hasInitializer; - } - - bool _hasIgnoredType(VariableDeclaration node) { - final ignoredTypes = config.parameters.ignoredTypes.toSet(); - if (ignoredTypes.isEmpty) return false; - - final variableType = node.declaredFragment?.element.type; - if (variableType == null) return false; - - return variableType.hasIgnoredType( - ignoredTypes: ignoredTypes, - ); + registry.addVariableDeclaration(this, AvoidLateKeywordVisitor(this)); } } diff --git a/lib/src/lints/avoid_late_keyword/visitors/avoid_late_keyword_visitor.dart b/lib/src/lints/avoid_late_keyword/visitors/avoid_late_keyword_visitor.dart new file mode 100644 index 00000000..4200f5bd --- /dev/null +++ b/lib/src/lints/avoid_late_keyword/visitors/avoid_late_keyword_visitor.dart @@ -0,0 +1,25 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:solid_lints/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart'; + +/// Visitor for [AvoidLateKeywordRule]. +class AvoidLateKeywordVisitor extends SimpleAstVisitor { + /// The rule to which the visitor belongs. + final AvoidLateKeywordRule rule; + + /// Creates an instance of [AvoidLateKeywordVisitor]. + AvoidLateKeywordVisitor(this.rule); + + @override + void visitVariableDeclaration(VariableDeclaration node) { + if (!node.isLate) { + return; + } + + if (node.initializer != null) { + return; + } + + rule.reportAtNode(node); + } +} diff --git a/test/avoid_late_keyword_rule_test.dart b/test/avoid_late_keyword_rule_test.dart new file mode 100644 index 00000000..d01da4c7 --- /dev/null +++ b/test/avoid_late_keyword_rule_test.dart @@ -0,0 +1,56 @@ +import 'package:analyzer_testing/analysis_rule/analysis_rule.dart'; +import 'package:solid_lints/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(AvoidLateKeywordRuleTest); + }); +} + +@reflectiveTest +class AvoidLateKeywordRuleTest extends AnalysisRuleTest { + @override + void setUp() { + rule = AvoidLateKeywordRule(); + super.setUp(); + } + + void test_reports_uninitialized_late_field() async { + await assertDiagnostics( + r''' +class Test { + late final int field; +} +''', + [lint(30, 5)], + ); + } + + void test_reports_uninitialized_late_local_variable() async { + await assertDiagnostics( + r''' +void m() { + late final String value; +} +''', + [lint(31, 5)], + ); + } + + void test_does_not_report_initialized_late_variable() async { + await assertNoDiagnostics(r''' +class Test { + late final int field = 1; +} +'''); + } + + void test_does_not_report_non_late_variable() async { + await assertNoDiagnostics(r''' +class Test { + final int field = 1; +} +'''); + } +}