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