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 }