diff --git a/CHANGELOG.md b/CHANGELOG.md index 31779dec..1f3d977e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.4 + +- Fixed an issue with `prefer_early_retrun` for throw expression + ## 0.2.3 - Replace deprecated whereNotNull() diff --git a/lib/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart b/lib/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart index d1a7195a..3c113552 100644 --- a/lib/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart +++ b/lib/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart @@ -1,7 +1,6 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/error/listener.dart'; -import 'package:collection/collection.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:solid_lints/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart'; import 'package:solid_lints/src/models/rule_config.dart'; @@ -96,7 +95,7 @@ class AvoidReturningWidgetsRule final isWidgetReturned = hasWidgetType(returnType); - final isIgnored = _shouldIgnore(node); + final isIgnored = config.parameters.exclude.shouldIgnore(node); final isOverriden = node.declaredElement?.hasOverride ?? false; @@ -105,25 +104,4 @@ class AvoidReturningWidgetsRule } }); } - - bool _shouldIgnore(Declaration node) { - final methodName = node.declaredElement?.name; - - final excludedItem = config.parameters.exclude - .firstWhereOrNull((e) => e.methodName == methodName); - - if (excludedItem == null) return false; - - final className = excludedItem.className; - - if (className == null || node is! MethodDeclaration) { - return true; - } else { - final classDeclaration = node.thisOrAncestorOfType(); - - if (classDeclaration == null) return false; - - return classDeclaration.name.toString() == className; - } - } } diff --git a/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart b/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart index 75012f5d..f753f31f 100644 --- a/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart +++ b/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart @@ -1,10 +1,10 @@ -import 'package:solid_lints/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_exclude.dart'; +import 'package:solid_lints/src/models/exlude_params.dart'; /// A data model class that represents the "avoid returning widgets" input /// parameters. class AvoidReturningWidgetsParameters { /// A list of methods that should be excluded from the lint. - final List exclude; + final ExcludeParameters exclude; /// Constructor for [AvoidReturningWidgetsParameters] model AvoidReturningWidgetsParameters({ @@ -13,16 +13,10 @@ class AvoidReturningWidgetsParameters { /// Method for creating from json data factory AvoidReturningWidgetsParameters.fromJson(Map json) { - final exclude = []; - - final excludeList = json['exclude'] as Iterable? ?? []; - for (final item in excludeList) { - if (item is Map) { - exclude.add(AvoidReturningWidgetsExclude.fromJson(item)); - } - } return AvoidReturningWidgetsParameters( - exclude: exclude, + exclude: ExcludeParameters.fromJson( + excludeList: json['exclude'] as Iterable, + ), ); } } diff --git a/lib/src/lints/prefer_early_return/visitors/prefer_early_return_visitor.dart b/lib/src/lints/prefer_early_return/visitors/prefer_early_return_visitor.dart index cc82a1ec..127de6e8 100644 --- a/lib/src/lints/prefer_early_return/visitors/prefer_early_return_visitor.dart +++ b/lib/src/lints/prefer_early_return/visitors/prefer_early_return_visitor.dart @@ -1,6 +1,7 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:solid_lints/src/lints/prefer_early_return/visitors/return_statement_visitor.dart'; +import 'package:solid_lints/src/lints/prefer_early_return/visitors/throw_expression_visitor.dart'; /// The AST visitor that will collect all unnecessary if statements class PreferEarlyReturnVisitor extends RecursiveAstVisitor { @@ -33,6 +34,7 @@ class PreferEarlyReturnVisitor extends RecursiveAstVisitor { if (_isElseIfStatement(node)) return; if (_hasElseStatement(node)) return; if (_hasReturnStatement(node)) return; + if (_hasThrowExpression(node)) return; _nodes.add(node); } @@ -70,4 +72,10 @@ class PreferEarlyReturnVisitor extends RecursiveAstVisitor { node.accept(visitor); return visitor.nodes.isNotEmpty; } + + bool _hasThrowExpression(Statement node) { + final visitor = ThrowExpressionVisitor(); + node.accept(visitor); + return visitor.nodes.isNotEmpty; + } } diff --git a/lib/src/lints/prefer_early_return/visitors/throw_expression_visitor.dart b/lib/src/lints/prefer_early_return/visitors/throw_expression_visitor.dart new file mode 100644 index 00000000..ceff7bd2 --- /dev/null +++ b/lib/src/lints/prefer_early_return/visitors/throw_expression_visitor.dart @@ -0,0 +1,16 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; + +/// The AST visitor that will collect every Return statement +class ThrowExpressionVisitor extends RecursiveAstVisitor { + final _nodes = []; + + /// All unnecessary return statements + Iterable get nodes => _nodes; + + @override + void visitThrowExpression(ThrowExpression node) { + super.visitThrowExpression(node); + _nodes.add(node); + } +} diff --git a/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_exclude.dart b/lib/src/models/exclude_rule.dart similarity index 59% rename from lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_exclude.dart rename to lib/src/models/exclude_rule.dart index f2cd81be..e4f4ae09 100644 --- a/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_exclude.dart +++ b/lib/src/models/exclude_rule.dart @@ -1,22 +1,23 @@ -/// Model class for AvoidReturningWidgetsExclude parameters -class AvoidReturningWidgetsExclude { + +/// Model class for ExcludeRule parameters +class ExcludeRule { /// The name of the method that should be excluded from the lint. final String methodName; /// The name of the class that should be excluded from the lint. final String? className; - /// Constructor for [AvoidReturningWidgetsExclude] model - const AvoidReturningWidgetsExclude({ + /// Constructor for [ExcludeRule] model + const ExcludeRule({ required this.methodName, required this.className, }); /// - factory AvoidReturningWidgetsExclude.fromJson( + factory ExcludeRule.fromJson( Map json, ) { - return AvoidReturningWidgetsExclude( + return ExcludeRule( methodName: json['method_name'] as String, className: json['class_name'] as String?, ); diff --git a/lib/src/models/exlude_params.dart b/lib/src/models/exlude_params.dart new file mode 100644 index 00000000..753cf1d2 --- /dev/null +++ b/lib/src/models/exlude_params.dart @@ -0,0 +1,53 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:collection/collection.dart'; +import 'package:solid_lints/src/models/exclude_rule.dart'; + +/// A data model class that represents the "avoid returning widgets" input +/// parameters. +class ExcludeParameters { + /// A list of methods that should be excluded from the lint. + final List exclude; + + /// Constructor for [ExcludeParameters] model + ExcludeParameters({ + required this.exclude, + }); + + /// Method for creating from json data + factory ExcludeParameters.fromJson({ + required Iterable excludeList, + }) { + final exclude = []; + + for (final item in excludeList) { + if (item is Map) { + exclude.add(ExcludeRule.fromJson(item)); + } + } + return ExcludeParameters( + exclude: exclude, + ); + } + + /// Method to define ignoring + bool shouldIgnore(Declaration node) { + final methodName = node.declaredElement?.name; + + final excludedItem = + exclude.firstWhereOrNull((e) => e.methodName == methodName); + + if (excludedItem == null) return false; + + final className = excludedItem.className; + + if (className == null || node is! MethodDeclaration) { + return true; + } else { + final classDeclaration = node.thisOrAncestorOfType(); + + if (classDeclaration == null) return false; + + return classDeclaration.name.toString() == className; + } + } +} diff --git a/lint_test/prefer_early_return_test.dart b/lint_test/prefer_early_return_test.dart index f5a076bf..d9108186 100644 --- a/lint_test/prefer_early_return_test.dart +++ b/lint_test/prefer_early_return_test.dart @@ -235,3 +235,65 @@ void threeSeqentialIfReturn2() { _doSomething(); } } + +void oneIfWithThrowWithReturn() { + //no lint + if (true) { + throw ''; + } + + return; +} + +void oneIfElseWithThrowReturn() { + //no lint + if (true) { + _doSomething(); + } else { + throw ''; + } +} + +void twoSeqentialIfWithThrow() { + if (false) throw ''; + //expect_lint: prefer_early_return + if (true) { + _doSomething(); + } +} + +void twoSeqentialIfWithThrowReturn2() { + //no lint + if (false) throw ''; + //expect_lint: prefer_early_return + if (true) { + _doSomething(); + } + + return; +} + +void threeSeqentialIfWithThrowReturn() { + //no lint + if (false) throw ''; + if (true) throw ''; + //expect_lint: prefer_early_return + if (true) { + _doSomething(); + } + + return; +} + +void threeSeqentialIfWithThrowReturn2() { + //no lint + if (false) throw ''; + //no lint + if (true) { + _doSomething(); + } + //expect_lint: prefer_early_return + if (true) { + _doSomething(); + } +}