1 //          Copyright Brian Schott (Hackerpilot) 2014.
2 // Distributed under the Boost Software License, Version 1.0.
3 //    (See accompanying file LICENSE_1_0.txt or copy at
4 //          http://www.boost.org/LICENSE_1_0.txt)
5 
6 module dscanner.analysis.ifelsesame;
7 
8 import std.stdio;
9 import dparse.ast;
10 import dparse.lexer;
11 import dscanner.analysis.base;
12 import dscanner.analysis.helpers;
13 import dsymbol.scope_ : Scope;
14 
15 /**
16  * Checks for duplicated code in conditional and logical expressions.
17  * $(UL
18  * $(LI If statements whose "then" block is the same as the "else" block)
19  * $(LI || and && expressions where the left and right are the same)
20  * $(LI == expressions where the left and right are the same)
21  * )
22  */
23 final class IfElseSameCheck : BaseAnalyzer
24 {
25 	alias visit = BaseAnalyzer.visit;
26 
27 	mixin AnalyzerInfo!"if_else_same_check";
28 
29 	this(string fileName, const(Scope)* sc, bool skipTests = false)
30 	{
31 		super(fileName, sc, skipTests);
32 	}
33 
34 	override void visit(const IfStatement ifStatement)
35 	{
36 		if (ifStatement.thenStatement && (ifStatement.thenStatement == ifStatement.elseStatement))
37 			addErrorMessage(ifStatement.line, ifStatement.column,
38 					"dscanner.bugs.if_else_same", "'Else' branch is identical to 'Then' branch.");
39 		ifStatement.accept(this);
40 	}
41 
42 	override void visit(const AssignExpression assignExpression)
43 	{
44 		auto e = cast(const AssignExpression) assignExpression.expression;
45 		if (e !is null && assignExpression.operator == tok!"="
46 				&& e.ternaryExpression == assignExpression.ternaryExpression)
47 		{
48 			addErrorMessage(assignExpression.line, assignExpression.column, "dscanner.bugs.self_assignment",
49 					"Left side of assignment operatior is identical to the right side.");
50 		}
51 		assignExpression.accept(this);
52 	}
53 
54 	override void visit(const AndAndExpression andAndExpression)
55 	{
56 		if (andAndExpression.left !is null && andAndExpression.right !is null
57 				&& andAndExpression.left == andAndExpression.right)
58 		{
59 			addErrorMessage(andAndExpression.line, andAndExpression.column,
60 					"dscanner.bugs.logic_operator_operands",
61 					"Left side of logical and is identical to right side.");
62 		}
63 		andAndExpression.accept(this);
64 	}
65 
66 	override void visit(const OrOrExpression orOrExpression)
67 	{
68 		if (orOrExpression.left !is null && orOrExpression.right !is null
69 				&& orOrExpression.left == orOrExpression.right)
70 		{
71 			addErrorMessage(orOrExpression.line, orOrExpression.column,
72 					"dscanner.bugs.logic_operator_operands",
73 					"Left side of logical or is identical to right side.");
74 		}
75 		orOrExpression.accept(this);
76 	}
77 }
78 
79 unittest
80 {
81 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
82 
83 	StaticAnalysisConfig sac = disabledConfig();
84 	sac.if_else_same_check = Check.enabled;
85 	assertAnalyzerWarnings(q{
86 		void testSizeT()
87 		{
88 			string person = "unknown";
89 			if (person == "unknown") // [warn]: 'Else' branch is identical to 'Then' branch.
90 				person = "bobrick"; // same
91 			else
92 				person = "bobrick"; // same
93 
94 			if (person == "unknown") // ok
95 				person = "ricky"; // not same
96 			else
97 				person = "bobby"; // not same
98 		}
99 	}}, sac);
100 
101 	assertAnalyzerWarnings(q{
102 		void foo()
103 		{
104 			if (auto stuff = call())
105 		}
106 	}}, sac);
107 
108 	stderr.writeln("Unittest for IfElseSameCheck passed.");
109 }