D-Scanner/src/dscanner/analysis/if_statements.d

126 lines
3.2 KiB
D

// Copyright Brian Schott (Hackerpilot) 2015.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
module dscanner.analysis.if_statements;
import dparse.ast;
import dparse.lexer;
import dparse.formatter;
import dscanner.analysis.base;
import dsymbol.scope_ : Scope;
class IfStatementCheck : BaseAnalyzer
{
alias visit = BaseAnalyzer.visit;
this(string fileName, const(Scope)* sc, bool skipTests = false)
{
super(fileName, sc, skipTests);
}
override void visit(const IfStatement ifStatement)
{
import std.string : format;
import std.algorithm : sort, countUntil;
import std.array : appender;
++depth;
if (ifStatement.expression.items.length == 1
&& (cast(AndAndExpression) ifStatement.expression.items[0]) is null)
{
redundancyCheck(ifStatement.expression,
ifStatement.expression.line, ifStatement.expression.column);
}
inIfExpresson = true;
ifStatement.expression.accept(this);
inIfExpresson = false;
ifStatement.thenStatement.accept(this);
if (expressions.length)
expressions = expressions[0 .. expressions.countUntil!(a => a.depth + 1 >= depth)];
if (ifStatement.elseStatement)
ifStatement.elseStatement.accept(this);
--depth;
}
override void visit(const AndAndExpression andAndExpression)
{
if (inIfExpresson)
{
redundancyCheck(andAndExpression, andAndExpression.line, andAndExpression.column);
redundancyCheck(andAndExpression.left, andAndExpression.line, andAndExpression.column);
redundancyCheck(andAndExpression.right, andAndExpression.line,
andAndExpression.column);
}
andAndExpression.accept(this);
}
override void visit(const OrOrExpression orOrExpression)
{
// intentionally does nothing
}
private:
invariant
{
assert(depth >= 0);
}
void redundancyCheck(const ExpressionNode expression, size_t line, size_t column)
{
import std.string : format;
import std.array : appender;
import std.algorithm : sort;
if (expression is null)
return;
auto app = appender!string();
dparse.formatter.format(app, expression);
immutable size_t prevLocation = alreadyChecked(app.data, line, column);
if (prevLocation != size_t.max)
{
addErrorMessage(line, column, KEY, "Expression %s is true: already checked on line %d.".format(
expressions[prevLocation].formatted, expressions[prevLocation].line));
}
else
{
expressions ~= ExpressionInfo(app.data, line, column, depth);
sort(expressions);
}
}
size_t alreadyChecked(string expressionText, size_t line, size_t column)
{
foreach (i, ref info; expressions)
{
if (info.line == line && info.column == column)
continue;
if (info.formatted == expressionText)
return i;
}
return size_t.max;
}
bool inIfExpresson;
int depth;
ExpressionInfo[] expressions;
enum string KEY = "dscanner.if_statement";
}
private struct ExpressionInfo
{
int opCmp(ref const ExpressionInfo other) const nothrow
{
if (line < other.line || (line == other.line && column < other.column))
return 1;
if (line == other.line && column == other.column)
return 0;
return -1;
}
string formatted;
size_t line;
size_t column;
int depth;
}