1 //          Copyright Basile Burg 2016.
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 analysis.auto_function;
7 
8 import analysis.base;
9 import analysis.helpers;
10 import dparse.ast;
11 import dparse.lexer;
12 
13 import std.stdio;
14 import std.algorithm.searching : any;
15 
16 /**
17  * Checks for auto functions without return statement.
18  *
19  * Auto function without return statement can be an omission and are not
20  * detected by the compiler. However sometimes they can be used as a trick
21  * to infer attributes.
22  */
23 final class AutoFunctionChecker : BaseAnalyzer
24 {
25 
26 private:
27 
28 	enum string KEY = "dscanner.suspicious.missing_return";
29 	enum string MESSAGE = "Auto function without return statement, prefer an explicit void";
30 
31 	bool[] _returns;
32 
33 public:
34 
35 	alias visit = BaseAnalyzer.visit;
36 
37 	///
38 	this(string fileName, bool skipTests = false)
39 	{
40 		super(fileName, null, skipTests);
41 	}
42 
43 	override void visit(const FunctionDeclaration decl)
44 	{
45 		_returns.length += 1;
46 		scope(exit) _returns.length -= 1;
47 		_returns[$-1] = false;
48 
49 		const bool autoFun = decl.storageClasses
50 			.any!(a => a.token.type == tok!"auto");
51 
52 		decl.accept(this);
53 
54 		if (autoFun && !_returns[$-1])
55 			addErrorMessage(decl.name.line, decl.name.column, KEY, MESSAGE);
56 	}
57 
58 	override void visit(const ReturnStatement rst)
59 	{
60 		if (_returns.length)
61 			_returns[$-1] = true;
62 		rst.accept(this);
63 	}
64 }
65 
66 unittest
67 {
68 	import std.stdio : stderr;
69 	import std.format : format;
70 	import analysis.config : StaticAnalysisConfig, Check;
71 	import analysis.helpers : assertAnalyzerWarnings;
72 
73 	StaticAnalysisConfig sac;
74 	sac.auto_function_check = Check.enabled;
75 	assertAnalyzerWarnings(q{
76 		auto ref doStuff(){} // [warn]: %s
77         auto doStuff(){} // [warn]: %s
78         int doStuff(){auto doStuff(){}} // [warn]: %s
79         auto doStuff(){return 0;}
80         int doStuff(){/*error but not the aim*/}
81 	}}.format(
82         AutoFunctionChecker.MESSAGE,
83         AutoFunctionChecker.MESSAGE,
84         AutoFunctionChecker.MESSAGE,
85     ), sac);
86 	stderr.writeln("Unittest for AutoFunctionChecker passed.");
87 }