Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix avoid-late if initialized #71

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
da4b854
Fix avoid-late if initialized
maxxlab Nov 15, 2023
f5f5bf2
Update lint_test/avoid_late_keyword_test.dart
maxxlab Nov 15, 2023
e34f822
Apply suggestions from code review
maxxlab Nov 15, 2023
2633fc9
Custom avoid-late
maxxlab Nov 15, 2023
f5a4a81
Fix naming
maxxlab Nov 15, 2023
78eef07
Apply suggestions from code review
maxxlab Nov 15, 2023
8aeeeb2
Avoid late simplified
maxxlab Nov 15, 2023
13b5e61
Update lib/lints/avoid_late_keyword/models/avoid_late_keyword_paramet…
maxxlab Nov 15, 2023
83629fe
Avoid-late ignored_types
maxxlab Nov 16, 2023
13a6d16
Avoid-late ignored_types formatted
maxxlab Nov 16, 2023
56abdb6
Update lib/lints/avoid_late_keyword/models/avoid_late_keyword_paramet…
maxxlab Nov 16, 2023
6315eca
Avoid-late ignored_types fix
maxxlab Nov 16, 2023
78246e9
Merge branch 'avoid_late_feature' of https://github.com/maxxlab/solid…
maxxlab Nov 16, 2023
dbd6bdf
Avoid-late ignored_types Fix
maxxlab Nov 16, 2023
d215871
Avoid-late allow_initialized testcases
maxxlab Nov 16, 2023
f639d91
Update lint_test/avoid_late_keyword_allow_initialized_test/pubspec.yaml
maxxlab Nov 16, 2023
850cfaa
Update lib/lints/avoid_late_keyword/models/avoid_late_keyword_paramet…
maxxlab Nov 16, 2023
b426d49
Allow subclasses for avoid-late whitelist
solid-yuriiprykhodko Nov 16, 2023
b023b8c
Fix naming
solid-yuriiprykhodko Nov 16, 2023
b4fe907
Merge pull request #1 from yurii-prykhodko-solid/ignore-late-subclasses
maxxlab Nov 16, 2023
662dc49
Short-circuit of there's no ignored types
solid-yuriiprykhodko Nov 16, 2023
41fe9ab
Short-circuit earlier
solid-yuriiprykhodko Nov 16, 2023
8167c07
Merge pull request #2 from yurii-prykhodko-solid/ignore-late-subclasses
maxxlab Nov 16, 2023
fbf2270
Update lib/lints/avoid_late_keyword/avoid_late_keyword_rule.dart
maxxlab Nov 17, 2023
2037550
Avoid-late ignored_types tests
maxxlab Nov 17, 2023
e3a85fa
Merge branch 'avoid_late_feature' of https://github.com/maxxlab/solid…
maxxlab Nov 17, 2023
bb69baa
Avoid-late add testcases
maxxlab Nov 17, 2023
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
36 changes: 34 additions & 2 deletions lib/lints/avoid_late_keyword/avoid_late_keyword_rule.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
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/lints/avoid_late_keyword/models/avoid_late_keyword_parameters.dart';
import 'package:solid_lints/models/rule_config.dart';
import 'package:solid_lints/models/solid_lint_rule.dart';
import 'package:solid_lints/utils/types_utils.dart';

