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

Cleanup of lint creation #41

Merged
merged 2 commits into from
Aug 14, 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
19 changes: 16 additions & 3 deletions lib/lints/avoid_global_state/avoid_global_state_rule.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/models/metric_rule.dart';
import 'package:solid_lints/models/solid_lint_rule.dart';

/// A global state rule which forbids using variables
/// that can be globally modified.
class AvoidGlobalStateRule extends DartLintRule {
class AvoidGlobalStateRule extends SolidLintRule {
/// The [LintCode] of this lint rule that represents
/// the error whether we use global state.
static const lintName = 'avoid_global_state';

/// Creates a new instance of [AvoidGlobalStateRule].
const AvoidGlobalStateRule({required super.code});
AvoidGlobalStateRule._(super.config);

/// Creates a new instance of [AvoidGlobalStateRule]
/// based on the lint configuration.
factory AvoidGlobalStateRule.createRule(CustomLintConfigs configs) {
final rule = MetricRule(
configs: configs,
name: lintName,
problemMessage: (_) => 'Avoid variables that can be globally mutated.',
);

return AvoidGlobalStateRule._(rule);
}

@override
void run(
Expand Down
21 changes: 18 additions & 3 deletions lib/lints/avoid_late_keyword/avoid_late_keyword_rule.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/models/metric_rule.dart';
import 'package:solid_lints/models/solid_lint_rule.dart';

/// A `late` keyword rule which forbids using it to avoid runtime exceptions.
class AvoidLateKeywordRule extends DartLintRule {
class AvoidLateKeywordRule extends SolidLintRule {
/// The [LintCode] of this lint rule that represents
/// the error whether we use `late` keyword.
static const lintName = 'avoid_late_keyword';

/// Creates a new instance of [AvoidLateKeywordRule].
const AvoidLateKeywordRule({required super.code});
AvoidLateKeywordRule._(super.config);

/// Creates a new instance of [AvoidLateKeywordRule]
/// based on the lint configuration.
factory AvoidLateKeywordRule.createRule(CustomLintConfigs configs) {
final rule = MetricRule(
configs: configs,
name: lintName,
problemMessage: (_) => ''
'Avoid using the "late" keyword. '
'It may result in runtime exceptions.',
);

return AvoidLateKeywordRule._(rule);
}

@override
void run(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,31 @@ import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/models/metric_rule.dart';
import 'package:solid_lints/models/solid_lint_rule.dart';

/// Rule which forbids using bang operator ("!")
/// as it may result in runtime exceptions.
class AvoidNonNullAssertionRule extends DartLintRule {
class AvoidNonNullAssertionRule extends SolidLintRule {
/// The [LintCode] of this lint rule that represents
/// the error whether we use bang operator.
static const lintName = 'avoid_non_null_assertion';

/// Creates a new instance of [AvoidNonNullAssertionRule].
const AvoidNonNullAssertionRule({required super.code});
AvoidNonNullAssertionRule._(super.config);

/// Creates a new instance of [AvoidNonNullAssertionRule]
/// based on the lint configuration.
factory AvoidNonNullAssertionRule.createRule(CustomLintConfigs configs) {
final rule = MetricRule(
configs: configs,
name: lintName,
problemMessage: (_) => ''
'Avoid using the bang operator. '
'It may result in runtime exceptions.',
);

return AvoidNonNullAssertionRule._(rule);
}

@override
void run(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
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/models/metric_rule.dart';
import 'package:solid_lints/models/solid_lint_rule.dart';
import 'package:solid_lints/utils/types_utils.dart';

/// A rule which forbids returning widgets from functions and methods.
class AvoidReturningWidgetsRule extends DartLintRule {
class AvoidReturningWidgetsRule extends SolidLintRule {
/// The [LintCode] of this lint rule that represents
/// the error whether we return a widget.
static const lintName = 'avoid_returning_widgets';

/// Creates a new instance of [AvoidReturningWidgetsRule].
const AvoidReturningWidgetsRule({required super.code});
AvoidReturningWidgetsRule._(super.config);

/// Creates a new instance of [AvoidReturningWidgetsRule]
/// based on the lint configuration.
factory AvoidReturningWidgetsRule.createRule(CustomLintConfigs configs) {
final rule = MetricRule(
Comment on lines +18 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems wrong semantically. AvoidReturningWidgetsRule isn't a metric, but uses a MetricRule for configuring itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think merging SolidLintRule and MetricRule into one thing and calling it SolidLint would be acceptable? Both rules and metrics are lints for us at the end of the day, so is it good?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. At the very least we can explore it. If it comes together logically -- why not.

configs: configs,
name: lintName,
problemMessage: (_) => ''
'Returning a widget from a function is considered an anti-pattern. '
'Extract your widget to a separate class.',
);

return AvoidReturningWidgetsRule._(rule);
}

@override
void run(
Expand Down
23 changes: 18 additions & 5 deletions lib/lints/cyclomatic_complexity/cyclomatic_complexity_metric.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,31 @@ import 'package:custom_lint_builder/custom_lint_builder.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';
import 'package:solid_lints/models/solid_lint_rule.dart';

/// A Complexity metric checks content of block and detects more easier solution
class CyclomaticComplexityMetric extends DartLintRule {
class CyclomaticComplexityMetric
extends SolidLintRule<CyclomaticComplexityParameters> {
/// The [LintCode] of this lint rule that represents the error if complexity
/// reaches maximum value.
static const lintName = 'cyclomatic_complexity';

/// Configuration for complexity metric rule.
final MetricRule<CyclomaticComplexityParameters> config;
CyclomaticComplexityMetric._(super.rule);

/// Creates a new instance of [CyclomaticComplexityMetric].
CyclomaticComplexityMetric(this.config) : super(code: config.lintCode);
/// Creates a new instance of [CyclomaticComplexityMetric]
/// based on the lint configuration.
factory CyclomaticComplexityMetric.createRule(CustomLintConfigs configs) {
final rule = MetricRule(
configs: configs,
name: lintName,
factory: CyclomaticComplexityParameters.fromJson,
problemMessage: (value) => ''
'The maximum allowed complexity of a function is '
'${value.maxComplexity}. Please decrease it.',
);

return CyclomaticComplexityMetric._(rule);
}

@override
void run(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,32 @@ 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';
import 'package:solid_lints/models/solid_lint_rule.dart';

/// A number of lines metric which checks whether we didn't exceed
/// the maximum allowed number of lines for a function.
class FunctionLinesOfCodeMetric extends DartLintRule {
class FunctionLinesOfCodeMetric
extends SolidLintRule<FunctionLinesOfCodeParameters> {
/// 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;
FunctionLinesOfCodeMetric._(super.config);

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

return FunctionLinesOfCodeMetric._(rule);
}

@override
void run(
Expand Down
23 changes: 18 additions & 5 deletions lib/lints/number_of_parameters/number_of_parameters_metric.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,32 @@ 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/models/solid_lint_rule.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
class NumberOfParametersMetric extends DartLintRule {
class NumberOfParametersMetric
extends SolidLintRule<NumberOfParametersParameters> {
/// The [LintCode] of this lint rule that represents the error if number of
/// parameters reaches the maximum value.
static const lintName = 'number_of_parameters';

/// Configuration for number of parameters metric rule.
final MetricRule<NumberOfParametersParameters> config;
NumberOfParametersMetric._(super.rule);

/// Creates a new instance of [NumberOfParametersMetric].
NumberOfParametersMetric(this.config) : super(code: config.lintCode);
/// Creates a new instance of [NumberOfParametersMetric]
/// based on the lint configuration.
factory NumberOfParametersMetric.createRule(CustomLintConfigs configs) {
final rule = MetricRule(
configs: configs,
name: lintName,
factory: NumberOfParametersParameters.fromJson,
problemMessage: (value) => ''
'The maximum allowed number of parameters is ${value.maxParameters}. '
'Try reducing the number of parameters.',
);

return NumberOfParametersMetric._(rule);
}

@override
void run(
Expand Down
16 changes: 16 additions & 0 deletions lib/models/solid_lint_rule.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:solid_lints/models/metric_rule.dart';

/// A base class for emitting information about
/// issues with user's `.dart` files.
abstract class SolidLintRule<T extends Object?> extends DartLintRule {
/// Constructor for [SolidLintRule] model.
SolidLintRule(this.config) : super(code: config.lintCode);

/// Configuration for a particular rule with all the
/// defined custom parameters.
final MetricRule<T> config;
Comment on lines +6 to +12
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should merge SolidLintRule and MetricRule into a single entity? This would simplify the declaration of these rules also -- it will just be an override on the rule implementation.


/// A flag which indicates whether this rule was enabled by the user.
bool get enabled => config.enabled;
}
113 changes: 15 additions & 98 deletions lib/solid_lints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,111 +6,28 @@ import 'package:solid_lints/lints/avoid_late_keyword/avoid_late_keyword_rule.dar
import 'package:solid_lints/lints/avoid_non_null_assertion/avoid_non_null_assertion_rule.dart';
import 'package:solid_lints/lints/avoid_returning_widgets/avoid_returning_widgets_rule.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/models/solid_lint_rule.dart';

/// creates plugin
/// Creates a plugin for our custom linter
PluginBase createPlugin() => _SolidLints();

/// Solid metric linter
/// Initialize custom solid lints
class _SolidLints extends PluginBase {
@override
List<LintRule> getLintRules(CustomLintConfigs configs) {
final List<LintRule> rules = [];

final cyclomaticComplexity = MetricRule<CyclomaticComplexityParameters>(
configs: configs,
name: CyclomaticComplexityMetric.lintName,
factory: CyclomaticComplexityParameters.fromJson,
problemMessage: (value) => ''
'The maximum allowed complexity of a function is '
'${value.maxComplexity}. Please decrease it.',
);

if (cyclomaticComplexity.enabled) {
rules.add(CyclomaticComplexityMetric(cyclomaticComplexity));
}

final numberOfParameters = MetricRule<NumberOfParametersParameters>(
configs: configs,
name: NumberOfParametersMetric.lintName,
factory: NumberOfParametersParameters.fromJson,
problemMessage: (value) => ''
'The maximum allowed number of parameters is ${value.maxParameters}. '
'Try reducing the number of parameters.',
);

if (numberOfParameters.enabled) {
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));
}

final avoidNonNullAssertion = MetricRule(
configs: configs,
name: AvoidNonNullAssertionRule.lintName,
problemMessage: (_) => ''
'Avoid using the bang operator. '
'It may result in runtime exceptions.',
);

if (avoidNonNullAssertion.enabled) {
rules.add(
AvoidNonNullAssertionRule(code: avoidNonNullAssertion.lintCode),
);
}

final avoidLateKeyword = MetricRule(
configs: configs,
name: AvoidLateKeywordRule.lintName,
problemMessage: (_) => ''
'Avoid using the "late" keyword. '
'It may result in runtime exceptions.',
);

if (avoidLateKeyword.enabled) {
rules.add(AvoidLateKeywordRule(code: avoidLateKeyword.lintCode));
}

final avoidGlobalState = MetricRule(
configs: configs,
name: AvoidGlobalStateRule.lintName,
problemMessage: (_) => 'Avoid variables that can be globally mutated.',
);

if (avoidGlobalState.enabled) {
rules.add(AvoidGlobalStateRule(code: avoidGlobalState.lintCode));
}

final avoidReturningWidgets = MetricRule(
configs: configs,
name: AvoidReturningWidgetsRule.lintName,
problemMessage: (_) => ''
'Returning a widget from a function is considered an anti-pattern. '
'Extract your widget to a separate class.',
);

if (avoidReturningWidgets.enabled) {
rules.add(
AvoidReturningWidgetsRule(code: avoidReturningWidgets.lintCode),
);
}

return rules;
final List<SolidLintRule> supportedRules = [
CyclomaticComplexityMetric.createRule(configs),
NumberOfParametersMetric.createRule(configs),
FunctionLinesOfCodeMetric.createRule(configs),
AvoidNonNullAssertionRule.createRule(configs),
AvoidLateKeywordRule.createRule(configs),
AvoidGlobalStateRule.createRule(configs),
AvoidReturningWidgetsRule.createRule(configs),
];

// Return only enabled rules
return supportedRules.where((r) => r.enabled).toList();
n-bernat marked this conversation as resolved.
Show resolved Hide resolved
}
}