From 9995073c387738c1580025964f04c622b46d8662 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 2 May 2024 15:28:41 +0300 Subject: [PATCH] Implement `exclude` property for the `function_lines_of_code` rule --- .../avoid_returning_widgets_rule.dart | 3 +- .../avoid_unused_parameters_visitor.dart | 20 ++++--------- .../cyclomatic_complexity_rule.dart | 21 ++++++++------ .../function_lines_of_code_rule.dart | 29 +++++++------------ .../function_lines_of_code_parameters.dart | 25 ++++++++-------- .../models/max_lines_parameters.dart | 20 +++++++++++++ .../ignored_entities_model.dart | 20 +++++++++---- .../analysis_options.yaml | 8 +++-- .../function_lines_of_code_test.dart | 17 +++++++++-- 9 files changed, 95 insertions(+), 68 deletions(-) create mode 100644 lib/src/lints/function_lines_of_code/models/max_lines_parameters.dart 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 d03c5e4..9f0a54a 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 @@ -94,7 +94,8 @@ class AvoidReturningWidgetsRule extends SolidLintRule { final isWidgetReturned = hasWidgetType(returnType); - final isIgnored = config.parameters.matchMethod(node); + final isIgnored = config.parameters.matchMethod(node) || + config.parameters.matchClass(node); final isOverriden = node.declaredElement?.hasOverride ?? false; diff --git a/lib/src/lints/avoid_unused_parameters/visitors/avoid_unused_parameters_visitor.dart b/lib/src/lints/avoid_unused_parameters/visitors/avoid_unused_parameters_visitor.dart index 5b0e1a0..fe79224 100644 --- a/lib/src/lints/avoid_unused_parameters/visitors/avoid_unused_parameters_visitor.dart +++ b/lib/src/lints/avoid_unused_parameters/visitors/avoid_unused_parameters_visitor.dart @@ -56,10 +56,8 @@ class AvoidUnusedParametersVisitor extends RecursiveAstVisitor { return; } - if (node.thisOrAncestorOfType() case final classNode?) { - if (ignoredEntities.matchClass(classNode)) { - return; - } + if (ignoredEntities.matchClass(node)) { + return; } final unused = _getUnusedParameters( @@ -87,16 +85,10 @@ class AvoidUnusedParametersVisitor extends RecursiveAstVisitor { return; } - if (ignoredEntities.matchMethod(node)) { + if (ignoredEntities.matchMethod(node) || ignoredEntities.matchClass(node)) { return; } - if (node.thisOrAncestorOfType() case final classNode?) { - if (ignoredEntities.matchClass(classNode)) { - return; - } - } - final isTearOff = _usedAsTearOff(node); if (!isOverride(node.metadata) && !isTearOff) { @@ -117,10 +109,8 @@ class AvoidUnusedParametersVisitor extends RecursiveAstVisitor { return; } - if (node.thisOrAncestorOfType() case final funcNode?) { - if (ignoredEntities.matchMethod(funcNode)) { - return; - } + if (ignoredEntities.matchMethod(node)) { + return; } _unusedParameters.addAll( diff --git a/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart b/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart index 62b0e9b..a4d4c3b 100644 --- a/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart +++ b/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart @@ -1,4 +1,3 @@ -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/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart'; @@ -53,17 +52,21 @@ class CyclomaticComplexityRule CustomLintContext context, ) { context.registry.addBlockFunctionBody((node) { - final functionNode = node.thisOrAncestorOfType(); - if (functionNode != null && - config.parameters.ignoredEntities.matchMethod(functionNode)) { + if (config.parameters.ignoredEntities.matchMethod(node) || + config.parameters.ignoredEntities.matchClass(node)) { return; } + // final functionNode = node.thisOrAncestorOfType(); + // if (functionNode != null && + // config.parameters.ignoredEntities.matchMethod(functionNode)) { + // return; + // } - final classNode = node.thisOrAncestorOfType(); - if (classNode != null && - config.parameters.ignoredEntities.matchClass(classNode)) { - return; - } + // final classNode = node.thisOrAncestorOfType(); + // if (classNode != null && + // config.parameters.ignoredEntities.matchClass(classNode)) { + // return; + // } final visitor = CyclomaticComplexityFlowVisitor(); node.visitChildren(visitor); diff --git a/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart b/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart index ff90076..a2abc8f 100644 --- a/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart +++ b/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart @@ -16,8 +16,11 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart'; /// rules: /// - function_lines_of_code: /// max_lines: 100 -/// excludeNames: -/// - "Build" +/// exclude: +/// - method_name: excludeMethod +/// class_name: ExcludeClass +/// - method_name: excludeFunction +/// - class_name: ExcludeEntireClass /// ``` class FunctionLinesOfCodeRule extends SolidLintRule { @@ -35,7 +38,7 @@ class FunctionLinesOfCodeRule name: lintName, paramsParser: FunctionLinesOfCodeParameters.fromJson, problemMessage: (value) => - 'The maximum allowed number of lines is ${value.maxLines}. ' + 'The maximum allowed number of lines is ${value.maxLinesModel}. ' 'Try splitting this function into smaller parts.', ); @@ -60,16 +63,16 @@ class FunctionLinesOfCodeRule ErrorReporter reporter, AstNode node, ) { - final functionName = _getFunctionName(node); - if (functionName != null && - config.parameters.excludeNames.contains(functionName)) { + if (config.parameters.ignoredEntitiesModel.matchMethod(node) || + config.parameters.ignoredEntitiesModel.matchClass(node)) { return; } final visitor = FunctionLinesOfCodeVisitor(resolver.lineInfo); node.visitChildren(visitor); - if (visitor.linesWithCode.length > config.parameters.maxLines) { + if (visitor.linesWithCode.length > + config.parameters.maxLinesModel.maxLines) { if (node is! AnnotatedNode) { return reporter.reportErrorForNode(code, node); } @@ -84,16 +87,4 @@ class FunctionLinesOfCodeRule ); } } - - String? _getFunctionName(AstNode node) { - if (node is FunctionDeclaration) { - return node.name.lexeme; - } else if (node is MethodDeclaration) { - return node.name.lexeme; - } else if (node is FunctionExpression) { - return node.declaredElement?.name; - } else { - return null; - } - } } diff --git a/lib/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart b/lib/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart index 757f5ba..6852f1c 100644 --- a/lib/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart +++ b/lib/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart @@ -1,26 +1,25 @@ -/// A data model class that represents the "function lines of code" input +import 'package:solid_lints/src/lints/function_lines_of_code/models/max_lines_parameters.dart'; +import 'package:solid_lints/src/models/ignored_entities_model/ignored_entities_model.dart'; + +/// A data model class that represents the `function_lines_of_code` config /// parameters. class FunctionLinesOfCodeParameters { - /// Maximum allowed number of lines of code (LoC) per function, - /// exceeding this limit triggers a warning. - final int maxLines; - - /// Function names to be excluded from the rule check - final List excludeNames; + /// The `max_lines` configuration + final MaxLinesParameters maxLinesModel; - static const _defaultMaxLines = 200; + /// The `exclude` configuration + final IgnoredEntitiesModel ignoredEntitiesModel; /// Constructor for [FunctionLinesOfCodeParameters] model const FunctionLinesOfCodeParameters({ - required this.maxLines, - required this.excludeNames, + required this.maxLinesModel, + required this.ignoredEntitiesModel, }); /// Method for creating from json data factory FunctionLinesOfCodeParameters.fromJson(Map json) => FunctionLinesOfCodeParameters( - maxLines: json['max_lines'] as int? ?? _defaultMaxLines, - excludeNames: - List.from(json['excludeNames'] as Iterable? ?? []), + maxLinesModel: MaxLinesParameters.fromJson(json), + ignoredEntitiesModel: IgnoredEntitiesModel.fromJson(json), ); } diff --git a/lib/src/lints/function_lines_of_code/models/max_lines_parameters.dart b/lib/src/lints/function_lines_of_code/models/max_lines_parameters.dart new file mode 100644 index 0000000..997e24c --- /dev/null +++ b/lib/src/lints/function_lines_of_code/models/max_lines_parameters.dart @@ -0,0 +1,20 @@ +/// Data model that represents the limit for the amount of lines within a +/// function. +class MaxLinesParameters { + /// Maximum allowed number of lines of code (LoC) per function, + /// exceeding this limit triggers a warning. + final int maxLines; + + static const _defaultMaxLines = 200; + + /// + const MaxLinesParameters({ + required this.maxLines, + }); + + /// Method for creating from json data + factory MaxLinesParameters.fromJson(Map json) => + MaxLinesParameters( + maxLines: json['max_lines'] as int? ?? _defaultMaxLines, + ); +} diff --git a/lib/src/models/ignored_entities_model/ignored_entities_model.dart b/lib/src/models/ignored_entities_model/ignored_entities_model.dart index 633a563..3a69ce0 100644 --- a/lib/src/models/ignored_entities_model/ignored_entities_model.dart +++ b/lib/src/models/ignored_entities_model/ignored_entities_model.dart @@ -38,8 +38,13 @@ class IgnoredEntitiesModel { /// Checks if the entire class should be ignored. /// Doesn't match if the config specifies a specific function within the class - bool matchClass(ClassDeclaration node) { - final className = node.name.toString(); + bool matchClass(AstNode node) { + final classNode = node.thisOrAncestorOfType(); + if (classNode == null) { + return false; + } + + final className = classNode.name.toString(); return entities.any((element) { return element.functionName == null && element.className == className; @@ -47,8 +52,13 @@ class IgnoredEntitiesModel { } /// Checks if the given method/function should be ignored. - bool matchMethod(Declaration node) { - final methodName = node.declaredElement?.name; + bool matchMethod(AstNode node) { + final methodNode = node.thisOrAncestorOfType(); + if (methodNode == null) { + return false; + } + + final methodName = methodNode.declaredElement?.name; return entities.any((entity) { if (entity.functionName != methodName) { @@ -59,7 +69,7 @@ class IgnoredEntitiesModel { return true; } - final matchingClass = node.thisOrAncestorMatching((node) { + final matchingClass = methodNode.thisOrAncestorMatching((node) { if (node case final ClassDeclaration classNode) { return classNode.name.toString() == entity.className; } diff --git a/lint_test/function_lines_of_code_test/analysis_options.yaml b/lint_test/function_lines_of_code_test/analysis_options.yaml index 51f88ab..49c25f7 100644 --- a/lint_test/function_lines_of_code_test/analysis_options.yaml +++ b/lint_test/function_lines_of_code_test/analysis_options.yaml @@ -6,6 +6,8 @@ custom_lint: rules: - function_lines_of_code: max_lines: 5 - excludeNames: - - "longFunctionExcluded" - - "longMethodExcluded" + exclude: + - method_name: excludeMethod + class_name: ExcludeClass + - method_name: excludeFunction + - class_name: ExcludeEntireClass \ No newline at end of file diff --git a/lint_test/function_lines_of_code_test/function_lines_of_code_test.dart b/lint_test/function_lines_of_code_test/function_lines_of_code_test.dart index aa4211a..72fea04 100644 --- a/lint_test/function_lines_of_code_test/function_lines_of_code_test.dart +++ b/lint_test/function_lines_of_code_test/function_lines_of_code_test.dart @@ -1,4 +1,4 @@ -class ClassWithLongMethods { +class ExcludeClass { int notLongMethod() { var i = 0; i++; @@ -18,7 +18,7 @@ class ClassWithLongMethods { } // Excluded by excludeNames - int longMethodExcluded() { + int excludeMethod() { var i = 0; i++; i++; @@ -47,7 +47,7 @@ int longFunction() { } // Excluded by excludeNames -int longFunctionExcluded() { +int excludeFunction() { var i = 0; i++; i++; @@ -55,3 +55,14 @@ int longFunctionExcluded() { i++; return i; } + +class ExcludeEntireClass { + int longFunction() { + var i = 0; + i++; + i++; + i++; + i++; + return i; + } +}