This commit is contained in:
Hackerpilot 2015-02-21 02:13:38 -08:00
parent 6b7cb5941f
commit dbea951e5d
3 changed files with 138 additions and 2 deletions

View File

@ -83,4 +83,7 @@ struct StaticAnalysisConfig
@INI("Checks for variables that could be declared immutable")
bool could_be_immutable_check = false; // disabled by default for now
@INI("Checks for redundant expressions in if statements")
bool redundant_if_check;
}

View File

@ -0,0 +1,131 @@
// 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 analysis.if_statements;
import std.d.ast;
import std.d.lexer;
import std.d.formatter;
import analysis.base;
class IfStatementCheck : BaseAnalyzer
{
alias visit = BaseAnalyzer.visit;
this(string fileName)
{
import std.container.binaryheap : heapify;
super(fileName);
}
override void visit(const IfStatement ifStatement)
{
import std.format : 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.format : format;
import std.array : appender;
import std.algorithm : sort;
if (expression is null)
return;
auto app = appender!string();
std.d.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;
private enum 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;
}

View File

@ -40,6 +40,7 @@ import analysis.comma_expression;
import analysis.function_attributes;
import analysis.local_imports;
import analysis.unmodified;
import analysis.if_statements;
bool first = true;
@ -112,8 +113,8 @@ void generateReport(string[] fileNames, const StaticAnalysisConfig config)
writeln("}");
}
// For multiple files
// Returns: true if there were errors
/// For multiple files
/// Returns: true if there were errors
bool analyze(string[] fileNames, const StaticAnalysisConfig config, bool staticAnalyze = true)
{
bool hasErrors = false;
@ -187,6 +188,7 @@ MessageSet analyze(string fileName, const Module m,
if (analysisConfig.comma_expression_check) checks ~= new CommaExpressionCheck(fileName);
if (analysisConfig.local_import_check) checks ~= new LocalImportCheck(fileName);
if (analysisConfig.could_be_immutable_check) checks ~= new UnmodifiedFinder(fileName);
if (analysisConfig.redundant_if_check) checks ~= new IfStatementCheck(fileName);
foreach (check; checks)
{