175 lines
3.3 KiB
D
175 lines
3.3 KiB
D
// Distributed under the Boost Software License, Version 1.0.
|
|
// (See accompanying file LICENSE_1_0.txt or copy at
|
|
// http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
module analysis.unused_label;
|
|
|
|
import std.stdio;
|
|
import std.d.ast;
|
|
import std.d.lexer;
|
|
import analysis.base;
|
|
import analysis.helpers;
|
|
|
|
class UnusedLabelCheck : BaseAnalyzer
|
|
{
|
|
alias visit = BaseAnalyzer.visit;
|
|
|
|
this(string fileName)
|
|
{
|
|
super(fileName);
|
|
}
|
|
|
|
static struct Label
|
|
{
|
|
string name;
|
|
size_t line;
|
|
size_t column;
|
|
bool used;
|
|
}
|
|
|
|
Label[string][] stack;
|
|
|
|
auto ref current() @property
|
|
{
|
|
return stack[$-1];
|
|
}
|
|
|
|
void pushScope()
|
|
{
|
|
stack.length++;
|
|
}
|
|
|
|
void popScope()
|
|
{
|
|
foreach (label; current.byValue())
|
|
{
|
|
assert(label.line != size_t.max && label.column != size_t.max);
|
|
if (!label.used)
|
|
{
|
|
addErrorMessage(label.line, label.column,
|
|
"dscanner.suspicious.unused_label",
|
|
"Label \"" ~ label.name ~ "\" is not used.");
|
|
}
|
|
}
|
|
stack.length--;
|
|
}
|
|
|
|
override void visit(const FunctionBody functionBody)
|
|
{
|
|
if (functionBody.blockStatement !is null)
|
|
{
|
|
pushScope();
|
|
functionBody.blockStatement.accept(this);
|
|
popScope();
|
|
}
|
|
if (functionBody.bodyStatement !is null)
|
|
{
|
|
pushScope();
|
|
functionBody.bodyStatement.accept(this);
|
|
popScope();
|
|
}
|
|
if (functionBody.outStatement !is null)
|
|
{
|
|
pushScope();
|
|
functionBody.outStatement.accept(this);
|
|
popScope();
|
|
}
|
|
if (functionBody.inStatement !is null)
|
|
{
|
|
pushScope();
|
|
functionBody.inStatement.accept(this);
|
|
popScope();
|
|
}
|
|
}
|
|
|
|
override void visit(const LabeledStatement labeledStatement)
|
|
{
|
|
auto token = &labeledStatement.identifier;
|
|
Label* label = token.text in current;
|
|
if (label is null)
|
|
{
|
|
current[token.text] = Label(token.text, token.line, token.column, false);
|
|
}
|
|
else
|
|
{
|
|
label.line = token.line;
|
|
label.column = token.column;
|
|
}
|
|
if (labeledStatement.declarationOrStatement !is null)
|
|
labeledStatement.declarationOrStatement.accept(this);
|
|
}
|
|
|
|
void labelUsed(string name)
|
|
{
|
|
Label* entry = name in current;
|
|
if (entry is null)
|
|
current[name] = Label(name, size_t.max, size_t.max, true);
|
|
else
|
|
entry.used = true;
|
|
}
|
|
|
|
override void visit(const ContinueStatement contStatement)
|
|
{
|
|
if (contStatement.label.text.length)
|
|
labelUsed(contStatement.label.text);
|
|
}
|
|
override void visit(const BreakStatement breakStatement)
|
|
{
|
|
if (breakStatement.label.text.length)
|
|
labelUsed(breakStatement.label.text);
|
|
}
|
|
override void visit(const GotoStatement gotoStatement)
|
|
{
|
|
if (gotoStatement.label.text.length)
|
|
labelUsed(gotoStatement.label.text);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import analysis.config : StaticAnalysisConfig;
|
|
|
|
StaticAnalysisConfig sac;
|
|
sac.unused_label_check = true;
|
|
assertAnalyzerWarnings(q{
|
|
int testUnusedLabel()
|
|
{
|
|
int x = 0;
|
|
A: // [warn]: Label "A" is not used.
|
|
if (x) goto B;
|
|
x++;
|
|
B:
|
|
goto C;
|
|
void foo()
|
|
{
|
|
C: // [warn]: Label "C" is not used.
|
|
return;
|
|
}
|
|
C:
|
|
void bar()
|
|
{
|
|
goto D;
|
|
D:
|
|
return;
|
|
}
|
|
D: // [warn]: Label "D" is not used.
|
|
goto E;
|
|
() {
|
|
E: // [warn]: Label "E" is not used.
|
|
return;
|
|
}();
|
|
E:
|
|
() {
|
|
goto F;
|
|
F:
|
|
return;
|
|
}();
|
|
F: // [warn]: Label "F" is not used.
|
|
return x;
|
|
G: // [warn]: Label "G" is not used.
|
|
}
|
|
}c, sac);
|
|
|
|
stderr.writeln("Unittest for UnusedLabelCheck passed.");
|
|
}
|