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.logic_precedence;
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_;
14 
15 /**
16  * Checks for code with confusing && and || operator precedence
17  * ---
18  * if (a && b || c) // bad
19  * if (a && (b || c)) // good
20  * ---
21  */
22 final class LogicPrecedenceCheck : BaseAnalyzer
23 {
24 	alias visit = BaseAnalyzer.visit;
25 
26 	enum string KEY = "dscanner.confusing.logical_precedence";
27 	mixin AnalyzerInfo!"logical_precedence_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 OrOrExpression orOr)
35 	{
36 		if (orOr.left is null || orOr.right is null)
37 			return;
38 		const AndAndExpression left = cast(AndAndExpression) orOr.left;
39 		const AndAndExpression right = cast(AndAndExpression) orOr.right;
40 		if (left is null && right is null)
41 			return;
42 		if ((left !is null && left.right is null) && (right !is null && right.right is null))
43 			return;
44 		addErrorMessage(orOr.line, orOr.column, KEY,
45 				"Use parenthesis to clarify this expression.");
46 		orOr.accept(this);
47 	}
48 }
49 
50 unittest
51 {
52 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
53 
54 	StaticAnalysisConfig sac = disabledConfig();
55 	sac.logical_precedence_check = Check.enabled;
56 	assertAnalyzerWarnings(q{
57 		void testFish()
58 		{
59 			if (a && b || c) {} // [warn]: Use parenthesis to clarify this expression.
60 			if ((a && b) || c) {} // Good
61 			if (b || c && d) {} // [warn]: Use parenthesis to clarify this expression.
62 			if (b || (c && d)) {} // Good
63 		}
64 	}}, sac);
65 	stderr.writeln("Unittest for LogicPrecedenceCheck passed.");
66 }