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 analysis.useless_assert;
7 
8 import analysis.base;
9 import analysis.helpers;
10 import dparse.ast;
11 import dparse.lexer;
12 
13 import std.stdio;
14 
15 /**
16  * Checks for asserts that always succeed
17  */
18 class UselessAssertCheck : BaseAnalyzer
19 {
20 	alias visit = BaseAnalyzer.visit;
21 
22 	///
23 	this(string fileName, bool skipTests = false)
24 	{
25 		// assertions likely to be in unittest so never skip
26 		super(fileName, null, skipTests);
27 	}
28 
29 	override void visit(const AssertExpression ae)
30 	{
31 		import std.conv : to;
32 		import std.string : removechars;
33 
34 		UnaryExpression unary = cast(UnaryExpression) ae.assertion;
35 		if (unary is null)
36 			return;
37 		if (unary.primaryExpression is null)
38 			return;
39 		immutable token = unary.primaryExpression.primary;
40 		immutable skipSwitch = unary.primaryExpression.arrayLiteral !is null
41 			|| unary.primaryExpression.assocArrayLiteral !is null
42 			|| unary.primaryExpression.functionLiteralExpression !is null;
43 		if (!skipSwitch) switch (token.type)
44 		{
45 		case tok!"doubleLiteral":
46 			if (!token.text.removechars("Ll").to!double)
47 				return;
48 			break;
49 		case tok!"floatLiteral":
50 			if (!token.text.removechars("Ff").to!float)
51 				return;
52 			break;
53 		case tok!"idoubleLiteral":
54 		case tok!"ifloatLiteral":
55 		case tok!"irealLiteral":
56 			return; // `to` doesn't support imaginary numbers
57 		case tok!"intLiteral":
58 			if (!token.text.to!int)
59 				return;
60 			break;
61 		case tok!"longLiteral":
62 			if (!token.text.removechars("Ll").to!long)
63 				return;
64 			break;
65 		case tok!"realLiteral":
66 			if (!token.text.to!real)
67 				return;
68 			break;
69 		case tok!"uintLiteral":
70 			if (!token.text.removechars("Uu").to!uint)
71 				return;
72 			break;
73 		case tok!"ulongLiteral":
74 			if (!token.text.removechars("UuLl").to!ulong)
75 				return;
76 			break;
77 		case tok!"characterLiteral":
78 			if (token.text == `'\0'`)
79 				return;
80 			break;
81 		case tok!"dstringLiteral":
82 		case tok!"stringLiteral":
83 		case tok!"wstringLiteral":
84 		case tok!"true":
85 			break;
86 		default:
87 			return;
88 		}
89 		addErrorMessage(ae.line, ae.column, KEY, MESSAGE);
90 	}
91 
92 private:
93 	enum string KEY = "dscanner.suspicious.useless_assert";
94 	enum string MESSAGE = "Assert condition is always true.";
95 }
96 
97 unittest
98 {
99 	import std.stdio : stderr;
100 	import analysis.config : StaticAnalysisConfig, Check;
101 	import std.format : format;
102 
103 	StaticAnalysisConfig sac;
104 	sac.useless_assert_check = Check.enabled;
105 	assertAnalyzerWarnings(q{
106 unittest
107 {
108 	assert(true); // [warn]: %1$s
109 	assert(1); // [warn]: %1$s
110 	assert([10]); // [warn]: %1$s
111 	assert(false);
112 	assert(0);
113 	assert(0.0L);
114 }
115 
116 }}
117 			.format(UselessAssertCheck.MESSAGE), sac);
118 	stderr.writeln("Unittest for UselessAssertCheck passed.");
119 }