1 // Copyright Brian Schott (Hackerpilot) 2015. 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 module analysis.if_statements; 6 7 import dparse.ast; 8 import dparse.lexer; 9 import dparse.formatter; 10 import analysis.base; 11 import dsymbol.scope_ : Scope; 12 13 class IfStatementCheck : BaseAnalyzer 14 { 15 alias visit = BaseAnalyzer.visit; 16 this(string fileName, const(Scope)* sc) 17 { 18 super(fileName, sc); 19 } 20 21 override void visit(const IfStatement ifStatement) 22 { 23 import std.string : format; 24 import std.algorithm : sort, countUntil; 25 import std.array : appender; 26 27 ++depth; 28 29 if (ifStatement.expression.items.length == 1 30 && (cast(AndAndExpression) ifStatement.expression.items[0]) is null) 31 { 32 redundancyCheck(ifStatement.expression, 33 ifStatement.expression.line, ifStatement.expression.column); 34 } 35 inIfExpresson = true; 36 ifStatement.expression.accept(this); 37 inIfExpresson = false; 38 ifStatement.thenStatement.accept(this); 39 if (expressions.length) 40 expressions = expressions[0 .. expressions.countUntil!(a => a.depth + 1 >= depth)]; 41 if (ifStatement.elseStatement) 42 ifStatement.elseStatement.accept(this); 43 --depth; 44 } 45 46 override void visit(const AndAndExpression andAndExpression) 47 { 48 if (inIfExpresson) 49 { 50 redundancyCheck(andAndExpression, andAndExpression.line, andAndExpression.column); 51 redundancyCheck(andAndExpression.left, andAndExpression.line, andAndExpression.column); 52 redundancyCheck(andAndExpression.right, andAndExpression.line, 53 andAndExpression.column); 54 } 55 andAndExpression.accept(this); 56 } 57 58 override void visit(const OrOrExpression orOrExpression) 59 { 60 // intentionally does nothing 61 } 62 63 private: 64 invariant 65 { 66 assert(depth >= 0); 67 } 68 69 void redundancyCheck(const ExpressionNode expression, size_t line, size_t column) 70 { 71 import std.string : format; 72 import std.array : appender; 73 import std.algorithm : sort; 74 75 if (expression is null) 76 return; 77 auto app = appender!string(); 78 dparse.formatter.format(app, expression); 79 immutable size_t prevLocation = alreadyChecked(app.data, line, column); 80 if (prevLocation != size_t.max) 81 { 82 addErrorMessage(line, column, KEY, "Expression %s is true: already checked on line %d.".format( 83 expressions[prevLocation].formatted, expressions[prevLocation].line)); 84 } 85 else 86 { 87 expressions ~= ExpressionInfo(app.data, line, column, depth); 88 sort(expressions); 89 } 90 } 91 92 size_t alreadyChecked(string expressionText, size_t line, size_t column) 93 { 94 foreach (i, ref info; expressions) 95 { 96 if (info.line == line && info.column == column) 97 continue; 98 if (info.formatted == expressionText) 99 return i; 100 } 101 return size_t.max; 102 } 103 104 bool inIfExpresson; 105 int depth; 106 ExpressionInfo[] expressions; 107 enum string KEY = "dscanner.if_statement"; 108 } 109 110 private struct ExpressionInfo 111 { 112 int opCmp(ref const ExpressionInfo other) const nothrow 113 { 114 if (line < other.line || (line == other.line && column < other.column)) 115 return 1; 116 if (line == other.line && column == other.column) 117 return 0; 118 return -1; 119 } 120 121 string formatted; 122 size_t line; 123 size_t column; 124 int depth; 125 }