From cbeca3ba345a65e16aa2a3477466e13700cc43eb Mon Sep 17 00:00:00 2001
From: Basile Burg <basile.b@gmx.com>
Date: Sat, 26 Nov 2016 06:59:40 +0100
Subject: [PATCH] auto functions checker, check assert(false) and string
 literal mixin.

---
 src/analysis/auto_function.d | 86 +++++++++++++++++++++++++++++++-----
 1 file changed, 76 insertions(+), 10 deletions(-)

diff --git a/src/analysis/auto_function.d b/src/analysis/auto_function.d
index 98489f4..4be8b59 100644
--- a/src/analysis/auto_function.d
+++ b/src/analysis/auto_function.d
@@ -29,6 +29,7 @@ private:
 	enum string MESSAGE = "Auto function without return statement, prefer an explicit void";
 
 	bool[] _returns;
+	size_t _mixinDepth;
 
 public:
 
@@ -40,7 +41,7 @@ public:
 		super(fileName, null, skipTests);
 	}
 
-	override void visit(const FunctionDeclaration decl)
+	override void visit(const(FunctionDeclaration) decl)
 	{
 		_returns.length += 1;
 		scope(exit) _returns.length -= 1;
@@ -55,12 +56,48 @@ public:
 			addErrorMessage(decl.name.line, decl.name.column, KEY, MESSAGE);
 	}
 
-	override void visit(const ReturnStatement rst)
+	override void visit(const(ReturnStatement) rst)
 	{
 		if (_returns.length)
 			_returns[$-1] = true;
 		rst.accept(this);
 	}
+
+	override void visit(const(AssertExpression) exp)
+	{
+		exp.accept(this);
+		if (_returns.length)
+		{
+			const UnaryExpression u = cast(UnaryExpression) exp.assertion;
+			if (!u)
+				return;
+			const PrimaryExpression p = u.primaryExpression;
+			if (!p)
+				return;
+
+			immutable token = p.primary;
+			if (token.type == tok!"false")
+				_returns[$-1] = true;
+			else if (token.text == "0")
+				_returns[$-1] = true;
+		}
+	}
+
+	override void visit(const(MixinExpression) mix)
+	{
+		++_mixinDepth;
+		mix.accept(this);
+		--_mixinDepth;
+	}
+
+	override void visit(const(PrimaryExpression) exp)
+	{
+		exp.accept(this);
+		import std.algorithm.searching : find;
+		import std.range : empty;
+		if (_returns.length && _mixinDepth && !exp.primary.text.find("return").empty)
+			_returns[$-1] = true;
+	}
 }
 
 unittest
@@ -74,14 +111,43 @@ unittest
 	sac.auto_function_check = Check.enabled;
 	assertAnalyzerWarnings(q{
 		auto ref doStuff(){} // [warn]: %s
-        auto doStuff(){} // [warn]: %s
-        int doStuff(){auto doStuff(){}} // [warn]: %s
-        auto doStuff(){return 0;}
-        int doStuff(){/*error but not the aim*/}
+		auto doStuff(){} // [warn]: %s
+		int doStuff(){auto doStuff(){}} // [warn]: %s
+		auto doStuff(){return 0;}
+		int doStuff(){/*error but not the aim*/}
 	}c.format(
-        AutoFunctionChecker.MESSAGE,
-        AutoFunctionChecker.MESSAGE,
-        AutoFunctionChecker.MESSAGE,
-    ), sac);
+		AutoFunctionChecker.MESSAGE,
+		AutoFunctionChecker.MESSAGE,
+		AutoFunctionChecker.MESSAGE,
+	), sac);
+
+	assertAnalyzerWarnings(q{
+		auto doStuff(){assert(true);} // [warn]: %s
+		auto doStuff(){assert(false);}
+	}c.format(
+		AutoFunctionChecker.MESSAGE,
+	), sac);
+
+	assertAnalyzerWarnings(q{
+		auto doStuff(){assert(1);} // [warn]: %s
+		auto doStuff(){assert(0);}
+	}c.format(
+		AutoFunctionChecker.MESSAGE,
+	), sac);
+
+	assertAnalyzerWarnings(q{
+		auto doStuff(){mixin("0+0");} // [warn]: %s
+		auto doStuff(){mixin("return 0;");}
+	}c.format(
+		AutoFunctionChecker.MESSAGE,
+	), sac);
+
+	assertAnalyzerWarnings(q{
+		auto doStuff(){mixin("0+0");} // [warn]: %s
+		auto doStuff(){mixin("static if (true)" ~ "  return " ~ 0.stringof ~ ";");}
+	}c.format(
+		AutoFunctionChecker.MESSAGE,
+	), sac);
+
 	stderr.writeln("Unittest for AutoFunctionChecker passed.");
 }