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, ifStatement.expression.line, 33 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, 51 andAndExpression.column); 52 redundancyCheck(andAndExpression.left, andAndExpression.line, 53 andAndExpression.column); 54 redundancyCheck(andAndExpression.right, andAndExpression.line, 55 andAndExpression.column); 56 } 57 andAndExpression.accept(this); 58 } 59 60 override void visit(const OrOrExpression orOrExpression) 61 { 62 // intentionally does nothing 63 } 64 65 private: 66 invariant 67 { 68 assert(depth >= 0); 69 } 70 71 void redundancyCheck(const ExpressionNode expression, size_t line, 72 size_t column) 73 { 74 import std.string : format; 75 import std.array : appender; 76 import std.algorithm : sort; 77 78 if (expression is null) 79 return; 80 auto app = appender!string(); 81 dparse.formatter.format(app, expression); 82 immutable size_t prevLocation = alreadyChecked(app.data, line, column); 83 if (prevLocation != size_t.max) 84 { 85 addErrorMessage(line, column, KEY, 86 "Expression %s is true: already checked on line %d.".format( 87 expressions[prevLocation].formatted, 88 expressions[prevLocation].line)); 89 } 90 else 91 { 92 expressions ~= ExpressionInfo(app.data, line, column, depth); 93 sort(expressions); 94 } 95 } 96 97 size_t alreadyChecked(string expressionText, size_t line, size_t column) 98 { 99 foreach (i, ref info; expressions) 100 { 101 if (info.line == line && info.column == column) 102 continue; 103 if (info.formatted == expressionText) 104 return i; 105 } 106 return size_t.max; 107 } 108 109 bool inIfExpresson; 110 int depth; 111 ExpressionInfo[] expressions; 112 enum string KEY = "dscanner.if_statement"; 113 } 114 115 private struct ExpressionInfo 116 { 117 int opCmp(ref const ExpressionInfo other) const nothrow 118 { 119 if (line < other.line || (line == other.line && column < other.column)) 120 return 1; 121 if (line == other.line && column == other.column) 122 return 0; 123 return -1; 124 } 125 126 string formatted; 127 size_t line; 128 size_t column; 129 int depth; 130 }