blob: ff31eb9940f292c945bd52068bc4dc5041636442 [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:linter/src/analyzer.dart';
const _desc = r'Avoid using unnecessary statements.';
const _details = r'''
**AVOID** using unnecessary statements.
Statements which have no clear effect are usually unnecessary, or should be
broken up.
For example,
**BAD:**
```
myvar;
1 + 2;
some.getter;
methodOne() + methodTwo();
foo ? bar : baz;
```
While the getter may trigger a side-effect, it is not usually obvious. Though
the added methods have a clear effect, the addition itself does not unless there
is some magical overload of the + operator.
Usually code like this indicates an incomplete thought, and is a bug. For
instance, the getter was likely supposed to be a function call.
**GOOD:**
```
some.method();
new SomeClass();
methodOne();
methodTwo();
foo ? bar() : baz();
return myvar;
```
''';
class UnnecessaryStatements extends LintRule {
UnnecessaryStatements()
: super(
name: 'unnecessary_statements',
description: _desc,
details: _details,
group: Group.errors);
@override
AstVisitor getVisitor() =>
new _Visitor(new _ReportNoClearEffectVisitor(this));
}
class _Visitor extends SimpleAstVisitor {
final _ReportNoClearEffectVisitor reportNoClearEffect;
_Visitor(this.reportNoClearEffect);
@override
visitExpressionStatement(ExpressionStatement node) {
if (node.parent is FunctionBody) {
return;
}
node.expression.accept(reportNoClearEffect);
}
@override
visitForStatement(ForStatement node) {
node.initialization?.accept(reportNoClearEffect);
node.updaters?.forEach((u) {
u.accept(reportNoClearEffect);
});
}
}
class _ReportNoClearEffectVisitor extends UnifyingAstVisitor {
final LintRule rule;
_ReportNoClearEffectVisitor(this.rule);
@override
visitMethodInvocation(MethodInvocation node) {
// Has a clear effect
}
@override
visitSuperConstructorInvocation(SuperConstructorInvocation node) {
// Has a clear effect
}
@override
visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
// Has a clear effect
}
@override
visitAssignmentExpression(AssignmentExpression node) {
// Has a clear effect
}
@override
visitAwaitExpression(AwaitExpression node) {
// Has a clear effect
}
@override
visitCascadeExpression(CascadeExpression node) {
// Has a clear effect
}
@override
visitPostfixExpression(PostfixExpression node) {
// Has a clear effect
}
@override
visitPrefixExpression(PrefixExpression node) {
if (node.operator.lexeme == '--' || node.operator.lexeme == '++') {
// Has a clear effect
return;
}
super.visitPrefixExpression(node);
}
@override
visitRethrowExpression(RethrowExpression node) {
// Has a clear effect
}
@override
visitThrowExpression(ThrowExpression node) {
// Has a clear effect
}
@override
visitInstanceCreationExpression(InstanceCreationExpression node) {
// A few APIs use this for side effects, like Timer. Also, for constructors
// that have side effects, they should have tests. Those tests will often
// include an instantiation expression statement with nothing else.
}
@override
visitConditionalExpression(ConditionalExpression node) {
node.thenExpression.accept(this);
node.elseExpression.accept(this);
}
@override
visitBinaryExpression(BinaryExpression node) {
switch (node.operator.lexeme) {
case '??':
case '||':
case '&&':
// these are OK when used for control flow
node.rightOperand.accept(this);
return;
}
super.visitBinaryExpression(node);
}
@override
visitNode(AstNode expression) {
rule.reportLint(expression);
}
}