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 override void visit(const BreakStatement breakStatement) 80 { 81 if (breakStatement.label.text.length) 82 labelUsed(breakStatement.label.text); 83 } 84 override void visit(const GotoStatement gotoStatement) 85 { 86 if (gotoStatement.label.text.length) 87 labelUsed(gotoStatement.label.text); 88 } 89 90 private: 91 92 static struct Label 93 { 94 string name; 95 size_t line; 96 size_t column; 97 bool used; 98 } 99 100 Label[string][] stack; 101 102 auto ref current() @property 103 { 104 return stack[$-1]; 105 } 106 107 void pushScope() 108 { 109 stack.length++; 110 } 111 112 void popScope() 113 { 114 foreach (label; current.byValue()) 115 { 116 assert(label.line != size_t.max && label.column != size_t.max); 117 if (!label.used) 118 { 119 addErrorMessage(label.line, label.column, 120 "dscanner.suspicious.unused_label", 121 "Label \"" ~ label.name ~ "\" is not used."); 122 } 123 } 124 stack.length--; 125 } 126 127 void labelUsed(string name) 128 { 129 Label* entry = name in current; 130 if (entry is null) 131 current[name] = Label(name, size_t.max, size_t.max, true); 132 else 133 entry.used = true; 134 } 135 } 136 137 unittest 138 { 139 import analysis.config : StaticAnalysisConfig; 140 import std.stdio : stderr; 141 142 StaticAnalysisConfig sac; 143 sac.unused_label_check = true; 144 assertAnalyzerWarnings(q{ 145 int testUnusedLabel() 146 { 147 int x = 0; 148 A: // [warn]: Label "A" is not used. 149 if (x) goto B; 150 x++; 151 B: 152 goto C; 153 void foo() 154 { 155 C: // [warn]: Label "C" is not used. 156 return; 157 } 158 C: 159 void bar() 160 { 161 goto D; 162 D: 163 return; 164 } 165 D: // [warn]: Label "D" is not used. 166 goto E; 167 () { 168 E: // [warn]: Label "E" is not used. 169 return; 170 }(); 171 E: 172 () { 173 goto F; 174 F: 175 return; 176 }(); 177 F: // [warn]: Label "F" is not used. 178 return x; 179 G: // [warn]: Label "G" is not used. 180 } 181 }}, sac); 182 183 stderr.writeln("Unittest for UnusedLabelCheck passed."); 184 }