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
82 changes: 24 additions & 58 deletions lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart
Original file line number Diff line number Diff line change
@@ -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
///
Expand Down Expand Up @@ -47,63 +47,29 @@ import 'package:solid_lints/src/utils/types_utils.dart';
/// }
/// }
/// ```
class AvoidLateKeywordRule extends SolidLintRule<AvoidLateKeywordParameters> {
/// 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.',
);
Comment on lines +50 to +56
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The migration to AnalysisRule has removed support for the allow_initialized and ignored_types configuration parameters that were present in the previous SolidLintRule implementation. While the PR title mentions "default behavior", this is a regression for users who rely on these settings (e.g., ignoring AnimationController). Furthermore, the class documentation (lines 15-25) still references these parameters, which is now misleading. Please consider either restoring parameter support or updating the documentation to reflect the current implementation.


/// 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));
}
}
Original file line number Diff line number Diff line change
@@ -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';
Comment on lines +1 to +3
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The isLate extension method used on line 15 is missing its required import. It was previously provided by package:solid_lints/src/utils/types_utils.dart. Without this import, the code will fail to compile.

Suggested change
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';
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';
import 'package:solid_lints/src/utils/types_utils.dart';


/// Visitor for [AvoidLateKeywordRule].
class AvoidLateKeywordVisitor extends SimpleAstVisitor<void> {
/// 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);
}
}
56 changes: 56 additions & 0 deletions test/avoid_late_keyword_rule_test.dart
Original file line number Diff line number Diff line change
@@ -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;
}
''');
}
}
Loading