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 124 #187

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
/// Model class for AvoidReturningWidgetsExclude parameters
class AvoidReturningWidgetsExclude {
/// Model class for ExcludeRule parameters
class ExcludedIdentifierParameter {
/// The name of the method that should be excluded from the lint.
final String methodName;

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

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

///
factory AvoidReturningWidgetsExclude.fromJson(
factory ExcludedIdentifierParameter.fromJson(
Map<dynamic, dynamic> json,
) {
return AvoidReturningWidgetsExclude(
return ExcludedIdentifierParameter(
methodName: json['method_name'] as String,
className: json['class_name'] as String?,
);
Expand Down
78 changes: 78 additions & 0 deletions lib/src/common/parameters/excluded_identifiers_list_parameter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:collection/collection.dart';
import 'package:solid_lints/src/common/parameters/excluded_identifier_parameter.dart';

/// A model representing "exclude" parameters for linting, defining
/// identifiers (classes, methods, functions) to be ignored during analysis.
class ExcludedIdentifiersListParameter {
/// A list of identifiers (classes, methods, functions) that should be
/// excluded from the lint.
final List<ExcludedIdentifierParameter> exclude;

/// A common parameter key for analysis_options.yaml
static const String excludeParameterName = 'exclude';

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

/// Method for creating from json data
factory ExcludedIdentifiersListParameter.fromJson({
required Iterable<dynamic> excludeList,
}) {
final exclude = <ExcludedIdentifierParameter>[];

for (final item in excludeList) {
if (item is Map) {
exclude.add(ExcludedIdentifierParameter.fromJson(item));
}
}
return ExcludedIdentifiersListParameter(
exclude: exclude,
);
}

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

final excludeList =
json[ExcludedIdentifiersListParameter.excludeParameterName]
as Iterable? ??
[];

for (final item in excludeList) {
if (item is Map) {
exclude.add(ExcludedIdentifierParameter.fromJson(item));
}
}
return ExcludedIdentifiersListParameter(
exclude: exclude,
);
}

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

final excludedItem = exclude.firstWhereOrNull(
(e) => e.methodName == methodName || e.className == methodName,
);

if (excludedItem == null) return false;

final className = excludedItem.className;

if (className == null || node is! MethodDeclaration) {
return true;
} else {
final classDeclaration = node.thisOrAncestorOfType<ClassDeclaration>();

return classDeclaration != null &&
classDeclaration.name.toString() == className;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_parameters.dart';
import 'package:solid_lints/src/models/rule_config.dart';
Expand Down Expand Up @@ -96,7 +95,7 @@ class AvoidReturningWidgetsRule

final isWidgetReturned = hasWidgetType(returnType);

final isIgnored = _shouldIgnore(node);
final isIgnored = config.parameters.exclude.shouldIgnore(node);

final isOverriden = node.declaredElement?.hasOverride ?? false;

Expand All @@ -105,25 +104,4 @@ class AvoidReturningWidgetsRule
}
});
}

bool _shouldIgnore(Declaration node) {
final methodName = node.declaredElement?.name;

final excludedItem = config.parameters.exclude
.firstWhereOrNull((e) => e.methodName == methodName);

if (excludedItem == null) return false;

final className = excludedItem.className;

if (className == null || node is! MethodDeclaration) {
return true;
} else {
final classDeclaration = node.thisOrAncestorOfType<ClassDeclaration>();

if (classDeclaration == null) return false;

return classDeclaration.name.toString() == className;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'package:solid_lints/src/lints/avoid_returning_widgets/models/avoid_returning_widgets_exclude.dart';
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 AvoidReturningWidgetsParameters {
/// A list of methods that should be excluded from the lint.
final List<AvoidReturningWidgetsExclude> exclude;
final ExcludedIdentifiersListParameter exclude;

/// Constructor for [AvoidReturningWidgetsParameters] model
AvoidReturningWidgetsParameters({
Expand All @@ -13,16 +13,8 @@ class AvoidReturningWidgetsParameters {

/// Method for creating from json data
factory AvoidReturningWidgetsParameters.fromJson(Map<String, dynamic> json) {
final exclude = <AvoidReturningWidgetsExclude>[];

final excludeList = json['exclude'] as Iterable? ?? [];
for (final item in excludeList) {
if (item is Map) {
exclude.add(AvoidReturningWidgetsExclude.fromJson(item));
}
}
return AvoidReturningWidgetsParameters(
exclude: exclude,
exclude: ExcludedIdentifiersListParameter.defaultFromJson(json),
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@

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 +66,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 +81,7 @@ class AvoidUnusedParametersRule extends SolidLintRule {
final rule = RuleConfig(
configs: configs,
name: lintName,
paramsParser: AvoidUnusedParameters.fromJson,
problemMessage: (_) => 'Parameter is unused.',
);

Expand All @@ -91,12 +94,16 @@ class AvoidUnusedParametersRule extends SolidLintRule {
ErrorReporter reporter,
CustomLintContext context,
) {
context.registry.addCompilationUnit((node) {
final visitor = AvoidUnusedParametersVisitor();
node.accept(visitor);
context.registry.addDeclaration((node) {
final isIgnored = config.parameters.exclude.shouldIgnore(node);

if (!isIgnored) {
final visitor = AvoidUnusedParametersVisitor();
node.accept(visitor);

for (final element in visitor.unusedParameters) {
reporter.atNode(element, code);
for (final element in visitor.unusedParameters) {
reporter.atNode(element, code);
}
}
});
}
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) {
final visitor = CyclomaticComplexityFlowVisitor();
node.visitChildren(visitor);

if (visitor.complexityEntities.length + 1 >
config.parameters.maxComplexity) {
reporter.atNode(node, code);
}
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) {
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),
);
}
Loading
Loading