D-Scanner/src/analysis/unused_label.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.");
}