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 6 module dscanner.analysis.incorrect_infinite_range; 7 8 import dscanner.analysis.base; 9 import dscanner.analysis.helpers; 10 import dparse.ast; 11 import dparse.lexer; 12 13 /** 14 * Checks for incorrect infinite range definitions 15 */ 16 final class IncorrectInfiniteRangeCheck : BaseAnalyzer 17 { 18 alias visit = BaseAnalyzer.visit; 19 20 /// 21 this(string fileName, bool skipTests = false) 22 { 23 super(fileName, null, skipTests); 24 } 25 26 override void visit(const StructBody structBody) 27 { 28 inStruct++; 29 structBody.accept(this); 30 inStruct--; 31 } 32 33 override void visit(const FunctionDeclaration fd) 34 { 35 if (inStruct > 0 && fd.name.text == "empty") 36 { 37 line = fd.name.line; 38 column = fd.name.column; 39 fd.accept(this); 40 } 41 } 42 43 override void visit(const FunctionBody fb) 44 { 45 if (fb.bodyStatement !is null) 46 visit(fb.bodyStatement.blockStatement); 47 else 48 if (fb.blockStatement !is null) 49 visit(fb.blockStatement); 50 } 51 52 override void visit(const BlockStatement bs) 53 { 54 if (bs.declarationsAndStatements is null) 55 return; 56 if (bs.declarationsAndStatements.declarationsAndStatements is null) 57 return; 58 if (bs.declarationsAndStatements.declarationsAndStatements.length != 1) 59 return; 60 visit(bs.declarationsAndStatements); 61 } 62 63 override void visit(const ReturnStatement rs) 64 { 65 if (inStruct == 0 || line == size_t.max) // not within a struct yet 66 return; 67 if (!rs.expression || rs.expression.items.length != 1) 68 return; 69 UnaryExpression unary = cast(UnaryExpression) rs.expression.items[0]; 70 if (unary is null) 71 return; 72 if (unary.primaryExpression is null) 73 return; 74 if (unary.primaryExpression.primary != tok!"false") 75 return; 76 addErrorMessage(line, column, KEY, MESSAGE); 77 } 78 79 override void visit(const Unittest u) 80 { 81 } 82 83 private: 84 uint inStruct; 85 enum string KEY = "dscanner.suspicious.incorrect_infinite_range"; 86 enum string MESSAGE = "Use `enum bool empty = false;` to define an infinite range."; 87 size_t line = size_t.max; 88 size_t column; 89 } 90 91 unittest 92 { 93 import std.stdio : stderr; 94 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 95 import std.format : format; 96 97 StaticAnalysisConfig sac = disabledConfig(); 98 sac.incorrect_infinite_range_check = Check.enabled; 99 assertAnalyzerWarnings(q{struct InfiniteRange 100 { 101 bool empty() // [warn]: %1$s 102 { 103 return false; 104 } 105 106 bool stuff() 107 { 108 return false; 109 } 110 111 unittest 112 { 113 return false; 114 } 115 116 // https://issues.dlang.org/show_bug.cgi?id=18409 117 struct Foo 118 { 119 ~this() nothrow @nogc; 120 } 121 } 122 123 bool empty() { return false; } 124 class C { bool empty() { return false; } } // [warn]: %1$s 125 126 }} 127 .format(IncorrectInfiniteRangeCheck.MESSAGE), sac); 128 } 129 130 // test for https://github.com/dlang-community/D-Scanner/issues/656 131 // unittests are skipped but D-Scanner sources are self checked 132 version(none) struct Foo 133 { 134 void empty() 135 { 136 return; 137 } 138 } 139 140 unittest 141 { 142 import std.stdio : stderr; 143 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 144 import std.format : format; 145 146 StaticAnalysisConfig sac = disabledConfig(); 147 sac.incorrect_infinite_range_check = Check.enabled; 148 assertAnalyzerWarnings(q{ 149 enum isAllZeroBits = () 150 { 151 if (true) 152 return true; 153 else 154 return false; 155 }(); 156 }, sac); 157 stderr.writeln("Unittest for IncorrectInfiniteRangeCheck passed."); 158 }