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 7d68d00..4bddb3b 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,10 @@ 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_exclude.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'; import 'package:solid_lints/src/models/solid_lint_rule.dart'; import 'package:solid_lints/src/utils/types_utils.dart'; @@ -48,7 +51,8 @@ import 'package:solid_lints/src/utils/types_utils.dart'; /// } /// } /// ``` -class AvoidReturningWidgetsRule extends SolidLintRule { +class AvoidReturningWidgetsRule + extends SolidLintRule { /// The [LintCode] of this lint rule that represents /// the error whether we return a widget. static const lintName = 'avoid_returning_widgets'; @@ -61,6 +65,7 @@ class AvoidReturningWidgetsRule extends SolidLintRule { final rule = RuleConfig( configs: configs, name: lintName, + paramsParser: AvoidReturningWidgetsParameters.fromJson, problemMessage: (_) => 'Returning a widget from a function is considered an anti-pattern. ' 'Unless you are overriding an existing method, ' @@ -92,11 +97,33 @@ class AvoidReturningWidgetsRule extends SolidLintRule { final isWidgetReturned = hasWidgetType(returnType); + final isIgnored = _hasIgnored(node, returnType); + final isOverriden = node.declaredElement?.hasOverride ?? false; - if (isWidgetReturned && !isOverriden) { + if (isWidgetReturned && !isOverriden && !isIgnored) { reporter.reportErrorForNode(code, node); } }); } + + bool _hasIgnored(Declaration node, DartType returnType) { + final methodName = node.declaredElement?.name; + + final excludedItem = config.parameters.exclude + .firstWhereOrNull((e) => e.methodName == methodName); + + if (excludedItem + case AvoidReturningWidgetsExclude( + :final String? className, + )) { + if (className == null) { + return true; + } else { + return returnType.hasIgnoredType(ignoredTypes: {className}); + } + } + + return false; + } } diff --git a/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_exclude.dart b/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_exclude.dart new file mode 100644 index 0000000..f2cd81b --- /dev/null +++ b/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_exclude.dart @@ -0,0 +1,24 @@ +/// Model class for AvoidReturningWidgetsExclude parameters +class AvoidReturningWidgetsExclude { + /// 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({ + required this.methodName, + required this.className, + }); + + /// + factory AvoidReturningWidgetsExclude.fromJson( + Map json, + ) { + return AvoidReturningWidgetsExclude( + methodName: json['method_name'] as String, + className: json['class_name'] as String?, + ); + } +} 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 new file mode 100644 index 0000000..b9846ce --- /dev/null +++ b/lib/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart @@ -0,0 +1,27 @@ +import 'package:solid_lints/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_exclude.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; + + /// Constructor for [AvoidReturningWidgetsParameters] model + AvoidReturningWidgetsParameters({ + required this.exclude, + }); + + /// Method for creating from json data + factory AvoidReturningWidgetsParameters.fromJson(Map json) { + final exclude = []; + + for (final item in (json['exclude'] as Iterable?) ?? []) { + if (item is Map && item['method_name'] is String) { + exclude.add(AvoidReturningWidgetsExclude.fromJson(item)); + } + } + return AvoidReturningWidgetsParameters( + exclude: exclude, + ); + } +} diff --git a/lint_test/avoid_returning_widget_test/analysis_options.yaml b/lint_test/avoid_returning_widget_test/analysis_options.yaml new file mode 100644 index 0000000..a382036 --- /dev/null +++ b/lint_test/avoid_returning_widget_test/analysis_options.yaml @@ -0,0 +1,11 @@ +analyzer: + plugins: + - ../custom_lint + +custom_lint: + rules: + - avoid_returning_widgets: + exclude: + - method_name: excludeWidget + class_name: SizedBox + - method_name: excludeWidget2 diff --git a/lint_test/avoid_returning_widget_test.dart b/lint_test/avoid_returning_widget_test/avoid_returning_widget_test.dart similarity index 94% rename from lint_test/avoid_returning_widget_test.dart rename to lint_test/avoid_returning_widget_test/avoid_returning_widget_test.dart index cc7a5fa..3051290 100644 --- a/lint_test/avoid_returning_widget_test.dart +++ b/lint_test/avoid_returning_widget_test/avoid_returning_widget_test.dart @@ -64,3 +64,7 @@ class MyWidget extends BaseWidget { Widget build() { return Offstage(); } + +SizedBox excludeWidget() => const SizedBox(); + +Container excludeWidget2() => Container();