Merge pull request #249 from WalterWaldron/fix242
Fix #242 - Warn about unused labels
This commit is contained in:
commit
1ea4f53a38
|
@ -51,6 +51,9 @@ struct StaticAnalysisConfig
|
|||
@INI("Checks for unused variables and function parameters")
|
||||
bool unused_variable_check;
|
||||
|
||||
@INI("Checks for unused labels")
|
||||
bool unused_label_check;
|
||||
|
||||
@INI("Checks for duplicate attributes")
|
||||
bool duplicate_attribute;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import analysis.range;
|
|||
import analysis.ifelsesame;
|
||||
import analysis.constructors;
|
||||
import analysis.unused;
|
||||
import analysis.unused_label;
|
||||
import analysis.duplicate_attribute;
|
||||
import analysis.opequals_without_tohash;
|
||||
import analysis.length_subtraction;
|
||||
|
@ -176,6 +177,7 @@ MessageSet analyze(string fileName, const Module m,
|
|||
if (analysisConfig.backwards_range_check) checks ~= new BackwardsRangeCheck(fileName);
|
||||
if (analysisConfig.if_else_same_check) checks ~= new IfElseSameCheck(fileName);
|
||||
if (analysisConfig.constructor_check) checks ~= new ConstructorCheck(fileName);
|
||||
if (analysisConfig.unused_label_check) checks ~= new UnusedLabelCheck(fileName);
|
||||
if (analysisConfig.unused_variable_check) checks ~= new UnusedVariableCheck(fileName);
|
||||
if (analysisConfig.duplicate_attribute) checks ~= new DuplicateAttributeCheck(fileName);
|
||||
if (analysisConfig.opequals_tohash_check) checks ~= new OpEqualsWithoutToHashCheck(fileName);
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
// 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}c, sac);
|
||||
|
||||
stderr.writeln("Unittest for UnusedLabelCheck passed.");
|
||||
}
|
Loading…
Reference in New Issue