-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add prefer-conditional-expressions rule and fix * Add tests for prefer-conditional-expressions rule * fix nested test plugin path * Fix tests after merge * Add prefer-first rule and fix * Add tests for prefer-first rule --------- Co-authored-by: Denis Bogatirov <[email protected]> Co-authored-by: Yurii Prykhodko <[email protected]>
- Loading branch information
1 parent
051ab41
commit 7e1deaf
Showing
7 changed files
with
186 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/error/error.dart'; | ||
import 'package:analyzer/source/source_range.dart'; | ||
import 'package:custom_lint_builder/custom_lint_builder.dart'; | ||
|
||
/// A Quick fix for `prefer-first` rule | ||
/// Suggests to replace iterable access expressions | ||
class PreferFirstFix extends DartFix { | ||
static const _replaceComment = "Replace with 'first'."; | ||
|
||
@override | ||
void run( | ||
CustomLintResolver resolver, | ||
ChangeReporter reporter, | ||
CustomLintContext context, | ||
AnalysisError analysisError, | ||
List<AnalysisError> others, | ||
) { | ||
context.registry.addMethodInvocation((node) { | ||
if (analysisError.sourceRange.intersects(node.sourceRange)) { | ||
final correction = _createCorrection(node); | ||
|
||
_addReplacement(reporter, node, correction); | ||
} | ||
}); | ||
|
||
context.registry.addIndexExpression((node) { | ||
if (analysisError.sourceRange.intersects(node.sourceRange)) { | ||
final correction = _createCorrection(node); | ||
|
||
_addReplacement(reporter, node, correction); | ||
} | ||
}); | ||
} | ||
|
||
String _createCorrection(Expression expression) { | ||
if (expression is MethodInvocation) { | ||
return expression.isCascaded | ||
? '..first' | ||
: '${expression.target ?? ''}.first'; | ||
} else if (expression is IndexExpression) { | ||
return expression.isCascaded | ||
? '..first' | ||
: '${expression.target ?? ''}.first'; | ||
} else { | ||
return '.first'; | ||
} | ||
} | ||
|
||
void _addReplacement( | ||
ChangeReporter reporter, | ||
Expression node, | ||
String correction, | ||
) { | ||
final changeBuilder = reporter.createChangeBuilder( | ||
message: _replaceComment, | ||
priority: 1, | ||
); | ||
|
||
changeBuilder.addDartFileEdit((builder) { | ||
builder.addSimpleReplacement( | ||
SourceRange(node.offset, node.length), | ||
correction, | ||
); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import 'package:analyzer/error/listener.dart'; | ||
import 'package:custom_lint_builder/custom_lint_builder.dart'; | ||
import 'package:solid_lints/lints/prefer_first/prefer_first_fix.dart'; | ||
import 'package:solid_lints/lints/prefer_first/prefer_first_visitor.dart'; | ||
import 'package:solid_lints/models/rule_config.dart'; | ||
import 'package:solid_lints/models/solid_lint_rule.dart'; | ||
|
||
/// A `prefer-first` rule which warns about | ||
/// usage of iterable[0] or iterable.elementAt(0) | ||
class PreferFirstRule extends SolidLintRule { | ||
/// The [LintCode] of this lint rule that represents the error if number of | ||
/// parameters reaches the maximum value. | ||
static const lintName = 'prefer-first'; | ||
|
||
PreferFirstRule._(super.config); | ||
|
||
/// Creates a new instance of [PreferFirstRule] | ||
/// based on the lint configuration. | ||
factory PreferFirstRule.createRule(CustomLintConfigs configs) { | ||
final config = RuleConfig( | ||
configs: configs, | ||
name: lintName, | ||
problemMessage: (value) => | ||
'Use first instead of accessing the element at zero index.', | ||
); | ||
|
||
return PreferFirstRule._(config); | ||
} | ||
|
||
@override | ||
void run( | ||
CustomLintResolver resolver, | ||
ErrorReporter reporter, | ||
CustomLintContext context, | ||
) { | ||
context.registry.addCompilationUnit((node) { | ||
final visitor = PreferFirstVisitor(); | ||
node.accept(visitor); | ||
|
||
for (final element in visitor.expressions) { | ||
reporter.reportErrorForNode(code, element); | ||
} | ||
}); | ||
} | ||
|
||
@override | ||
List<Fix> getFixes() => [PreferFirstFix()]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import 'package:analyzer/dart/ast/ast.dart'; | ||
import 'package:analyzer/dart/ast/visitor.dart'; | ||
import 'package:solid_lints/utils/types_utils.dart'; | ||
|
||
/// The AST visitor that will collect all Iterable access expressions | ||
/// which can be replaced with .first | ||
class PreferFirstVisitor extends RecursiveAstVisitor<void> { | ||
final _expressions = <Expression>[]; | ||
|
||
/// List of all Iterable access expressions | ||
Iterable<Expression> get expressions => _expressions; | ||
|
||
@override | ||
void visitMethodInvocation(MethodInvocation node) { | ||
super.visitMethodInvocation(node); | ||
final isIterable = isIterableOrSubclass(node.realTarget?.staticType); | ||
final isElementAt = node.methodName.name == 'elementAt'; | ||
|
||
if (isIterable && isElementAt) { | ||
final arg = node.argumentList.arguments.first; | ||
|
||
if (arg is IntegerLiteral && arg.value == 0) { | ||
_expressions.add(node); | ||
} | ||
} | ||
} | ||
|
||
@override | ||
void visitIndexExpression(IndexExpression node) { | ||
super.visitIndexExpression(node); | ||
|
||
if (isListOrSubclass(node.realTarget.staticType)) { | ||
final index = node.index; | ||
|
||
if (index is IntegerLiteral && index.value == 0) { | ||
_expressions.add(node); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,3 +50,4 @@ custom_lint: | |
- dispose-method | ||
- no-magic-number | ||
- prefer-conditional-expressions | ||
- prefer-first |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/// Check the `prefer-first` rule | ||
void fun() { | ||
const zero = 0; | ||
final list = [0, 1, 2, 3]; | ||
final set = {0, 1, 2, 3}; | ||
final map = {0: 0, 1: 1, 2: 2, 3: 3}; | ||
|
||
// expect_lint: prefer-first | ||
list[0]; | ||
list[zero]; | ||
// expect_lint: prefer-first | ||
list.elementAt(0); | ||
list.elementAt(zero); | ||
// expect_lint: prefer-first | ||
set.elementAt(0); | ||
|
||
// expect_lint: prefer-first | ||
map.keys.elementAt(0); | ||
// expect_lint: prefer-first | ||
map.values.elementAt(0); | ||
} |