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 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 (rs.expression.items.length != 1)
66 			return;
67 		UnaryExpression unary = cast(UnaryExpression) rs.expression.items[0];
68 		if (unary is null)
69 			return;
70 		if (unary.primaryExpression is null)
71 			return;
72 		if (unary.primaryExpression.primary != tok!"false")
73 			return;
74 		addErrorMessage(line, column, KEY, MESSAGE);
75 	}
76 
77 	override void visit(const Unittest u)
78 	{
79 	}
80 
81 private:
82 	uint inStruct;
83 	enum string KEY = "dscanner.suspicious.incorrect_infinite_range";
84 	enum string MESSAGE = "Use `enum bool empty = false;` to define an infinite range.";
85 	size_t line;
86 	size_t column;
87 }
88 
89 unittest
90 {
91 	import std.stdio : stderr;
92 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
93 	import std.format : format;
94 
95 	StaticAnalysisConfig sac = disabledConfig();
96 	sac.incorrect_infinite_range_check = Check.enabled;
97 	assertAnalyzerWarnings(q{struct InfiniteRange
98 {
99 	bool empty() // [warn]: %1$s
100 	{
101 		return false;
102 	}
103 
104 	bool stuff()
105 	{
106 		return false;
107 	}
108 
109 	unittest
110 	{
111 		return false;
112 	}
113 
114 	// https://issues.dlang.org/show_bug.cgi?id=18409
115 	struct Foo
116 	{
117 		~this() nothrow @nogc;
118 	}
119 }
120 
121 bool empty() { return false; }
122 class C { bool empty() { return false; } } // [warn]: %1$s
123 
124 }c
125 			.format(IncorrectInfiniteRangeCheck.MESSAGE), sac);
126 
127 	stderr.writeln("Unittest for IncorrectInfiniteRangeCheck passed.");
128 }