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