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