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