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