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.unused_label; 6 7 import dparse.ast; 8 import dparse.lexer; 9 import analysis.base; 10 import analysis.helpers; 11 import dsymbol.scope_ : Scope; 12 13 class UnusedLabelCheck : BaseAnalyzer 14 { 15 alias visit = BaseAnalyzer.visit; 16 17 this(string fileName, const(Scope)* sc) 18 { 19 super(fileName, sc); 20 } 21 22 override void visit(const Module mod) 23 { 24 pushScope(); 25 mod.accept(this); 26 popScope(); 27 } 28 29 override void visit(const FunctionBody functionBody) 30 { 31 if (functionBody.blockStatement !is null) 32 { 33 pushScope(); 34 functionBody.blockStatement.accept(this); 35 popScope(); 36 } 37 if (functionBody.bodyStatement !is null) 38 { 39 pushScope(); 40 functionBody.bodyStatement.accept(this); 41 popScope(); 42 } 43 if (functionBody.outStatement !is null) 44 { 45 pushScope(); 46 functionBody.outStatement.accept(this); 47 popScope(); 48 } 49 if (functionBody.inStatement !is null) 50 { 51 pushScope(); 52 functionBody.inStatement.accept(this); 53 popScope(); 54 } 55 } 56 57 override void visit(const LabeledStatement labeledStatement) 58 { 59 auto token = &labeledStatement.identifier; 60 Label* label = token.text in current; 61 if (label is null) 62 { 63 current[token.text] = Label(token.text, token.line, token.column, false); 64 } 65 else 66 { 67 label.line = token.line; 68 label.column = token.column; 69 } 70 if (labeledStatement.declarationOrStatement !is null) 71 labeledStatement.declarationOrStatement.accept(this); 72 } 73 74 override void visit(const ContinueStatement contStatement) 75 { 76 if (contStatement.label.text.length) 77 labelUsed(contStatement.label.text); 78 } 79 80 override void visit(const BreakStatement breakStatement) 81 { 82 if (breakStatement.label.text.length) 83 labelUsed(breakStatement.label.text); 84 } 85 86 override void visit(const GotoStatement gotoStatement) 87 { 88 if (gotoStatement.label.text.length) 89 labelUsed(gotoStatement.label.text); 90 } 91 92 private: 93 94 static struct Label 95 { 96 string name; 97 size_t line; 98 size_t column; 99 bool used; 100 } 101 102 Label[string][] stack; 103 104 auto ref current() @property 105 { 106 return stack[$ - 1]; 107 } 108 109 void pushScope() 110 { 111 stack.length++; 112 } 113 114 void popScope() 115 { 116 foreach (label; current.byValue()) 117 { 118 assert(label.line != size_t.max && label.column != size_t.max); 119 if (!label.used) 120 { 121 addErrorMessage(label.line, label.column, "dscanner.suspicious.unused_label", 122 "Label \"" ~ label.name ~ "\" is not used."); 123 } 124 } 125 stack.length--; 126 } 127 128 void labelUsed(string name) 129 { 130 Label* entry = name in current; 131 if (entry is null) 132 current[name] = Label(name, size_t.max, size_t.max, true); 133 else 134 entry.used = true; 135 } 136 } 137 138 unittest 139 { 140 import analysis.config : StaticAnalysisConfig; 141 import std.stdio : stderr; 142 143 StaticAnalysisConfig sac; 144 sac.unused_label_check = true; 145 assertAnalyzerWarnings(q{ 146 int testUnusedLabel() 147 { 148 int x = 0; 149 A: // [warn]: Label "A" is not used. 150 if (x) goto B; 151 x++; 152 B: 153 goto C; 154 void foo() 155 { 156 C: // [warn]: Label "C" is not used. 157 return; 158 } 159 C: 160 void bar() 161 { 162 goto D; 163 D: 164 return; 165 } 166 D: // [warn]: Label "D" is not used. 167 goto E; 168 () { 169 E: // [warn]: Label "E" is not used. 170 return; 171 }(); 172 E: 173 () { 174 goto F; 175 F: 176 return; 177 }(); 178 F: // [warn]: Label "F" is not used. 179 return x; 180 G: // [warn]: Label "G" is not used. 181 } 182 }}, sac); 183 184 stderr.writeln("Unittest for UnusedLabelCheck passed."); 185 }