Skip to content

Commit

Permalink
Merge pull request #149 from byshy/trunk
Browse files Browse the repository at this point in the history
Add no-blank-line-before-single-return rule to the lint analyzers
  • Loading branch information
santitigaga authored Nov 5, 2024
2 parents 82bb5d7 + 8c8867c commit b285f93
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 0 deletions.
1 change: 1 addition & 0 deletions example/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ dart_code_linter:
- avoid-unnecessary-type-casts
- avoid-unused-parameters
- newline-before-return
- no-blank-line-before-single-return
- no-boolean-literal-compare
- no-empty-block
- no-equal-then-else
Expand Down
2 changes: 2 additions & 0 deletions lib/src/analyzers/lint_analyzer/rules/rules_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import 'rules_list/list_all_equatable_fields/list_all_equatable_fields_rule.dart
import 'rules_list/member_ordering/member_ordering_rule.dart';
import 'rules_list/missing_test_assertion/missing_test_assertion_rule.dart';
import 'rules_list/newline_before_return/newline_before_return_rule.dart';
import 'rules_list/no_blank_line_before_single_return/no_blank_line_before_single_return_rule.dart';
import 'rules_list/no_boolean_literal_compare/no_boolean_literal_compare_rule.dart';
import 'rules_list/no_empty_block/no_empty_block_rule.dart';
import 'rules_list/no_equal_arguments/no_equal_arguments_rule.dart';
Expand Down Expand Up @@ -130,6 +131,7 @@ final _implementedRules = <String, Rule Function(Map<String, Object>)>{
MemberOrderingRule.ruleId: MemberOrderingRule.new,
MissingTestAssertionRule.ruleId: MissingTestAssertionRule.new,
NewlineBeforeReturnRule.ruleId: NewlineBeforeReturnRule.new,
NoBlankLineBeforeSingleReturnRule.ruleId: NoBlankLineBeforeSingleReturnRule.new,
NoBooleanLiteralCompareRule.ruleId: NoBooleanLiteralCompareRule.new,
NoEmptyBlockRule.ruleId: NoEmptyBlockRule.new,
NoEqualArgumentsRule.ruleId: NoEqualArgumentsRule.new,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
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';

import '../../../../../utils/node_utils.dart';
import '../../../lint_utils.dart';
import '../../../models/internal_resolved_unit_result.dart';
import '../../../models/issue.dart';
import '../../../models/severity.dart';
import '../../models/dart_rule.dart';
import '../../rule_utils.dart';

part 'visitor.dart';

class NoBlankLineBeforeSingleReturnRule extends DartRule {
static const String ruleId = 'no-blank-line-before-single-return';

static const warning = 'Remove blank line before single return statement in a block.';

NoBlankLineBeforeSingleReturnRule([Map<String, Object> config = const {}])
: super(
id: ruleId,
severity: readSeverity(config, Severity.style),
excludes: readExcludes(config),
includes: readIncludes(config),
);

@override
Iterable<Issue> check(InternalResolvedUnitResult source) {
final visitor = _Visitor();

source.unit.visitChildren(visitor);

return visitor.statements
// Ensure the return statement is in a block
.where((statement) => statement.parent is Block)
// Ensure the return statement is the only statement in the block
.where((statement) {
final parentBlock = statement.parent as Block;

return parentBlock.statements.length == 1;
})
// Ensure there is no blank line before the return statement, ignoring comments
.where((statement) {
final lineInfo = source.lineInfo;

// Get the last non-comment token before the return statement
final previousTokenLine = lineInfo
.getLocation(statement.returnKeyword.previous!.end)
.lineNumber;

final tokenLine = lineInfo
.getLocation(
_optimalToken(statement.returnKeyword, lineInfo).offset,
)
.lineNumber;

return tokenLine != previousTokenLine + 1;
})
.map((statement) => createIssue(
rule: this,
location: nodeLocation(node: statement, source: source),
message: warning,
))
.toList(growable: false);
}
}

Token _optimalToken(Token token, LineInfo lineInfo) {
var optimalToken = token;
var commentToken = _latestCommentToken(token);

while (commentToken != null) {
final commentTokenLineNumber = lineInfo.getLocation(commentToken.end).lineNumber;
final optimalTokenLineNumber = lineInfo.getLocation(optimalToken.offset).lineNumber;

final isDirectlyPrecedingComment = commentTokenLineNumber + 1 >= optimalTokenLineNumber;

if (!isDirectlyPrecedingComment) {
break;
}

optimalToken = commentToken;
commentToken = commentToken.previous;
}

return optimalToken;
}

Token? _latestCommentToken(Token token) {
Token? latestCommentToken = token.precedingComments;

while (latestCommentToken?.next != null) {
latestCommentToken = latestCommentToken?.next;
}

return latestCommentToken;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
part of 'no_blank_line_before_single_return_rule.dart';

class _Visitor extends RecursiveAstVisitor<void> {
final _statements = <ReturnStatement>[];

Iterable<ReturnStatement> get statements => _statements;

@override
void visitReturnStatement(ReturnStatement node) {
super.visitReturnStatement(node);
_statements.add(node);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// ignore_for_file: always_put_control_body_on_new_line, newline-before-return
int simpleFunction() {
var a = 4;

if (a > 70) {
/* multi line
comment */
return a + 1;
} else if (a > 65) {
a++;
/* multi line
comment */
return a + 1;
} else if (a > 60) {
a++;

/* multi line
comment */
return a + 2;
} else if (a > 55) {
a--;
/* multi line
comment */

return a + 3;
}

if (a > 50) {
// simple comment
// simple comment second line
return a + 1;
} else if (a > 45) {
a++;
// simple comment
// simple comment second line

return a + 2;
} else if (a > 40) {
a++;
// simple comment

// simple comment second line
return a + 2;
} else if (a > 35) {
a--;

// simple comment
// simple comment second line
return a + 3;
}

if (a > 30) {
// simple comment
return a + 1;
} else if (a > 25) {
a++;
// simple comment
return a + 2;
} else if (a > 20) {
a--;

// simple comment
return a + 3;
}

if (a > 15) {
return a + 1;
} else if (a > 10) {
a++;
return a + 2;
} else if (a > 5) {
a--;

return a + 3;
}

if (a > 5) {

return a - 1;
}

if (a > 5) {

// one line comment
return a - 1;
}

if (a > 5) {

// one line comment
// one line comment
return a - 1;
}

if (a > 5) {

/*
* Multi line comment
* */
return a - 1;
}

if (a > 5) {
// one line comment

return a - 1;
}

if (a > 5) {

// one line comment

return a - 1;
}

if (a > 5) {

/*
* Multi line comment
* */

return a - 1;
}

if (a > 4) return a + 1;

if (a > 3) return a + 2;

if (a > 2) {
return a + 3;
}

return a;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:dart_code_linter/src/analyzers/lint_analyzer/models/severity.dart';
import 'package:dart_code_linter/src/analyzers/lint_analyzer/rules/rules_list/no_blank_line_before_single_return/no_blank_line_before_single_return_rule.dart';
import 'package:test/test.dart';

import '../../../../../helpers/rule_test_helper.dart';

const _examplePath = 'no_blank_line_before_single_return/examples/example.dart';

void main() {
group('NoBlankLineBeforeSingleReturnRule', () {
test('initialization', () async {
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
final issues = NoBlankLineBeforeSingleReturnRule().check(unit);

RuleTestHelper.verifyInitialization(
issues: issues,
ruleId: NoBlankLineBeforeSingleReturnRule.ruleId,
severity: Severity.style,
);
});

test('reports about found issues', () async {
final unit = await RuleTestHelper.resolveFromFile(_examplePath);
final issues = NoBlankLineBeforeSingleReturnRule().check(unit);

List<int> startLines = [79, 85, 92, 100, 106, 113, 122];

RuleTestHelper.verifyIssues(
issues: issues,
startLines: startLines,
startColumns: List.generate(startLines.length, (index) => 5),
locationTexts: List.generate(startLines.length, (index) => 'return a - 1;'),
messages: List.generate(startLines.length, (index) => NoBlankLineBeforeSingleReturnRule.warning),
);
});
});
}

0 comments on commit b285f93

Please sign in to comment.