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 std.d.ast;
8 import std.d.lexer;
9 
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)
19 	{
20 		super(fileName);
21 	}
22 
23 	override void visit(const Module mod)
24 	{
25 		pushScope();
26 		mod.accept(this);
27 		popScope();
28 	}
29 
30 	override void visit(const BlockStatement block)
31 	{
32 		pushScope();
33 		block.accept(this);
34 		popScope();
35 	}
36 
37 	override void visit(const StructBody structBody)
38 	{
39 		pushScope();
40 		structBody.accept(this);
41 		popScope();
42 	}
43 
44 	override void visit(const VariableDeclaration var)
45 	{
46 		foreach (dec; var.declarators)
47 			duplicateCheck(dec.name, false);
48 	}
49 
50 	override void visit(const LabeledStatement labeledStatement)
51 	{
52 		duplicateCheck(labeledStatement.identifier, true);
53 		if (labeledStatement.declarationOrStatement !is null)
54 			labeledStatement.declarationOrStatement.accept(this);
55 	}
56 
57 	alias visit = BaseAnalyzer.visit;
58 
59 private:
60 
61 	Thing[string][] stack;
62 
63 	void duplicateCheck(const Token name, bool fromLabel)
64 	{
65 		import std.conv : to;
66 		const(Thing)* thing = name.text in currentScope;
67 		if (thing is null)
68 			currentScope[name.text] = Thing(name.text, name.line, name.column, false);
69 		else
70 		{
71 			immutable thisKind = fromLabel ? "Label" : "Variable";
72 			immutable otherKind = thing.isVar ? "variable" : "label";
73 			addErrorMessage(name.line, name.column, "dscanner.suspicious.label_var_same_name",
74 				thisKind ~ " \"" ~ name.text ~ "\" has the same name as a "
75 				~ otherKind ~ " defined on line " ~ to!string(thing.line) ~ ".");
76 		}
77 	}
78 
79 	static struct Thing
80 	{
81 		string name;
82 		size_t line;
83 		size_t column;
84 		bool isVar;
85 	}
86 
87 	ref currentScope() @property
88 	{
89 		return stack[$-1];
90 	}
91 
92 	void pushScope()
93 	{
94 		stack.length++;
95 	}
96 
97 	void popScope()
98 	{
99 		stack.length--;
100 	}
101 }
102 
103 unittest
104 {
105 	import analysis.config : StaticAnalysisConfig;
106 	import std.stdio : stderr;
107 
108 	StaticAnalysisConfig sac;
109 	sac.label_var_same_name_check = true;
110 	assertAnalyzerWarnings(q{
111 unittest
112 {
113 blah:
114 	int blah; // [warn]: Variable "blah" has the same name as a label defined on line 4.
115 }
116 int blah;
117 	}}, sac);
118 	stderr.writeln("Unittest for LabelVarNameCheck passed.");
119 }