Skip to content

Commit

Permalink
Implement exclude property for the function_lines_of_code rule
Browse files Browse the repository at this point in the history
  • Loading branch information
sufftea committed May 2, 2024
1 parent 63096c5 commit 9995073
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ class AvoidReturningWidgetsRule extends SolidLintRule<IgnoredEntitiesModel> {

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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ class AvoidUnusedParametersVisitor extends RecursiveAstVisitor<void> {
return;
}

if (node.thisOrAncestorOfType<ClassDeclaration>() case final classNode?) {
if (ignoredEntities.matchClass(classNode)) {
return;
}
if (ignoredEntities.matchClass(node)) {
return;
}

final unused = _getUnusedParameters(
Expand Down Expand Up @@ -87,16 +85,10 @@ class AvoidUnusedParametersVisitor extends RecursiveAstVisitor<void> {
return;
}

if (ignoredEntities.matchMethod(node)) {
if (ignoredEntities.matchMethod(node) || ignoredEntities.matchClass(node)) {
return;
}

if (node.thisOrAncestorOfType<ClassDeclaration>() case final classNode?) {
if (ignoredEntities.matchClass(classNode)) {
return;
}
}

final isTearOff = _usedAsTearOff(node);

if (!isOverride(node.metadata) && !isTearOff) {
Expand All @@ -117,10 +109,8 @@ class AvoidUnusedParametersVisitor extends RecursiveAstVisitor<void> {
return;
}

if (node.thisOrAncestorOfType<FunctionDeclaration>() case final funcNode?) {
if (ignoredEntities.matchMethod(funcNode)) {
return;
}
if (ignoredEntities.matchMethod(node)) {
return;
}

_unusedParameters.addAll(
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -53,17 +52,21 @@ class CyclomaticComplexityRule
CustomLintContext context,
) {
context.registry.addBlockFunctionBody((node) {
final functionNode = node.thisOrAncestorOfType<Declaration>();
if (functionNode != null &&
config.parameters.ignoredEntities.matchMethod(functionNode)) {
if (config.parameters.ignoredEntities.matchMethod(node) ||
config.parameters.ignoredEntities.matchClass(node)) {
return;
}
// final functionNode = node.thisOrAncestorOfType<Declaration>();
// if (functionNode != null &&
// config.parameters.ignoredEntities.matchMethod(functionNode)) {
// return;
// }

final classNode = node.thisOrAncestorOfType<ClassDeclaration>();
if (classNode != null &&
config.parameters.ignoredEntities.matchClass(classNode)) {
return;
}
// final classNode = node.thisOrAncestorOfType<ClassDeclaration>();
// if (classNode != null &&
// config.parameters.ignoredEntities.matchClass(classNode)) {
// return;
// }

final visitor = CyclomaticComplexityFlowVisitor();
node.visitChildren(visitor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<FunctionLinesOfCodeParameters> {
Expand All @@ -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.',
);

Expand All @@ -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);
}
Expand All @@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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<String> 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<String, Object?> json) =>
FunctionLinesOfCodeParameters(
maxLines: json['max_lines'] as int? ?? _defaultMaxLines,
excludeNames:
List<String>.from(json['excludeNames'] as Iterable? ?? []),
maxLinesModel: MaxLinesParameters.fromJson(json),
ignoredEntitiesModel: IgnoredEntitiesModel.fromJson(json),
);
}
Original file line number Diff line number Diff line change
@@ -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<String, Object?> json) =>
MaxLinesParameters(
maxLines: json['max_lines'] as int? ?? _defaultMaxLines,
);
}
20 changes: 15 additions & 5 deletions lib/src/models/ignored_entities_model/ignored_entities_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,27 @@ 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<ClassDeclaration>();
if (classNode == null) {
return false;
}

final className = classNode.name.toString();

return entities.any((element) {
return element.functionName == null && element.className == className;
});
}

/// 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<Declaration>();
if (methodNode == null) {
return false;
}

final methodName = methodNode.declaredElement?.name;

return entities.any((entity) {
if (entity.functionName != methodName) {
Expand All @@ -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;
}
Expand Down
8 changes: 5 additions & 3 deletions lint_test/function_lines_of_code_test/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class ClassWithLongMethods {
class ExcludeClass {
int notLongMethod() {
var i = 0;
i++;
Expand All @@ -18,7 +18,7 @@ class ClassWithLongMethods {
}

// Excluded by excludeNames
int longMethodExcluded() {
int excludeMethod() {
var i = 0;
i++;
i++;
Expand Down Expand Up @@ -47,11 +47,22 @@ int longFunction() {
}

// Excluded by excludeNames
int longFunctionExcluded() {
int excludeFunction() {
var i = 0;
i++;
i++;
i++;
i++;
return i;
}

class ExcludeEntireClass {
int longFunction() {
var i = 0;
i++;
i++;
i++;
i++;
return i;
}
}

0 comments on commit 9995073

Please sign in to comment.