D-Scanner/src/dscanner/analysis/ifelsesame.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.");
}