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

Add linter for lines of code #34

Merged
merged 4 commits into from
Aug 10, 2023
Merged
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
2 changes: 2 additions & 0 deletions example/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ custom_lint:
max_complexity: 4
- number_of_parameters:
max_parameters: 2
- function_lines_of_code:
max_lines: 50
59 changes: 59 additions & 0 deletions example/test/lines_of_code_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:test/test.dart';

/// Check number of lines fail
///
/// `function_lines_of_code: max_lines`
/// expect_lint: function_lines_of_code
void linesOfCode() {
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
test("addition", () {
expect(1 + 1, equals(2));
});
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart';
import 'package:solid_lints/cyclomatic_complexity/visitor/cyclomatic_complexity_flow_visitor.dart';
import 'package:solid_lints/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart';
import 'package:solid_lints/lints/cyclomatic_complexity/visitor/cyclomatic_complexity_flow_visitor.dart';
import 'package:solid_lints/models/metric_rule.dart';

/// A Complexity metric checks content of block and detects more easier solution
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//MIT License
// MIT License
//
// Copyright (c) 2020-2021 Dart Code Checker team
//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart';
import 'package:solid_lints/lints/function_lines_of_code/visitor/function_lines_of_code_visitor.dart';
import 'package:solid_lints/models/metric_rule.dart';

/// A number of lines metric which checks whether we didn't exceed
/// the maximum allowed number of lines for a file
class FunctionLinesOfCodeMetric extends DartLintRule {
/// The [LintCode] of this lint rule that represents the error if number of
/// parameters reaches the maximum value.
static const lintName = 'function_lines_of_code';

/// Configuration for number of parameters metric rule.
final MetricRule<FunctionLinesOfCodeParameters> config;

/// Creates a new instance of [FunctionLinesOfCodeMetric].
FunctionLinesOfCodeMetric(this.config) : super(code: config.lintCode);

@override
void run(
CustomLintResolver resolver,
ErrorReporter reporter,
CustomLintContext context,
) {
final visitor = FunctionLinesOfCodeVisitor(resolver.lineInfo);

context.registry.addDeclaration((node) {
node.visitChildren(visitor);

if (visitor.linesWithCode.length > config.parameters.maxLines) {
reporter.reportErrorForOffset(
code,
node.firstTokenAfterCommentAndMetadata.offset,
node.end,
);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/// A data model class that represents the "source lines of code" input
/// parameters.
class FunctionLinesOfCodeParameters {
/// Maximum number of lines
final int maxLines;

static const _defaultMaxLines = 200;

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

/// Method for creating from json data
factory FunctionLinesOfCodeParameters.fromJson(Map<String, Object?> json) =>
FunctionLinesOfCodeParameters(
maxLines: json['max_lines'] as int? ?? _defaultMaxLines,
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// MIT License
//
// Copyright (c) 2020-2021 Dart Code Checker team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/source/line_info.dart';

/// The AST visitor that will find lines with code.
class FunctionLinesOfCodeVisitor extends RecursiveAstVisitor<void> {
final LineInfo _lineInfo;

final _linesWithCode = <int>{};

/// Returns the array with indices of lines with code.
Iterable<int> get linesWithCode => _linesWithCode;

/// Creates a new instance of [FunctionLinesOfCodeVisitor].
FunctionLinesOfCodeVisitor(this._lineInfo);

@override
void visitBlockFunctionBody(BlockFunctionBody node) {
_collectFunctionBodyData(
node.block.leftBracket.next,
node.block.rightBracket,
);
super.visitBlockFunctionBody(node);
}

@override
void visitExpressionFunctionBody(ExpressionFunctionBody node) {
_collectFunctionBodyData(
node.expression.beginToken.previous,
node.expression.endToken.next,
);
super.visitExpressionFunctionBody(node);
}

void _collectFunctionBodyData(Token? firstToken, Token? lastToken) {
var token = firstToken;
while (token != lastToken && token != null) {
if (!token.isSynthetic) {
_linesWithCode.add(_lineInfo.getLocation(token.offset).lineNumber);
}

token = token.next;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +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:solid_lints/lints/number_of_parameters/models/number_of_parameters_parameters.dart';
import 'package:solid_lints/models/metric_rule.dart';
import 'package:solid_lints/number_of_parameters/models/number_of_parameters_parameters.dart';

/// A number of parameters metric which checks whether we didn't exceed
/// the maximum allowed number of parameters for a function or a method
Expand Down
23 changes: 19 additions & 4 deletions lib/solid_lints.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
library solid_metrics;

import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/cyclomatic_complexity/cyclomatic_complexity_metric.dart';
import 'package:solid_lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart';
import 'package:solid_lints/lints/cyclomatic_complexity/cyclomatic_complexity_metric.dart';
import 'package:solid_lints/lints/cyclomatic_complexity/models/cyclomatic_complexity_parameters.dart';
import 'package:solid_lints/lints/function_lines_of_code/function_lines_of_code_metric.dart';
import 'package:solid_lints/lints/function_lines_of_code/models/function_lines_of_code_parameters.dart';
import 'package:solid_lints/lints/number_of_parameters/models/number_of_parameters_parameters.dart';
import 'package:solid_lints/lints/number_of_parameters/number_of_parameters_metric.dart';
import 'package:solid_lints/models/metric_rule.dart';
import 'package:solid_lints/number_of_parameters/models/number_of_parameters_parameters.dart';
import 'package:solid_lints/number_of_parameters/number_of_parameters_metric.dart';

/// creates plugin
PluginBase createPlugin() => _SolidLints();
Expand Down Expand Up @@ -42,6 +44,19 @@ class _SolidLints extends PluginBase {
rules.add(NumberOfParametersMetric(numberOfParameters));
}

final functionLinesOfCode = MetricRule<FunctionLinesOfCodeParameters>(
configs: configs,
name: FunctionLinesOfCodeMetric.lintName,
factory: FunctionLinesOfCodeParameters.fromJson,
problemMessage: (value) => ''
'The maximum allowed number of lines is ${value.maxLines}. '
'Try splitting this function into smaller parts.',
);

if (functionLinesOfCode.enabled) {
rules.add(FunctionLinesOfCodeMetric(functionLinesOfCode));
}

return rules;
}
}