Skip to content

Commit f32d2ae

Browse files
solid-illiaaihistovIllia Aihistov
andauthored
refactor: migrate no_empty_block (#299)
* refactor: migrate no_empty_block * fix: allow multiple preceding comments to satisfy the todo check in no_empty_block rule * docs: add configuration examples to no_empty_block rule documentation * test: rename excluded method in no_empty_block_rule_test --------- Co-authored-by: Illia Aihistov <illia.aihistov-us@solid.software>
1 parent 56125c8 commit f32d2ae

6 files changed

Lines changed: 283 additions & 125 deletions

File tree

lib/main.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:solid_lints/src/lints/cyclomatic_complexity/cyclomatic_complexit
1414
import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart';
1515
import 'package:solid_lints/src/lints/double_literal_format/fixes/double_literal_format_fix.dart';
1616
import 'package:solid_lints/src/lints/function_lines_of_code/function_lines_of_code_rule.dart';
17+
import 'package:solid_lints/src/lints/no_empty_block/no_empty_block_rule.dart';
1718
import 'package:solid_lints/src/lints/prefer_first/fixes/prefer_first_fix.dart';
1819
import 'package:solid_lints/src/lints/prefer_first/prefer_first_rule.dart';
1920
import 'package:solid_lints/src/lints/proper_super_calls/proper_super_calls_rule.dart';
@@ -63,6 +64,9 @@ class SolidLintsPlugin extends Plugin {
6364
CyclomaticComplexityRule(
6465
analysisOptionsLoader: analysisLoader,
6566
),
67+
NoEmptyBlockRule(
68+
analysisOptionsLoader: analysisLoader,
69+
),
6670
UseNearestContextRule(),
6771
preferFirstRule,
6872
// TODO: Add more lint rules and use analysisLoader

lib/src/lints/no_empty_block/models/no_empty_block_parameters.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ class NoEmptyBlockParameters {
1717
required this.allowWithComments,
1818
});
1919

20+
/// Empty [NoEmptyBlockParameters] model, excludes nothing.
21+
factory NoEmptyBlockParameters.empty() {
22+
return NoEmptyBlockParameters(
23+
exclude: ExcludedIdentifiersListParameter(exclude: []),
24+
allowWithComments: false,
25+
);
26+
}
27+
2028
/// Method for creating from json data
2129
factory NoEmptyBlockParameters.fromJson(Map<String, dynamic> json) {
2230
return NoEmptyBlockParameters(

lib/src/lints/no_empty_block/no_empty_block_rule.dart

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import 'package:analyzer/error/listener.dart';
2-
import 'package:custom_lint_builder/custom_lint_builder.dart';
1+
import 'package:analyzer/analysis_rule/rule_context.dart';
2+
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
3+
import 'package:analyzer/error/error.dart';
34
import 'package:solid_lints/src/lints/no_empty_block/models/no_empty_block_parameters.dart';
45
import 'package:solid_lints/src/lints/no_empty_block/visitors/no_empty_block_visitor.dart';
5-
import 'package:solid_lints/src/models/rule_config.dart';
66
import 'package:solid_lints/src/models/solid_lint_rule.dart';
77

88
// Inspired by TSLint (https://palantir.github.io/tslint/rules/no-empty/)
@@ -13,6 +13,20 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart';
1313
///
1414
/// An empty code block often indicates missing code.
1515
///
16+
/// ### Example config:
17+
///
18+
/// ```yaml
19+
/// plugins:
20+
/// solid_lints:
21+
/// diagnostics:
22+
/// no_empty_block:
23+
/// allow_with_comments: true
24+
/// exclude:
25+
/// - method_name: build
26+
/// - class_name: MyClass
27+
/// method_name: build
28+
/// ```
29+
///
1630
/// ### Example
1731
///
1832
/// #### BAD:
@@ -55,40 +69,39 @@ class NoEmptyBlockRule extends SolidLintRule<NoEmptyBlockParameters> {
5569
/// the error whether left empty block.
5670
static const String lintName = 'no_empty_block';
5771

58-
NoEmptyBlockRule._(super.config);
72+
static const _code = LintCode(
73+
lintName,
74+
'Block is empty. Empty blocks are often indicators of missing code.',
75+
);
5976

60-
/// Creates a new instance of [NoEmptyBlockRule]
61-
/// based on the lint configuration.
62-
factory NoEmptyBlockRule.createRule(CustomLintConfigs configs) {
63-
final config = RuleConfig(
64-
configs: configs,
65-
name: lintName,
66-
paramsParser: NoEmptyBlockParameters.fromJson,
67-
problemMessage: (_) =>
68-
'Block is empty. Empty blocks are often indicators of missing code.',
69-
);
77+
@override
78+
DiagnosticCode get diagnosticCode => _code;
7079

71-
return NoEmptyBlockRule._(config);
72-
}
80+
/// Creates a new instance of [NoEmptyBlockRule]
81+
NoEmptyBlockRule({
82+
required super.analysisOptionsLoader,
83+
}) : super.withParameters(
84+
name: lintName,
85+
description: _code.problemMessage,
86+
parametersParser: NoEmptyBlockParameters.fromJson,
87+
);
7388

7489
@override
75-
void run(
76-
CustomLintResolver resolver,
77-
DiagnosticReporter reporter,
78-
CustomLintContext context,
90+
void registerNodeProcessors(
91+
RuleVisitorRegistry registry,
92+
RuleContext context,
7993
) {
80-
context.registry.addDeclaration((node) {
81-
final isIgnored = config.parameters.exclude.shouldIgnore(node);
82-
if (isIgnored) return;
94+
super.registerNodeProcessors(registry, context);
8395

84-
final visitor = NoEmptyBlockVisitor(
85-
allowWithComments: config.parameters.allowWithComments,
86-
);
87-
node.accept(visitor);
96+
final parameters =
97+
getParametersForContext(context) ?? NoEmptyBlockParameters.empty();
98+
99+
final visitor = NoEmptyBlockVisitor(
100+
rule: this,
101+
allowWithComments: parameters.allowWithComments,
102+
exclude: parameters.exclude,
103+
);
88104

89-
for (final emptyBlock in visitor.emptyBlocks) {
90-
reporter.atNode(emptyBlock, code);
91-
}
92-
});
105+
registry.addCompilationUnit(this, visitor);
93106
}
94107
}

lib/src/lints/no_empty_block/visitors/no_empty_block_visitor.dart

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,29 @@
2121
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2222
// SOFTWARE.
2323

24+
import 'package:analyzer/analysis_rule/analysis_rule.dart';
2425
import 'package:analyzer/dart/ast/ast.dart';
26+
import 'package:analyzer/dart/ast/token.dart';
2527
import 'package:analyzer/dart/ast/visitor.dart';
28+
import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart';
2629

2730
const _todoComment = 'TODO';
2831

2932
/// The AST visitor that will find all empty blocks, excluding catch blocks
3033
/// and blocks containing [_todoComment]
3134
class NoEmptyBlockVisitor extends RecursiveAstVisitor<void> {
35+
final AnalysisRule _rule;
3236
final bool _allowWithComments;
33-
34-
final _emptyBlocks = <Block>[];
37+
final ExcludedIdentifiersListParameter _exclude;
3538

3639
/// Constructor for [NoEmptyBlockVisitor]
37-
/// [_allowWithComments] indicates whether to allow empty blocks that contain
38-
/// any comments
39-
NoEmptyBlockVisitor({required bool allowWithComments})
40-
: _allowWithComments = allowWithComments;
41-
42-
/// All empty blocks
43-
Iterable<Block> get emptyBlocks => _emptyBlocks;
40+
NoEmptyBlockVisitor({
41+
required AnalysisRule rule,
42+
required bool allowWithComments,
43+
required ExcludedIdentifiersListParameter exclude,
44+
}) : _rule = rule,
45+
_allowWithComments = allowWithComments,
46+
_exclude = exclude;
4447

4548
@override
4649
void visitBlock(Block node) {
@@ -51,11 +54,25 @@ class NoEmptyBlockVisitor extends RecursiveAstVisitor<void> {
5154
if (_allowWithComments && _isPrecedingCommentAny(node)) return;
5255
if (_isPrecedingCommentToDo(node)) return;
5356

54-
_emptyBlocks.add(node);
57+
final enclosingDeclaration = node.thisOrAncestorOfType<Declaration>();
58+
if (enclosingDeclaration != null &&
59+
_exclude.shouldIgnore(enclosingDeclaration)) {
60+
return;
61+
}
62+
63+
_rule.reportAtNode(node);
5564
}
5665

57-
static bool _isPrecedingCommentToDo(Block node) =>
58-
node.endToken.precedingComments?.lexeme.contains(_todoComment) ?? false;
66+
static bool _isPrecedingCommentToDo(Block node) {
67+
Token? comment = node.endToken.precedingComments;
68+
while (comment != null) {
69+
if (comment.lexeme.contains(_todoComment)) {
70+
return true;
71+
}
72+
comment = comment.next;
73+
}
74+
return false;
75+
}
5976

6077
static bool _isPrecedingCommentAny(Block node) =>
6178
node.endToken.precedingComments != null;

lint_test/no_empty_block_test.dart

Lines changed: 0 additions & 82 deletions
This file was deleted.

0 commit comments

Comments
 (0)