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