Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 167 1 #186

Merged
merged 15 commits into from
Dec 6, 2024
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
## 0.2.4

## 0.3.0
- Added `exclude` parameter for the following lints:
- `avoid_returning_widgets`
- `avoid_unused_parameters`
- `cyclomatic_complexity`
- `function_lines_of_code`
- `no_empty_bloc`
- `number_of_parameters`
- BREAKING CHANGE: Renamed `excludeNames` parameter to `exclude` for `function_lines_of_code` lint.
- Fixed an issue with `prefer_early_retrun` for throw expression

## 0.2.3
Expand Down
10 changes: 7 additions & 3 deletions lib/src/common/parameters/excluded_identifier_parameter.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
/// Model class for ExcludeRule parameters
class ExcludedIdentifierParameter {
/// The name of the method that should be excluded from the lint.
final String methodName;
final String? methodName;

/// The name of the class that should be excluded from the lint.
final String? className;

/// The name of the plain Strings that should be excluded from the lint
final String? declarationName;

/// Constructor for [ExcludedIdentifierParameter] model
const ExcludedIdentifierParameter({
required this.methodName,
required this.className,
this.methodName,
this.className,
this.declarationName,
});

///
Expand Down
38 changes: 35 additions & 3 deletions lib/src/common/parameters/excluded_identifiers_list_parameter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,51 @@ class ExcludedIdentifiersListParameter {
for (final item in excludeList) {
if (item is Map) {
exclude.add(ExcludedIdentifierParameter.fromJson(item));
} else if (item is String) {
exclude.add(
ExcludedIdentifierParameter(
declarationName: item,
),
);
}
}
return ExcludedIdentifiersListParameter(
exclude: exclude,
);
}

/// Method for creating from json data with default params
factory ExcludedIdentifiersListParameter.defaultFromJson(
Map<String, dynamic> json,
) {
final excludeList =
json[ExcludedIdentifiersListParameter.excludeParameterName]
as Iterable? ??
[];

return ExcludedIdentifiersListParameter.fromJson(
excludeList: excludeList,
);
}

/// Returns whether the target node should be ignored during analysis.
bool shouldIgnore(Declaration node) {
final methodName = node.declaredElement?.name;
final declarationName = node.declaredElement?.name;

final excludedItem =
exclude.firstWhereOrNull((e) => e.methodName == methodName);
final excludedItem = exclude.firstWhereOrNull(
(e) {
if (e.declarationName == declarationName) {
return true;
} else if (node is ClassDeclaration) {
return e.className == declarationName;
} else if (node is MethodDeclaration) {
return e.methodName == declarationName;
} else if (node is FunctionDeclaration) {
return e.methodName == declarationName;
}
return false;
},
);

if (excludedItem == null) return false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ class AvoidReturningWidgetsParameters {
/// Method for creating from json data
factory AvoidReturningWidgetsParameters.fromJson(Map<String, dynamic> json) {
return AvoidReturningWidgetsParameters(
exclude: ExcludedIdentifiersListParameter.fromJson(
excludeList: json[ExcludedIdentifiersListParameter.excludeParameterName]
as Iterable? ??
[],
),
exclude: ExcludedIdentifiersListParameter.defaultFromJson(json),
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/src/lints/avoid_unused_parameters/models/avoid_unused_parameters.dart';
import 'package:solid_lints/src/lints/avoid_unused_parameters/visitors/avoid_unused_parameters_visitor.dart';
import 'package:solid_lints/src/models/rule_config.dart';
import 'package:solid_lints/src/models/solid_lint_rule.dart';
Expand Down Expand Up @@ -64,7 +65,7 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart';
/// };
///
/// ```
class AvoidUnusedParametersRule extends SolidLintRule {
class AvoidUnusedParametersRule extends SolidLintRule<AvoidUnusedParameters> {
/// This lint rule represents
/// the error whether we use bad formatted double literals.
static const String lintName = 'avoid_unused_parameters';
Expand All @@ -79,6 +80,7 @@ class AvoidUnusedParametersRule extends SolidLintRule {
final rule = RuleConfig(
configs: configs,
name: lintName,
paramsParser: AvoidUnusedParameters.fromJson,
problemMessage: (_) => 'Parameter is unused.',
);

Expand All @@ -91,7 +93,11 @@ class AvoidUnusedParametersRule extends SolidLintRule {
ErrorReporter reporter,
CustomLintContext context,
) {
context.registry.addCompilationUnit((node) {
context.registry.addDeclaration((node) {
final isIgnored = config.parameters.exclude.shouldIgnore(node);

if (isIgnored) return;

final visitor = AvoidUnusedParametersVisitor();
node.accept(visitor);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart';

/// A data model class that represents the "avoid returning widgets" input
/// parameters.
class AvoidUnusedParameters {
/// A list of methods that should be excluded from the lint.
final ExcludedIdentifiersListParameter exclude;

/// Constructor for [AvoidUnusedParameters] model
AvoidUnusedParameters({
required this.exclude,
});

/// Method for creating from json data
factory AvoidUnusedParameters.fromJson(Map<String, dynamic> json) {
return AvoidUnusedParameters(
exclude: ExcludedIdentifiersListParameter.defaultFromJson(json),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,19 @@ class CyclomaticComplexityRule
CustomLintContext context,
) {
context.registry.addBlockFunctionBody((node) {
final visitor = CyclomaticComplexityFlowVisitor();
node.visitChildren(visitor);
context.registry.addDeclaration((declarationNode) {
final isIgnored =
config.parameters.exclude.shouldIgnore(declarationNode);
if (isIgnored) return;

if (visitor.complexityEntities.length + 1 >
config.parameters.maxComplexity) {
reporter.atNode(node, code);
}
final visitor = CyclomaticComplexityFlowVisitor();
node.visitChildren(visitor);

if (visitor.complexityEntities.length + 1 >
config.parameters.maxComplexity) {
reporter.atNode(node, code);
}
});
});
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart';

/// Cyclomatic complexity metric limits configuration.
class CyclomaticComplexityParameters {
/// Threshold cyclomatic complexity level, exceeding it triggers a warning.
final int maxComplexity;

/// A list of methods that should be excluded from the lint.
final ExcludedIdentifiersListParameter exclude;

/// Reference: NIST 500-235 item 2.5
static const _defaultMaxComplexity = 10;

/// Constructor for [CyclomaticComplexityParameters] model
const CyclomaticComplexityParameters({
required this.maxComplexity,
required this.exclude,
});

/// Method for creating from json data
factory CyclomaticComplexityParameters.fromJson(Map<String, Object?> json) =>
CyclomaticComplexityParameters(
maxComplexity: json['max_complexity'] as int? ?? _defaultMaxComplexity,
exclude: ExcludedIdentifiersListParameter.defaultFromJson(json),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,22 @@ class FunctionLinesOfCodeRule
) {
void checkNode(AstNode node) => _checkNode(resolver, reporter, node);

context.registry.addMethodDeclaration(checkNode);
context.registry.addFunctionDeclaration(checkNode);
context.registry.addFunctionExpression(checkNode);
context.registry.addDeclaration((declarationNode) {
final isIgnored = config.parameters.exclude.shouldIgnore(declarationNode);

if (isIgnored) return;

context.registry.addMethodDeclaration(checkNode);
context.registry.addFunctionDeclaration(checkNode);
context.registry.addFunctionExpression(checkNode);
});
}

void _checkNode(
CustomLintResolver resolver,
ErrorReporter reporter,
AstNode node,
) {
final functionName = _getFunctionName(node);
if (functionName != null &&
config.parameters.excludeNames.contains(functionName)) {
return;
}

final visitor = FunctionLinesOfCodeVisitor(resolver.lineInfo);
node.visitChildren(visitor);

Expand All @@ -84,16 +84,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,27 @@
import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart';

/// A data model class that represents the "function lines of code" input
/// 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;
/// A list of methods that should be excluded from the lint.
final ExcludedIdentifiersListParameter exclude;

static const _defaultMaxLines = 200;

/// Constructor for [FunctionLinesOfCodeParameters] model
const FunctionLinesOfCodeParameters({
required this.maxLines,
required this.excludeNames,
required this.exclude,
});

/// 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? ?? []),
exclude: ExcludedIdentifiersListParameter.defaultFromJson(json),
yurii-prykhodko-solid marked this conversation as resolved.
Show resolved Hide resolved
);
}
20 changes: 20 additions & 0 deletions lib/src/lints/no_empty_block/models/no_empty_block_parameters.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart';

/// A data model class that represents the "no empty block" lint input
/// parameters.
class NoEmptyBlockParameters {
/// A list of methods that should be excluded from the lint.
final ExcludedIdentifiersListParameter exclude;

/// Constructor for [NoEmptyBlockParameters] model
NoEmptyBlockParameters({
required this.exclude,
});

/// Method for creating from json data
factory NoEmptyBlockParameters.fromJson(Map<String, dynamic> json) {
return NoEmptyBlockParameters(
exclude: ExcludedIdentifiersListParameter.defaultFromJson(json),
);
}
}
9 changes: 7 additions & 2 deletions lib/src/lints/no_empty_block/no_empty_block_rule.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/src/lints/no_empty_block/models/no_empty_block_parameters.dart';
import 'package:solid_lints/src/lints/no_empty_block/visitors/no_empty_block_visitor.dart';
import 'package:solid_lints/src/models/rule_config.dart';
import 'package:solid_lints/src/models/solid_lint_rule.dart';
Expand Down Expand Up @@ -49,7 +50,7 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart';
/// } catch (_) {} // ignored by this rule
/// }
/// ```
class NoEmptyBlockRule extends SolidLintRule {
class NoEmptyBlockRule extends SolidLintRule<NoEmptyBlockParameters> {
/// This lint rule represents
/// the error whether left empty block.
static const String lintName = 'no_empty_block';
Expand All @@ -62,6 +63,7 @@ class NoEmptyBlockRule extends SolidLintRule {
final config = RuleConfig(
configs: configs,
name: lintName,
paramsParser: NoEmptyBlockParameters.fromJson,
problemMessage: (_) =>
'Block is empty. Empty blocks are often indicators of missing code.',
);
Expand All @@ -75,7 +77,10 @@ class NoEmptyBlockRule extends SolidLintRule {
ErrorReporter reporter,
CustomLintContext context,
) {
context.registry.addCompilationUnit((node) {
context.registry.addDeclaration((node) {
final isIgnored = config.parameters.exclude.shouldIgnore(node);
if (isIgnored) return;

final visitor = NoEmptyBlockVisitor();
node.accept(visitor);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart';

/// A data model class that represents the "number of parameters" input
/// parameters.
class NumberOfParametersParameters {
/// Maximum number of parameters allowed before a warning is triggered.
final int maxParameters;

/// A list of methods that should be excluded from the lint.
final ExcludedIdentifiersListParameter exclude;

static const _defaultMaxParameters = 2;

/// Constructor for [NumberOfParametersParameters] model
const NumberOfParametersParameters({
required this.maxParameters,
required this.exclude,
});

/// Method for creating from json data
factory NumberOfParametersParameters.fromJson(Map<String, Object?> json) =>
NumberOfParametersParameters(
maxParameters: json['max_parameters'] as int? ?? _defaultMaxParameters,
exclude: ExcludedIdentifiersListParameter.defaultFromJson(json),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class NumberOfParametersRule
CustomLintContext context,
) {
context.registry.addDeclaration((node) {
final isIgnored = config.parameters.exclude.shouldIgnore(node);
final parameters = switch (node) {
(final MethodDeclaration node) =>
node.parameters?.parameters.length ?? 0,
Expand All @@ -73,7 +74,7 @@ class NumberOfParametersRule
_ => 0,
};

if (parameters > config.parameters.maxParameters) {
if (!isIgnored && parameters > config.parameters.maxParameters) {
reporter.atOffset(
offset: node.firstTokenAfterCommentAndMetadata.offset,
length: node.end,
Expand Down
Loading
Loading