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 class LogicPrecedenceCheck : BaseAnalyzer
23 {
24 alias visit = BaseAnalyzer.visit;
25
26 enum string KEY = "dscanner.confusing.logical_precedence";
27
28 this(string fileName, const(Scope)* sc, bool skipTests = false)
29 {
30 super(fileName, sc, skipTests);
31 }
32
33 override void visit(const OrOrExpression orOr)
34 {
35 if (orOr.left is null || orOr.right is null)
36 return;
37 const AndAndExpression left = cast(AndAndExpression) orOr.left;
38 const AndAndExpression right = cast(AndAndExpression) orOr.right;
39 if (left is null && right is null)
40 return;
41 if ((left !is null && left.right is null) && (right !is null && right.right is null))
42 return;
43 addErrorMessage(orOr.line, orOr.column, KEY,
44 "Use parenthesis to clarify this expression.");
45 orOr.accept(this);
46 }
47 }
48
49 unittest
50 {
51 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
52
53 StaticAnalysisConfig sac = disabledConfig();
54 sac.logical_precedence_check = Check.enabled;
55 assertAnalyzerWarnings(q{
56 void testFish()
57 {
58 if (a && b || c) {} // [warn]: Use parenthesis to clarify this expression.
59 if ((a && b) || c) {} // Good
60 if (b || c && d) {} // [warn]: Use parenthesis to clarify this expression.
61 if (b || (c && d)) {} // Good
62 }
63 }c, sac);
64 stderr.writeln("Unittest for LogicPrecedenceCheck passed.");
65 }