From 63096c573e627033c6e2a6061d9c087b87615003 Mon Sep 17 00:00:00 2001 From: alex Date: Thu, 2 May 2024 11:21:32 +0300 Subject: [PATCH] Add exclude config to the `cyclomatic_complexity` rule --- .../cyclomatic_complexity_rule.dart | 17 ++++++- .../cyclomatic_complexity_parameters.dart | 28 +++++++----- .../max_cyclomatic_complexity_parameters.dart | 21 +++++++++ .../ignored_entities_model.dart | 4 +- .../ignored_entity.dart | 1 + .../analysis_options.yaml | 13 ++++++ .../cyclomatic_complexity_test.dart | 45 +++++++++++++++++++ 7 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 lib/src/lints/cyclomatic_complexity/models/max_cyclomatic_complexity_parameters.dart create mode 100644 lint_test/cyclomatic_complexity/analysis_options.yaml rename lint_test/{ => cyclomatic_complexity}/cyclomatic_complexity_test.dart (50%) diff --git a/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart b/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart index 86a2570c..62b0e9b8 100644 --- a/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart +++ b/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart @@ -1,3 +1,4 @@ +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'; @@ -39,7 +40,7 @@ class CyclomaticComplexityRule paramsParser: CyclomaticComplexityParameters.fromJson, problemMessage: (value) => 'The maximum allowed complexity of a function is ' - '${value.maxComplexity}. Please decrease it.', + '${value.maxCyclomaticComplexity.maxComplexity}. Please decrease it.', ); return CyclomaticComplexityRule._(rule); @@ -52,11 +53,23 @@ class CyclomaticComplexityRule CustomLintContext context, ) { context.registry.addBlockFunctionBody((node) { + 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 visitor = CyclomaticComplexityFlowVisitor(); node.visitChildren(visitor); if (visitor.complexityEntities.length + 1 > - config.parameters.maxComplexity) { + config.parameters.maxCyclomaticComplexity.maxComplexity) { reporter.reportErrorForNode(code, node); } }); diff --git a/lib/src/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart b/lib/src/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart index 3ffa5ae1..6166a88b 100644 --- a/lib/src/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart +++ b/lib/src/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart @@ -1,19 +1,25 @@ -/// Cyclomatic complexity metric limits configuration. +import 'package:solid_lints/src/lints/cyclomatic_complexity/models/max_cyclomatic_complexity_parameters.dart'; +import 'package:solid_lints/src/models/ignored_entities_model/ignored_entities_model.dart'; + +/// Config parameters for the `cyclomatic_complexity` rule class CyclomaticComplexityParameters { - /// Threshold cyclomatic complexity level, exceeding it triggers a warning. - final int maxComplexity; + /// `max_complexity` configuration + final MaxCyclomaticComplexityParameters maxCyclomaticComplexity; - /// Reference: NIST 500-235 item 2.5 - static const _defaultMaxComplexity = 10; + /// `exclude` configuration + final IgnoredEntitiesModel ignoredEntities; /// Constructor for [CyclomaticComplexityParameters] model const CyclomaticComplexityParameters({ - required this.maxComplexity, + required this.maxCyclomaticComplexity, + required this.ignoredEntities, }); - /// Method for creating from json data - factory CyclomaticComplexityParameters.fromJson(Map json) => - CyclomaticComplexityParameters( - maxComplexity: json['max_complexity'] as int? ?? _defaultMaxComplexity, - ); + /// + factory CyclomaticComplexityParameters.fromJson(Map json) { + return CyclomaticComplexityParameters( + ignoredEntities: IgnoredEntitiesModel.fromJson(json), + maxCyclomaticComplexity: MaxCyclomaticComplexityParameters.fromJson(json), + ); + } } diff --git a/lib/src/lints/cyclomatic_complexity/models/max_cyclomatic_complexity_parameters.dart b/lib/src/lints/cyclomatic_complexity/models/max_cyclomatic_complexity_parameters.dart new file mode 100644 index 00000000..237c681f --- /dev/null +++ b/lib/src/lints/cyclomatic_complexity/models/max_cyclomatic_complexity_parameters.dart @@ -0,0 +1,21 @@ +/// Cyclomatic complexity metric limits configuration. +class MaxCyclomaticComplexityParameters { + /// Threshold cyclomatic complexity level, exceeding it triggers a warning. + final int maxComplexity; + + /// Reference: NIST 500-235 item 2.5 + static const _defaultMaxComplexity = 10; + + /// Constructor for [MaxCyclomaticComplexityParameters] model + const MaxCyclomaticComplexityParameters({ + required this.maxComplexity, + }); + + /// Method for creating from json data + factory MaxCyclomaticComplexityParameters.fromJson( + Map json, + ) => + MaxCyclomaticComplexityParameters( + maxComplexity: json['max_complexity'] as int? ?? _defaultMaxComplexity, + ); +} 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 75f5b876..633a563b 100644 --- a/lib/src/models/ignored_entities_model/ignored_entities_model.dart +++ b/lib/src/models/ignored_entities_model/ignored_entities_model.dart @@ -1,9 +1,9 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:solid_lints/src/models/ignored_entities_model/ignored_entity.dart'; -/// Manages a list of entities (functions/methods/classes) that should be +/// Manages a list of entities (functions/methods/classes) that should be /// excluded from a lint rule. -/// +/// /// Example config: /// ```yaml /// custom_lint: diff --git a/lib/src/models/ignored_entities_model/ignored_entity.dart b/lib/src/models/ignored_entities_model/ignored_entity.dart index f6472064..f182b8ec 100644 --- a/lib/src/models/ignored_entities_model/ignored_entity.dart +++ b/lib/src/models/ignored_entities_model/ignored_entity.dart @@ -15,6 +15,7 @@ class IgnoredEntity { /// Class name final String? className; + /// Function name final String? functionName; diff --git a/lint_test/cyclomatic_complexity/analysis_options.yaml b/lint_test/cyclomatic_complexity/analysis_options.yaml new file mode 100644 index 00000000..8671cb2d --- /dev/null +++ b/lint_test/cyclomatic_complexity/analysis_options.yaml @@ -0,0 +1,13 @@ +analyzer: + plugins: + - ../custom_lint + +custom_lint: + rules: + - cyclomatic_complexity: + max_complexity: 4 + exclude: + - method_name: excludeMethod + class_name: ExcludeClass + - method_name: excludeFunction + - class_name: ExcludeEntireClass diff --git a/lint_test/cyclomatic_complexity_test.dart b/lint_test/cyclomatic_complexity/cyclomatic_complexity_test.dart similarity index 50% rename from lint_test/cyclomatic_complexity_test.dart rename to lint_test/cyclomatic_complexity/cyclomatic_complexity_test.dart index 0fafcdab..33bbd1a4 100644 --- a/lint_test/cyclomatic_complexity_test.dart +++ b/lint_test/cyclomatic_complexity/cyclomatic_complexity_test.dart @@ -31,3 +31,48 @@ class A { if (true) {} } } + +void excludeFunction() { + if (true) { + if (true) { + if (true) { + if (true) {} + } + } + } +} + +class ExcludeEntireClass { + void foo() { + if (true) { + if (true) { + if (true) { + if (true) {} + } + } + } + } +} + +class ExcludeClass { + /// expect_lint: cyclomatic_complexity + void foo() { + if (true) { + if (true) { + if (true) { + if (true) {} + } + } + } + } + + void excludeMethod() { + if (true) { + if (true) { + if (true) { + if (true) {} + } + } + } + } +}