1 // Distributed under the Boost Software License, Version 1.0.
2 //    (See accompanying file LICENSE_1_0.txt or copy at
3 //          http://www.boost.org/LICENSE_1_0.txt)
4 
5 module analysis.label_var_same_name_check;
6 
7 import dparse.ast;
8 import dparse.lexer;
9 import dsymbol.scope_ : Scope;
10 import analysis.base;
11 import analysis.helpers;
12 
13 /**
14  * Checks for labels and variables that have the same name.
15  */
16 class LabelVarNameCheck : BaseAnalyzer
17 {
18 	this(string fileName, const(Scope)* sc)
19 	{
20 		super(fileName, sc);
21 	}
22 
23 	mixin ScopedVisit!Module;
24 	mixin ScopedVisit!BlockStatement;
25 	mixin ScopedVisit!StructBody;
26 	mixin ScopedVisit!CaseStatement;
27 	mixin ScopedVisit!ForStatement;
28 	mixin ScopedVisit!IfStatement;
29 	mixin ScopedVisit!TemplateDeclaration;
30 
31 	override void visit(const VariableDeclaration var)
32 	{
33 		foreach (dec; var.declarators)
34 			duplicateCheck(dec.name, false, conditionalDepth > 0);
35 	}
36 
37 	override void visit(const LabeledStatement labeledStatement)
38 	{
39 		duplicateCheck(labeledStatement.identifier, true, conditionalDepth > 0);
40 		if (labeledStatement.declarationOrStatement !is null)
41 			labeledStatement.declarationOrStatement.accept(this);
42 	}
43 
44 	override void visit(const ConditionalDeclaration condition)
45 	{
46 		if (condition.falseDeclarations.length > 0)
47 			++conditionalDepth;
48 		condition.accept(this);
49 		if (condition.falseDeclarations.length > 0)
50 			--conditionalDepth;
51 	}
52 
53 	alias visit = BaseAnalyzer.visit;
54 
55 private:
56 
57 	Thing[string][] stack;
58 
59 	template ScopedVisit(NodeType)
60 	{
61 		override void visit(const NodeType n)
62 		{
63 			pushScope();
64 			n.accept(this);
65 			popScope();
66 		}
67 	}
68 
69 	void duplicateCheck(const Token name, bool fromLabel, bool isConditional)
70 	{
71 		import std.conv : to;
72 		import std.range : retro;
73 
74 		size_t i = 0;
75 		foreach (s; retro(stack))
76 		{
77 			const(Thing)* thing = name.text in s;
78 			if (thing is null)
79 				currentScope[name.text] = Thing(name.text, name.line, name.column, !fromLabel /+, isConditional+/ );
80 			else if (i != 0 || !isConditional)
81 			{
82 				immutable thisKind = fromLabel ? "Label" : "Variable";
83 				immutable otherKind = thing.isVar ? "variable" : "label";
84 				addErrorMessage(name.line, name.column, "dscanner.suspicious.label_var_same_name",
85 						thisKind ~ " \"" ~ name.text ~ "\" has the same name as a "
86 						~ otherKind ~ " defined on line " ~ to!string(thing.line) ~ ".");
87 			}
88 			++i;
89 		}
90 	}
91 
92 	static struct Thing
93 	{
94 		string name;
95 		size_t line;
96 		size_t column;
97 		bool isVar;
98 		//bool isConditional;
99 	}
100 
101 	ref currentScope() @property
102 	{
103 		return stack[$ - 1];
104 	}
105 
106 	void pushScope()
107 	{
108 		stack.length++;
109 	}
110 
111 	void popScope()
112 	{
113 		stack.length--;
114 	}
115 
116 	int conditionalDepth;
117 }
118 
119 unittest
120 {
121 	import analysis.config : StaticAnalysisConfig;
122 	import std.stdio : stderr;
123 
124 	StaticAnalysisConfig sac;
125 	sac.label_var_same_name_check = true;
126 	assertAnalyzerWarnings(q{
127 unittest
128 {
129 blah:
130 	int blah; // [warn]: Variable "blah" has the same name as a label defined on line 4.
131 }
132 int blah;
133 unittest
134 {
135 	static if (stuff)
136 		int a;
137 	int a; // [warn]: Variable "a" has the same name as a variable defined on line 11.
138 }
139 
140 unittest
141 {
142 	static if (stuff)
143 		int a = 10;
144 	else
145 		int a = 20;
146 }
147 
148 unittest
149 {
150 	static if (stuff)
151 		int a = 10;
152 	else
153 		int a = 20;
154 	int a; // [warn]: Variable "a" has the same name as a variable defined on line 28.
155 }
156 template T(stuff)
157 {
158 	int b;
159 }
160 
161 void main(string[] args)
162 {
163 	for (int a = 0; a < 10; a++)
164 		things(a);
165 
166 	for (int a = 0; a < 10; a++)
167 		things(a);
168 	int b;
169 }
170 
171 }}, sac);
172 	stderr.writeln("Unittest for LabelVarNameCheck passed.");
173 }