1 //          Copyright Brian Schott (Hackerpilot) 2014.
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.length_subtraction;
7 
8 import std.stdio;
9 
10 import dparse.ast;
11 import dparse.lexer;
12 import dscanner.analysis.base;
13 import dscanner.analysis.helpers;
14 import dsymbol.scope_;
15 
16 /**
17  * Checks for subtraction from a .length property. This is usually a bug.
18  */
19 final class LengthSubtractionCheck : BaseAnalyzer
20 {
21 	alias visit = BaseAnalyzer.visit;
22 
23 	mixin AnalyzerInfo!"length_subtraction_check";
24 
25 	this(string fileName, const(Scope)* sc, bool skipTests = false)
26 	{
27 		super(fileName, sc, skipTests);
28 	}
29 
30 	override void visit(const AddExpression addExpression)
31 	{
32 		if (addExpression.operator == tok!"-")
33 		{
34 			const UnaryExpression l = cast(const UnaryExpression) addExpression.left;
35 			const UnaryExpression r = cast(const UnaryExpression) addExpression.right;
36 			if (l is null || r is null)
37 			{
38 				//				stderr.writeln(__FILE__, " ", __LINE__);
39 				goto end;
40 			}
41 			if (r.primaryExpression is null || r.primaryExpression.primary.type != tok!"intLiteral")
42 			{
43 				//				stderr.writeln(__FILE__, " ", __LINE__);
44 				goto end;
45 			}
46 			if (l.identifierOrTemplateInstance is null
47 					|| l.identifierOrTemplateInstance.identifier.text != "length")
48 			{
49 				//				stderr.writeln(__FILE__, " ", __LINE__);
50 				goto end;
51 			}
52 			const(Token) token = l.identifierOrTemplateInstance.identifier;
53 			addErrorMessage(token.line, token.column, "dscanner.suspicious.length_subtraction",
54 					"Avoid subtracting from '.length' as it may be unsigned.");
55 		}
56 	end:
57 		addExpression.accept(this);
58 	}
59 }
60 
61 unittest
62 {
63 	import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
64 
65 	StaticAnalysisConfig sac = disabledConfig();
66 	sac.length_subtraction_check = Check.enabled;
67 	assertAnalyzerWarnings(q{
68 		void testSizeT()
69 		{
70 			if (i < a.length - 1) // [warn]: Avoid subtracting from '.length' as it may be unsigned.
71 				writeln("something");
72 		}
73 	}}, sac);
74 	stderr.writeln("Unittest for IfElseSameCheck passed.");
75 }