169 lines
4.4 KiB
D
169 lines
4.4 KiB
D
// Copyright Brian Schott (Hackerpilot) 2014.
|
|
// 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.ifelsesame;
|
|
|
|
import dscanner.analysis.base;
|
|
import dmd.hdrgen : toChars;
|
|
import dmd.tokens : EXP;
|
|
import std.conv : to;
|
|
import std.string : format;
|
|
import std.typecons : Tuple, tuple;
|
|
|
|
/**
|
|
* Checks for duplicated code in conditional and logical expressions.
|
|
* $(UL
|
|
* $(LI If statements whose "then" block is the same as the "else" block)
|
|
* $(LI || and && expressions where the left and right are the same)
|
|
* $(LI == and != expressions where the left and right are the same)
|
|
* $(LI >, <, >=, and <= expressions where the left and right are the same)
|
|
* $(LI Assignments where the left and right are the same)
|
|
* )
|
|
*/
|
|
extern (C++) class IfElseSameCheck(AST) : BaseAnalyzerDmd
|
|
{
|
|
alias visit = BaseAnalyzerDmd.visit;
|
|
mixin AnalyzerInfo!"if_else_same_check";
|
|
|
|
private enum IF_KEY = "dscanner.bugs.if_else_same";
|
|
private enum IF_MESSAGE = "'Else' branch is identical to 'Then' branch.";
|
|
|
|
private enum LOGICAL_EXP_KEY = "dscanner.bugs.logic_operator_operands";
|
|
private enum LOGICAL_EXP_MESSAGE = "Left side of logical %s is identical to right side.";
|
|
|
|
private enum ASSIGN_KEY = "dscanner.bugs.self_assignment";
|
|
private enum ASSIGN_MESSAGE = "Left side of assignment operation is identical to the right side.";
|
|
|
|
private bool inAssignment = false;
|
|
|
|
extern (D) this(string fileName, bool skipTests = false)
|
|
{
|
|
super(fileName, skipTests);
|
|
}
|
|
|
|
override void visit(AST.IfStatement ifStatement)
|
|
{
|
|
super.visit(ifStatement);
|
|
|
|
if (ifStatement.ifbody is null || ifStatement.elsebody is null)
|
|
return;
|
|
|
|
auto thenBody = to!string(toChars(ifStatement.ifbody));
|
|
auto elseBody = to!string(toChars(ifStatement.elsebody));
|
|
|
|
if (thenBody == elseBody)
|
|
{
|
|
auto lineNum = cast(ulong) ifStatement.loc.linnum;
|
|
auto charNum = cast(ulong) ifStatement.loc.charnum;
|
|
addErrorMessage(lineNum, charNum, IF_KEY, IF_MESSAGE);
|
|
}
|
|
}
|
|
|
|
override void visit(AST.AssignExp assignExp)
|
|
{
|
|
bool oldInAssignment = inAssignment;
|
|
inAssignment = true;
|
|
super.visit(assignExp);
|
|
inAssignment = oldInAssignment;
|
|
}
|
|
|
|
override void visit(AST.CondExp condExp)
|
|
{
|
|
super.visit(condExp);
|
|
if (inAssignment)
|
|
handleBinaryExpression(condExp);
|
|
}
|
|
|
|
override void visit(AST.LogicalExp logicalExpr)
|
|
{
|
|
super.visit(logicalExpr);
|
|
handleBinaryExpression(logicalExpr);
|
|
}
|
|
|
|
private void handleBinaryExpression(AST.BinExp expr)
|
|
{
|
|
auto expr1 = to!string(toChars(expr.e1));
|
|
auto expr2 = to!string(toChars(expr.e2));
|
|
|
|
if (expr1 == expr2)
|
|
{
|
|
auto lineNum = cast(ulong) expr.loc.linnum;
|
|
auto charNum = cast(ulong) expr.loc.charnum;
|
|
auto errorInfo = getErrorInfo(expr.op);
|
|
addErrorMessage(lineNum, charNum, errorInfo[0], errorInfo[1]);
|
|
}
|
|
}
|
|
|
|
private extern (D) Tuple!(string, string) getErrorInfo(EXP op)
|
|
{
|
|
switch (op)
|
|
{
|
|
case EXP.orOr:
|
|
return tuple(LOGICAL_EXP_KEY, LOGICAL_EXP_MESSAGE.format("or"));
|
|
case EXP.andAnd:
|
|
return tuple(LOGICAL_EXP_KEY, LOGICAL_EXP_MESSAGE.format("and"));
|
|
case EXP.question:
|
|
return tuple(ASSIGN_KEY, ASSIGN_MESSAGE);
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
|
|
import dscanner.analysis.helpers : assertAnalyzerWarningsDMD;
|
|
import std.stdio : stderr;
|
|
|
|
StaticAnalysisConfig sac = disabledConfig();
|
|
sac.if_else_same_check = Check.enabled;
|
|
|
|
assertAnalyzerWarningsDMD(q{
|
|
void testThenElseSame()
|
|
{
|
|
string person = "unknown";
|
|
if (person == "unknown") // [warn]: 'Else' branch is identical to 'Then' branch.
|
|
person = "bobrick";
|
|
else
|
|
person = "bobrick";
|
|
|
|
if (person == "unknown")
|
|
person = "ricky";
|
|
else
|
|
person = "bobby";
|
|
}
|
|
}c, sac);
|
|
|
|
assertAnalyzerWarningsDMD(q{
|
|
void testLogicalExp()
|
|
{
|
|
int a = 5, b = 5;
|
|
if (a == b || a == b) // [warn]: Left side of logical or is identical to right side.
|
|
a = 6;
|
|
if (a == b && a == b) // [warn]: Left side of logical and is identical to right side.
|
|
a = 6;
|
|
}
|
|
}c, sac);
|
|
|
|
assertAnalyzerWarningsDMD(q{
|
|
void testAssignExp()
|
|
{
|
|
int a = 5, b = 5;
|
|
a = b > 5 ? b : b; // [warn]: Left side of assignment operation is identical to the right side.
|
|
}
|
|
}c, sac);
|
|
|
|
assertAnalyzerWarningsDMD(q{
|
|
void foo()
|
|
{
|
|
if (auto stuff = call()) {}
|
|
}
|
|
}c, sac);
|
|
|
|
stderr.writeln("Unittest for IfElseSameCheck passed.");
|
|
}
|