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 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 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, conditionalDepth > 0); 48 } 49 50 override void visit(const LabeledStatement labeledStatement) 51 { 52 duplicateCheck(labeledStatement.identifier, true, conditionalDepth > 0); 53 if (labeledStatement.declarationOrStatement !is null) 54 labeledStatement.declarationOrStatement.accept(this); 55 } 56 57 override void visit(const ConditionalDeclaration condition) 58 { 59 if (condition.falseDeclarations.length > 0) 60 ++conditionalDepth; 61 condition.accept(this); 62 if (condition.falseDeclarations.length > 0) 63 --conditionalDepth; 64 } 65 66 alias visit = BaseAnalyzer.visit; 67 68 private: 69 70 Thing[string][] stack; 71 72 void duplicateCheck(const Token name, bool fromLabel, bool isConditional) 73 { 74 import std.conv : to; 75 import std.range : retro; 76 77 size_t i = 0; 78 foreach (s; retro(stack)) 79 { 80 const(Thing)* thing = name.text in s; 81 if (thing is null) 82 currentScope[name.text] = Thing(name.text, name.line, name.column, 83 !fromLabel/+, isConditional+/); 84 else if (i != 0 || !isConditional) 85 { 86 immutable thisKind = fromLabel ? "Label" : "Variable"; 87 immutable otherKind = thing.isVar ? "variable" : "label"; 88 addErrorMessage(name.line, name.column, "dscanner.suspicious.label_var_same_name", 89 thisKind ~ " \"" ~ name.text ~ "\" has the same name as a " 90 ~ otherKind ~ " defined on line " ~ to!string(thing.line) ~ "."); 91 } 92 ++i; 93 } 94 } 95 96 static struct Thing 97 { 98 string name; 99 size_t line; 100 size_t column; 101 bool isVar; 102 //bool isConditional; 103 } 104 105 ref currentScope() @property 106 { 107 return stack[$-1]; 108 } 109 110 void pushScope() 111 { 112 stack.length++; 113 } 114 115 void popScope() 116 { 117 stack.length--; 118 } 119 120 int conditionalDepth; 121 } 122 123 unittest 124 { 125 import analysis.config : StaticAnalysisConfig; 126 import std.stdio : stderr; 127 128 StaticAnalysisConfig sac; 129 sac.label_var_same_name_check = true; 130 assertAnalyzerWarnings(q{ 131 unittest 132 { 133 blah: 134 int blah; // [warn]: Variable "blah" has the same name as a label defined on line 4. 135 } 136 int blah; 137 unittest 138 { 139 static if (stuff) 140 int a; 141 int a; // [warn]: Variable "a" has the same name as a variable defined on line 11. 142 } 143 144 unittest 145 { 146 static if (stuff) 147 int a = 10; 148 else 149 int a = 20; 150 } 151 152 unittest 153 { 154 static if (stuff) 155 int a = 10; 156 else 157 int a = 20; 158 int a; // [warn]: Variable "a" has the same name as a variable defined on line 28. 159 } 160 }}, sac); 161 stderr.writeln("Unittest for LabelVarNameCheck passed."); 162 }