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.specifiedFunctionBody && fb.specifiedFunctionBody.blockStatement !is null) 46 visit(fb.specifiedFunctionBody.blockStatement); 47 } 48 49 override void visit(const BlockStatement bs) 50 { 51 if (bs.declarationsAndStatements is null) 52 return; 53 if (bs.declarationsAndStatements.declarationsAndStatements is null) 54 return; 55 if (bs.declarationsAndStatements.declarationsAndStatements.length != 1) 56 return; 57 visit(bs.declarationsAndStatements); 58 } 59 60 override void visit(const ReturnStatement rs) 61 { 62 if (inStruct == 0 || line == size_t.max) // not within a struct yet 63 return; 64 if (!rs.expression || rs.expression.items.length != 1) 65 return; 66 UnaryExpression unary = cast(UnaryExpression) rs.expression.items[0]; 67 if (unary is null) 68 return; 69 if (unary.primaryExpression is null) 70 return; 71 if (unary.primaryExpression.primary != tok!"false") 72 return; 73 addErrorMessage(line, column, KEY, MESSAGE); 74 } 75 76 override void visit(const Unittest u) 77 { 78 } 79 80 private: 81 uint inStruct; 82 enum string KEY = "dscanner.suspicious.incorrect_infinite_range"; 83 enum string MESSAGE = "Use `enum bool empty = false;` to define an infinite range."; 84 size_t line = size_t.max; 85 size_t column; 86 } 87 88 unittest 89 { 90 import std.stdio : stderr; 91 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 92 import std.format : format; 93 94 StaticAnalysisConfig sac = disabledConfig(); 95 sac.incorrect_infinite_range_check = Check.enabled; 96 assertAnalyzerWarnings(q{struct InfiniteRange 97 { 98 bool empty() // [warn]: %1$s 99 { 100 return false; 101 } 102 103 bool stuff() 104 { 105 return false; 106 } 107 108 unittest 109 { 110 return false; 111 } 112 113 // https://issues.dlang.org/show_bug.cgi?id=18409 114 struct Foo 115 { 116 ~this() nothrow @nogc; 117 } 118 } 119 120 bool empty() { return false; } 121 class C { bool empty() { return false; } } // [warn]: %1$s 122 123 }} 124 .format(IncorrectInfiniteRangeCheck.MESSAGE), sac); 125 } 126 127 // test for https://github.com/dlang-community/D-Scanner/issues/656 128 // unittests are skipped but D-Scanner sources are self checked 129 version(none) struct Foo 130 { 131 void empty() 132 { 133 return; 134 } 135 } 136 137 unittest 138 { 139 import std.stdio : stderr; 140 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 141 import std.format : format; 142 143 StaticAnalysisConfig sac = disabledConfig(); 144 sac.incorrect_infinite_range_check = Check.enabled; 145 assertAnalyzerWarnings(q{ 146 enum isAllZeroBits = () 147 { 148 if (true) 149 return true; 150 else 151 return false; 152 }(); 153 }, sac); 154 stderr.writeln("Unittest for IncorrectInfiniteRangeCheck passed."); 155 }