diff --git a/lib/lints/no_magic_number/models/no_magic_number_parameters.dart b/lib/lints/no_magic_number/models/no_magic_number_parameters.dart index 783d7b31..3a51f439 100644 --- a/lib/lints/no_magic_number/models/no_magic_number_parameters.dart +++ b/lib/lints/no_magic_number/models/no_magic_number_parameters.dart @@ -2,14 +2,20 @@ /// parameters. class NoMagicNumberParameters { static const _allowedConfigName = 'allowed'; + static const _allowedInWidgetParamsConfigName = 'allowed_in_widget_params'; static const _defaultMagicNumbers = [-1, 0, 1]; /// List of allowed numbers final Iterable allowedNumbers; + /// The flag indicates whether magic numbers are allowed as a Widget instance + /// parameter. + final bool allowedInWidgetParams; + /// Constructor for [NoMagicNumberParameters] model const NoMagicNumberParameters({ required this.allowedNumbers, + required this.allowedInWidgetParams, }); /// Method for creating from json data @@ -17,5 +23,7 @@ class NoMagicNumberParameters { NoMagicNumberParameters( allowedNumbers: json[_allowedConfigName] as Iterable? ?? _defaultMagicNumbers, + allowedInWidgetParams: + json[_allowedInWidgetParamsConfigName] as bool? ?? false, ); } diff --git a/lib/lints/no_magic_number/no_magic_number_rule.dart b/lib/lints/no_magic_number/no_magic_number_rule.dart index be55635b..28d03797 100644 --- a/lib/lints/no_magic_number/no_magic_number_rule.dart +++ b/lib/lints/no_magic_number/no_magic_number_rule.dart @@ -22,7 +22,9 @@ // SOFTWARE. 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/lints/no_magic_number/models/no_magic_number_parameters.dart'; import 'package:solid_lints/lints/no_magic_number/no_magic_number_visitor.dart'; @@ -71,7 +73,8 @@ class NoMagicNumberRule extends SolidLintRule { .where(_isNotInsideIndexExpression) .where(_isNotInsideEnumConstantArguments) .where(_isNotDefaultValue) - .where(_isNotInConstructorInitializer); + .where(_isNotInConstructorInitializer) + .where(_isNotWidgetParameter); for (final magicNumber in magicNumbers) { reporter.reportErrorForNode(code, magicNumber); @@ -132,4 +135,31 @@ class NoMagicNumberRule extends SolidLintRule { bool _isNotInConstructorInitializer(Literal literal) { return literal.thisOrAncestorOfType() == null; } + + bool _isNotWidgetParameter(Literal literal) { + if (!config.parameters.allowedInWidgetParams) return true; + + final widgetCreationExpression = literal.thisOrAncestorMatching( + _isWidgetCreationExpression, + ); + + return widgetCreationExpression == null; + } + + bool _isWidgetCreationExpression( + AstNode node, + ) { + if (node is! InstanceCreationExpression) return false; + + final staticType = node.staticType; + + if (staticType is! InterfaceType) return false; + + final widgetSupertype = staticType.allSupertypes.firstWhereOrNull( + (supertype) => + supertype.getDisplayString(withNullability: false) == 'Widget', + ); + + return widgetSupertype != null; + } } diff --git a/lint_test/no_magic_number_allowed_in_widget_params_test/analysis_options.yaml b/lint_test/no_magic_number_allowed_in_widget_params_test/analysis_options.yaml new file mode 100644 index 00000000..8c1644ac --- /dev/null +++ b/lint_test/no_magic_number_allowed_in_widget_params_test/analysis_options.yaml @@ -0,0 +1,8 @@ +analyzer: + plugins: + - ../custom_lint + +custom_lint: + rules: + - no_magic_number: + allowed_in_widget_params: true diff --git a/lint_test/no_magic_number_allowed_in_widget_params_test/no_magic_number_allowed_in_widget_params_test.dart b/lint_test/no_magic_number_allowed_in_widget_params_test/no_magic_number_allowed_in_widget_params_test.dart new file mode 100644 index 00000000..a01e12fa --- /dev/null +++ b/lint_test/no_magic_number_allowed_in_widget_params_test/no_magic_number_allowed_in_widget_params_test.dart @@ -0,0 +1,27 @@ +// Allowed for numbers in a Widget subtype parameters. +abstract interface class Widget {} + +class StatelessWidget implements Widget {} + +class MyWidget extends StatelessWidget { + final MyWidgetDecoration decoration; + final int value; + + MyWidget({ + required this.decoration, + required this.value, + }); +} + +class MyWidgetDecoration { + final int size; + + MyWidgetDecoration({required this.size}); +} + +Widget build() { + return MyWidget( + decoration: MyWidgetDecoration(size: 12), + value: 23, + ); +} diff --git a/lint_test/no_magic_number_allowed_in_widget_params_test/pubspec.yaml b/lint_test/no_magic_number_allowed_in_widget_params_test/pubspec.yaml new file mode 100644 index 00000000..b5d3efe4 --- /dev/null +++ b/lint_test/no_magic_number_allowed_in_widget_params_test/pubspec.yaml @@ -0,0 +1,11 @@ +name: no_magic_number_allowed_in_widget_params_test +description: A starting point for Dart libraries or applications. +publish_to: none + +environment: + sdk: '>=3.0.0 <4.0.0' + +dev_dependencies: + solid_lints: + path: ../../ + test: ^1.20.1 diff --git a/lint_test/no_magic_number_test.dart b/lint_test/no_magic_number_test.dart index 454ca326..3f94784e 100644 --- a/lint_test/no_magic_number_test.dart +++ b/lint_test/no_magic_number_test.dart @@ -86,3 +86,32 @@ class ConstructorInitializer { ConstructorInitializer() : value = 10; } + +abstract interface class Widget {} + +class StatelessWidget implements Widget {} + +class MyWidget extends StatelessWidget { + final MyWidgetDecoration decoration; + final int value; + + MyWidget({ + required this.decoration, + required this.value, + }); +} + +class MyWidgetDecoration { + final int size; + + MyWidgetDecoration({required this.size}); +} + +Widget build() { + return MyWidget( + // expect_lint: no_magic_number + decoration: MyWidgetDecoration(size: 12), + // expect_lint: no_magic_number + value: 23, + ); +}