/// A `late` keyword rule which forbids using it to avoid runtime exceptions.
class AvoidLateKeywordRule extends SolidLintRule {
class AvoidLateKeywordRule extends SolidLintRule<AvoidLateKeywordParameters> {
/// The [LintCode] of this lint rule that represents
/// the error whether we use `late` keyword.
static const lintName = 'avoid_late_keyword';
Expand All @@ -17,6 +20,7 @@ class AvoidLateKeywordRule extends SolidLintRule {
final rule = RuleConfig(
configs: configs,
name: lintName,
paramsParser: AvoidLateKeywordParameters.fromJson,
problemMessage: (_) => 'Avoid using the "late" keyword. '
'It may result in runtime exceptions.',
);
Expand All @@ -31,9 +35,37 @@ class AvoidLateKeywordRule extends SolidLintRule {
CustomLintContext context,
) {
context.registry.addVariableDeclaration((node) {
if (node.declaredElement?.isLate ?? false) {
if (_shouldLint(node)) {
reporter.reportErrorForNode(code, node);
}
});
}

bool _shouldLint(VariableDeclaration node) {
final isLateDeclaration = node.declaredElement?.isLate ?? false;
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.declaredElement?.type;
if (variableType == null) return false;

final checkedTypes = [variableType, ...variableType.supertypes]
.map((t) => t.getDisplayString(withNullability: false))
.toSet();

return checkedTypes.intersection(ignoredTypes).isNotEmpty;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/// A data model class that represents the "avoid late keyword" input
/// parameters.
class AvoidLateKeywordParameters {
/// Allow immediately initialised late variables.
///
/// ```dart
/// late var ok = 0; // ok when allowInitialized == true
/// late var notOk; // initialized elsewhere, not allowed
/// ```
final bool allowInitialized;

/// Types that would be ignored by avoid-late rule
final Iterable<String> ignoredTypes;

/// Constructor for [AvoidLateKeywordParameters] model
const AvoidLateKeywordParameters({
this.allowInitialized = false,
this.ignoredTypes = const [],
});

/// Method for creating from json data
factory AvoidLateKeywordParameters.fromJson(Map<String, Object?> json) =>
AvoidLateKeywordParameters(
allowInitialized: json['allow_initialized'] as bool? ?? false,
ignoredTypes:
List<String>.from(json['ignored_types'] as Iterable? ?? []),
);
}
8 changes: 8 additions & 0 deletions lib/utils/types_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,18 @@
// SOFTWARE.
// ignore_for_file: public_member_api_docs

import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:collection/collection.dart';

extension Subtypes on DartType {
Iterable<DartType> get supertypes {
final element = this.element;
return element is InterfaceElement ? element.allSupertypes : [];
}
}

bool hasWidgetType(DartType type) =>
(isWidgetOrSubclass(type) ||
_isIterable(type) ||
Expand Down
6 changes: 5 additions & 1 deletion lint_test/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ custom_lint:
- function_lines_of_code:
max_lines: 50
- avoid_non_null_assertion
- avoid_late_keyword
- avoid_late_keyword:
allow_initialized: true
ignored_types:
- ColorTween
- AnimationController
- avoid_global_state
- avoid_returning_widgets
- avoid_unnecessary_setstate
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
analyzer:
plugins:
- ../custom_lint

custom_lint:
rules:
- avoid_late_keyword:
allow_initialized: false
ignored_types:
- Animation
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// ignore_for_file: prefer_const_declarations, unused_local_variable, prefer_match_file_name
// ignore_for_file: avoid_global_state

abstract class Animation {}

class AnimationController implements Animation {}

class SubAnimationController extends AnimationController {}

class ColorTween {}

/// Check "late" keyword fail
///
/// `avoid_late_keyword`
/// allow_initialized option disabled
class AvoidLateKeyword {
late final Animation animation1;

late final animation2 = AnimationController();

late final animation3 = SubAnimationController();

/// expect_lint: avoid_late_keyword
late final ColorTween colorTween1;

/// expect_lint: avoid_late_keyword
late final colorTween2 = ColorTween();

/// expect_lint: avoid_late_keyword
late final colorTween3 = colorTween2;

late final AnimationController controller1;

/// expect_lint: avoid_late_keyword
late final field1 = 'string';

/// expect_lint: avoid_late_keyword
late final String field2;

/// expect_lint: avoid_late_keyword
late final String field3 = 'string';

/// expect_lint: avoid_late_keyword
late final field4;

void test() {
late final Animation animation1;

late final animation2 = AnimationController();

late final animation3 = SubAnimationController();

/// expect_lint: avoid_late_keyword
late final ColorTween colorTween1;

/// expect_lint: avoid_late_keyword
late final colorTween2 = ColorTween();

/// expect_lint: avoid_late_keyword
late final colorTween3 = colorTween2;

late final AnimationController controller1;

/// expect_lint: avoid_late_keyword
late final local1 = 'string';

/// expect_lint: avoid_late_keyword
late final String local2;

/// expect_lint: avoid_late_keyword
late final String local4 = 'string';

/// expect_lint: avoid_late_keyword
late final local3;
}
}
11 changes: 11 additions & 0 deletions lint_test/avoid_late_keyword_allow_initialized_test/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: avoid_late_keyword_allow_initialized_test
publish_to: none

environment:
sdk: '>=3.0.0 <4.0.0'

dependencies:

dev_dependencies:
solid_lints:
path: ../../
51 changes: 47 additions & 4 deletions lint_test/avoid_late_keyword_test.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,64 @@
// ignore_for_file: prefer_const_declarations, unused_local_variable, prefer_match_file_name
// ignore_for_file: avoid_global_state

class ColorTween {}

class AnimationController {}

class SubAnimationController extends AnimationController {}

class NotAllowed {}

/// Check "late" keyword fail
///
/// `avoid_late_keyword`
/// allow_initialized option enabled
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's also test allow_initialized == false + ignored types.

class AvoidLateKeyword {
/// expect_lint: avoid_late_keyword
late final ColorTween colorTween;

late final AnimationController controller1;

late final SubAnimationController controller2;

late final controller3 = AnimationController();

late final controller4 = SubAnimationController();

late final field1 = 'string';

/// expect_lint: avoid_late_keyword
late String field2;
late final String field2;

/// expect_lint: avoid_late_keyword
late final field3;

/// expect_lint: avoid_late_keyword
late final NotAllowed na1;

late final na2 = NotAllowed();

void test() {
late final ColorTween colorTween;

late final AnimationController controller1;

late final SubAnimationController controller2;

late final controller3 = AnimationController();

late final controller4 = SubAnimationController();

late final local1 = 'string';

/// expect_lint: avoid_late_keyword
late final field3 = 'string';
late final String local2;

/// expect_lint: avoid_late_keyword
late String field4;
late final local3;

/// expect_lint: avoid_late_keyword
late final NotAllowed na1;

late final na2 = NotAllowed();
}
}