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 }