Skip to content
Open
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
6 changes: 6 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import 'package:solid_lints/src/lints/avoid_unnecessary_type_assertions/avoid_un
import 'package:solid_lints/src/lints/avoid_unnecessary_type_assertions/fixes/avoid_unnecessary_type_assertions_fix.dart';
import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart';
import 'package:solid_lints/src/lints/double_literal_format/fixes/double_literal_format_fix.dart';
import 'package:solid_lints/src/lints/function_lines_of_code/function_lines_of_code_rule.dart';
import 'package:solid_lints/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart';
import 'package:solid_lints/src/lints/proper_super_calls/proper_super_calls_rule.dart';

/// The entry point for the Solid Lints analyser server plugin.
Expand Down Expand Up @@ -47,6 +49,10 @@ class SolidLintsPlugin extends Plugin {
analysisOptionsLoader: analysisLoader,
parametersParser: AvoidReturningWidgetsParameters.fromJson,
),
FunctionLinesOfCodeRule(
analysisOptionsLoader: analysisLoader,
parametersParser: FunctionLinesOfCodeParameters.fromJson,
),
];

for (final lintRule in lintRules) {
Expand Down
115 changes: 38 additions & 77 deletions lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:analyzer/analysis_rule/rule_context.dart';
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
import 'package:analyzer/error/error.dart';
import 'package:solid_lints/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart';
import 'package:solid_lints/src/lints/function_lines_of_code/visitors/function_lines_of_code_visitor.dart';
import 'package:solid_lints/src/models/rule_config.dart';
import 'package:solid_lints/src/lints/function_lines_of_code/visitors/function_lines_of_code_rule_visitor.dart';
import 'package:solid_lints/src/models/solid_lint_rule.dart';

/// An approximate metric of meaningful lines of source code inside a function,
Expand All @@ -12,90 +11,52 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart';
/// ### Example config:
///
/// ```yaml
/// custom_lint:
/// rules:
/// - function_lines_of_code:
/// max_lines: 100
/// excludeNames:
/// - "Build"
/// plugins:
/// solid_lints:
/// diagnostics:
/// function_lines_of_code:
/// max_lines: 100
/// exclude:
/// - "Build"
/// ```
class FunctionLinesOfCodeRule
extends SolidLintRule<FunctionLinesOfCodeParameters> {
/// This lint rule represents the error if number of
/// parameters reaches the maximum value.
/// This lint rule name.
static const lintName = 'function_lines_of_code';

FunctionLinesOfCodeRule._(super.config);

/// Creates a new instance of [FunctionLinesOfCodeRule]
/// based on the lint configuration.
factory FunctionLinesOfCodeRule.createRule(CustomLintConfigs configs) {
final rule = RuleConfig(
configs: configs,
name: lintName,
paramsParser: FunctionLinesOfCodeParameters.fromJson,
problemMessage: (value) =>
'The maximum allowed number of lines is ${value.maxLines}. '
'Try splitting this function into smaller parts.',
);

return FunctionLinesOfCodeRule._(rule);
}
static const _code = LintCode(
lintName,
'The maximum allowed number of lines is {0}. '
'Try splitting this function into smaller parts.',
);

@override
void run(
CustomLintResolver resolver,
DiagnosticReporter reporter,
CustomLintContext context,
) {
void checkNode(AstNode node) => _checkNode(resolver, reporter, node);
DiagnosticCode get diagnosticCode => _code;

void checkDeclarationNode(Declaration node) {
final isIgnored = config.parameters.exclude.shouldIgnore(node);
if (isIgnored) {
return;
}
checkNode(node);
}

// Check for an anonymous function
void checkFunctionExpressionNode(FunctionExpression node) {
// If a FunctionExpression is an immediate child of a FunctionDeclaration
// this means it's a named function, which are already check as part of
// addFunctionDeclaration call.
if (node.parent is FunctionDeclaration) {
return;
}
checkNode(node);
}

context.registry.addFunctionDeclaration(checkDeclarationNode);
context.registry.addMethodDeclaration(checkDeclarationNode);
context.registry.addFunctionExpression(checkFunctionExpressionNode);
}
/// Creates a new instance of [FunctionLinesOfCodeRule]
FunctionLinesOfCodeRule({
required super.analysisOptionsLoader,
required super.parametersParser,
}) : super.withParameters(
name: lintName,
description:
'An approximate metric of meaningful lines of source code '
'inside a function, excluding blank lines and comments.',
);

void _checkNode(
CustomLintResolver resolver,
DiagnosticReporter reporter,
AstNode node,
@override
void registerNodeProcessors(
RuleVisitorRegistry registry,
RuleContext context,
) {
final visitor = FunctionLinesOfCodeVisitor(resolver.lineInfo);
node.visitChildren(visitor);
super.registerNodeProcessors(registry, context);

if (visitor.linesWithCode.length > config.parameters.maxLines) {
if (node is! AnnotatedNode) {
reporter.atNode(node, code);
return;
}
final parameters =
getParametersForContext(context) ??
FunctionLinesOfCodeParameters.empty();

final startOffset = node.firstTokenAfterCommentAndMetadata.offset;
final lengthDifference = startOffset - node.offset;
final visitor = FunctionLinesOfCodeRuleVisitor(this, context, parameters);

reporter.atOffset(
offset: startOffset,
length: node.length - lengthDifference,
diagnosticCode: code,
);
}
registry.addCompilationUnit(this, visitor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ class FunctionLinesOfCodeParameters {
required this.exclude,
});

/// Empty [FunctionLinesOfCodeParameters] model with default max lines.
factory FunctionLinesOfCodeParameters.empty() {
return FunctionLinesOfCodeParameters(
maxLines: _defaultMaxLines,
exclude: ExcludedIdentifiersListParameter(exclude: []),
);
}

/// Method for creating from json data
factory FunctionLinesOfCodeParameters.fromJson(Map<String, Object?> json) =>
FunctionLinesOfCodeParameters(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import 'package:analyzer/analysis_rule/rule_context.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:solid_lints/src/lints/function_lines_of_code/function_lines_of_code_rule.dart';
import 'package:solid_lints/src/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart';
import 'package:solid_lints/src/lints/function_lines_of_code/visitors/function_lines_of_code_visitor.dart';

/// A visitor that reports on functions/methods exceeding the max line limit.
class FunctionLinesOfCodeRuleVisitor extends RecursiveAstVisitor<void> {
final FunctionLinesOfCodeRule _rule;
final RuleContext _context;
final FunctionLinesOfCodeParameters _parameters;

/// Creates a new instance of [FunctionLinesOfCodeRuleVisitor].
FunctionLinesOfCodeRuleVisitor(this._rule, this._context, this._parameters);

@override
void visitFunctionDeclaration(FunctionDeclaration node) {
final isIgnored = _parameters.exclude.shouldIgnore(node);
if (!isIgnored) {
_checkNode(node);
}
super.visitFunctionDeclaration(node);
}

@override
void visitMethodDeclaration(MethodDeclaration node) {
final isIgnored = _parameters.exclude.shouldIgnore(node);
if (!isIgnored) {
_checkNode(node);
}
super.visitMethodDeclaration(node);
}

@override
void visitFunctionExpression(FunctionExpression node) {
if (node.parent is! FunctionDeclaration) {
_checkNode(node);
}
super.visitFunctionExpression(node);
}
Comment thread
solid-illiaaihistov marked this conversation as resolved.

void _checkNode(AstNode node) {
final currentUnit = _context.currentUnit;
if (currentUnit == null) return;

final lineInfo = currentUnit.unit.lineInfo;
final visitor = FunctionLinesOfCodeVisitor(lineInfo);
node.visitChildren(visitor);

if (visitor.linesWithCode.length > _parameters.maxLines) {
final reporter = currentUnit.diagnosticReporter;

if (node is! AnnotatedNode) {
reporter.atNode(
node,
_rule.diagnosticCode,
arguments: [_parameters.maxLines],
);
return;
}

final startOffset = node.firstTokenAfterCommentAndMetadata.offset;
final lengthDifference = startOffset - node.offset;

reporter.atOffset(
offset: startOffset,
length: node.length - lengthDifference,
diagnosticCode: _rule.diagnosticCode,
arguments: [_parameters.maxLines],
);
}
}
}
14 changes: 0 additions & 14 deletions lint_test/function_lines_of_code_test/analysis_options.yaml

This file was deleted.

Loading
Loading