Skip to content
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
4 changes: 4 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'package:solid_lints/src/lints/cyclomatic_complexity/cyclomatic_complexit
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/no_empty_block/no_empty_block_rule.dart';
import 'package:solid_lints/src/lints/prefer_first/fixes/prefer_first_fix.dart';
import 'package:solid_lints/src/lints/prefer_first/prefer_first_rule.dart';
import 'package:solid_lints/src/lints/proper_super_calls/proper_super_calls_rule.dart';
Expand Down Expand Up @@ -63,6 +64,9 @@ class SolidLintsPlugin extends Plugin {
CyclomaticComplexityRule(
analysisOptionsLoader: analysisLoader,
),
NoEmptyBlockRule(
analysisOptionsLoader: analysisLoader,
),
UseNearestContextRule(),
preferFirstRule,
// TODO: Add more lint rules and use analysisLoader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ class NoEmptyBlockParameters {
required this.allowWithComments,
});

/// Empty [NoEmptyBlockParameters] model, excludes nothing.
factory NoEmptyBlockParameters.empty() {
return NoEmptyBlockParameters(
exclude: ExcludedIdentifiersListParameter(exclude: []),
allowWithComments: false,
);
}

/// Method for creating from json data
factory NoEmptyBlockParameters.fromJson(Map<String, dynamic> json) {
return NoEmptyBlockParameters(
Expand Down
75 changes: 44 additions & 31 deletions lib/src/lints/no_empty_block/no_empty_block_rule.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
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/no_empty_block/models/no_empty_block_parameters.dart';
import 'package:solid_lints/src/lints/no_empty_block/visitors/no_empty_block_visitor.dart';
import 'package:solid_lints/src/models/rule_config.dart';
import 'package:solid_lints/src/models/solid_lint_rule.dart';

// Inspired by TSLint (https://palantir.github.io/tslint/rules/no-empty/)
Expand All @@ -13,6 +13,20 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart';
///
/// An empty code block often indicates missing code.
///
/// ### Example config:
///
/// ```yaml
/// plugins:
/// solid_lints:
/// diagnostics:
/// no_empty_block:
/// allow_with_comments: true
/// exclude:
/// - method_name: build
/// - class_name: MyClass
/// method_name: build
/// ```
///
/// ### Example
///
/// #### BAD:
Expand Down Expand Up @@ -55,40 +69,39 @@ class NoEmptyBlockRule extends SolidLintRule<NoEmptyBlockParameters> {
/// the error whether left empty block.
static const String lintName = 'no_empty_block';

NoEmptyBlockRule._(super.config);
static const _code = LintCode(
lintName,
'Block is empty. Empty blocks are often indicators of missing code.',
);

/// Creates a new instance of [NoEmptyBlockRule]
/// based on the lint configuration.
factory NoEmptyBlockRule.createRule(CustomLintConfigs configs) {
final config = RuleConfig(
configs: configs,
name: lintName,
paramsParser: NoEmptyBlockParameters.fromJson,
problemMessage: (_) =>
'Block is empty. Empty blocks are often indicators of missing code.',
);
@override
DiagnosticCode get diagnosticCode => _code;

return NoEmptyBlockRule._(config);
}
/// Creates a new instance of [NoEmptyBlockRule]
NoEmptyBlockRule({
required super.analysisOptionsLoader,
}) : super.withParameters(
name: lintName,
description: _code.problemMessage,
parametersParser: NoEmptyBlockParameters.fromJson,
);

@override
void run(
CustomLintResolver resolver,
DiagnosticReporter reporter,
CustomLintContext context,
void registerNodeProcessors(
RuleVisitorRegistry registry,
RuleContext context,
) {
context.registry.addDeclaration((node) {
final isIgnored = config.parameters.exclude.shouldIgnore(node);
if (isIgnored) return;
super.registerNodeProcessors(registry, context);

final visitor = NoEmptyBlockVisitor(
allowWithComments: config.parameters.allowWithComments,
);
node.accept(visitor);
final parameters =
getParametersForContext(context) ?? NoEmptyBlockParameters.empty();

final visitor = NoEmptyBlockVisitor(
rule: this,
allowWithComments: parameters.allowWithComments,
exclude: parameters.exclude,
);

for (final emptyBlock in visitor.emptyBlocks) {
reporter.atNode(emptyBlock, code);
}
});
registry.addCompilationUnit(this, visitor);
}
}
41 changes: 29 additions & 12 deletions lib/src/lints/no_empty_block/visitors/no_empty_block_visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,29 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import 'package:analyzer/analysis_rule/analysis_rule.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:solid_lints/src/common/parameters/excluded_identifiers_list_parameter.dart';

const _todoComment = 'TODO';

/// The AST visitor that will find all empty blocks, excluding catch blocks
/// and blocks containing [_todoComment]
class NoEmptyBlockVisitor extends RecursiveAstVisitor<void> {
final AnalysisRule _rule;
final bool _allowWithComments;

final _emptyBlocks = <Block>[];
final ExcludedIdentifiersListParameter _exclude;

/// Constructor for [NoEmptyBlockVisitor]
/// [_allowWithComments] indicates whether to allow empty blocks that contain
/// any comments
NoEmptyBlockVisitor({required bool allowWithComments})
: _allowWithComments = allowWithComments;

/// All empty blocks
Iterable<Block> get emptyBlocks => _emptyBlocks;
NoEmptyBlockVisitor({
required AnalysisRule rule,
required bool allowWithComments,
required ExcludedIdentifiersListParameter exclude,
}) : _rule = rule,
_allowWithComments = allowWithComments,
_exclude = exclude;

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

_emptyBlocks.add(node);
final enclosingDeclaration = node.thisOrAncestorOfType<Declaration>();
if (enclosingDeclaration != null &&
_exclude.shouldIgnore(enclosingDeclaration)) {
return;
}
Comment thread
solid-illiaaihistov marked this conversation as resolved.

_rule.reportAtNode(node);
}

static bool _isPrecedingCommentToDo(Block node) =>
node.endToken.precedingComments?.lexeme.contains(_todoComment) ?? false;
static bool _isPrecedingCommentToDo(Block node) {
Token? comment = node.endToken.precedingComments;
while (comment != null) {
if (comment.lexeme.contains(_todoComment)) {
return true;
}
comment = comment.next;
}
return false;
}
Comment thread
solid-illiaaihistov marked this conversation as resolved.

static bool _isPrecedingCommentAny(Block node) =>
node.endToken.precedingComments != null;
Expand Down
82 changes: 0 additions & 82 deletions lint_test/no_empty_block_test.dart

This file was deleted.

Loading
Loading