Implement #243
This commit is contained in:
parent
77a7b4f5d2
commit
cad1f05367
|
@ -92,4 +92,7 @@ struct StaticAnalysisConfig
|
||||||
|
|
||||||
@INI("Checks for redundant parenthesis")
|
@INI("Checks for redundant parenthesis")
|
||||||
bool redundant_parens_check;
|
bool redundant_parens_check;
|
||||||
|
|
||||||
|
@INI("Checks for labels with the same name as variables")
|
||||||
|
bool label_var_same_name_check;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
// 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.label_var_same_name_check;
|
||||||
|
|
||||||
|
import std.d.ast;
|
||||||
|
import std.d.lexer;
|
||||||
|
|
||||||
|
import analysis.base;
|
||||||
|
import analysis.helpers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for labels and variables that have the same name.
|
||||||
|
*/
|
||||||
|
class LabelVarNameCheck : BaseAnalyzer
|
||||||
|
{
|
||||||
|
this(string fileName)
|
||||||
|
{
|
||||||
|
super(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const Module mod)
|
||||||
|
{
|
||||||
|
pushScope();
|
||||||
|
mod.accept(this);
|
||||||
|
popScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const BlockStatement block)
|
||||||
|
{
|
||||||
|
pushScope();
|
||||||
|
block.accept(this);
|
||||||
|
popScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const StructBody structBody)
|
||||||
|
{
|
||||||
|
pushScope();
|
||||||
|
structBody.accept(this);
|
||||||
|
popScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const VariableDeclaration var)
|
||||||
|
{
|
||||||
|
foreach (dec; var.declarators)
|
||||||
|
duplicateCheck(dec.name, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
override void visit(const LabeledStatement labeledStatement)
|
||||||
|
{
|
||||||
|
duplicateCheck(labeledStatement.identifier, true);
|
||||||
|
if (labeledStatement.declarationOrStatement !is null)
|
||||||
|
labeledStatement.declarationOrStatement.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
alias visit = BaseAnalyzer.visit;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Thing[string][] stack;
|
||||||
|
|
||||||
|
void duplicateCheck(const Token name, bool fromLabel)
|
||||||
|
{
|
||||||
|
import std.conv : to;
|
||||||
|
const(Thing)* thing = name.text in currentScope;
|
||||||
|
if (thing is null)
|
||||||
|
currentScope[name.text] = Thing(name.text, name.line, name.column, false);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
immutable thisKind = fromLabel ? "Label" : "Variable";
|
||||||
|
immutable otherKind = thing.isVar ? "variable" : "label";
|
||||||
|
addErrorMessage(name.line, name.column, "dscanner.suspicious.label_var_same_name",
|
||||||
|
thisKind ~ " \"" ~ name.text ~ "\" has the same name as a "
|
||||||
|
~ otherKind ~ " defined on line " ~ to!string(thing.line) ~ ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct Thing
|
||||||
|
{
|
||||||
|
string name;
|
||||||
|
size_t line;
|
||||||
|
size_t column;
|
||||||
|
bool isVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref currentScope() @property
|
||||||
|
{
|
||||||
|
return stack[$-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushScope()
|
||||||
|
{
|
||||||
|
stack.length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void popScope()
|
||||||
|
{
|
||||||
|
stack.length--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
import analysis.config : StaticAnalysisConfig;
|
||||||
|
import std.stdio : stderr;
|
||||||
|
|
||||||
|
StaticAnalysisConfig sac;
|
||||||
|
sac.label_var_same_name_check = true;
|
||||||
|
assertAnalyzerWarnings(q{
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
blah:
|
||||||
|
int blah; // [warn]: Variable "blah" has the same name as a label defined on line 4.
|
||||||
|
}
|
||||||
|
int blah;
|
||||||
|
}c, sac);
|
||||||
|
stderr.writeln("Unittest for LabelVarNameCheck passed.");
|
||||||
|
}
|
|
@ -43,6 +43,7 @@ import analysis.local_imports;
|
||||||
import analysis.unmodified;
|
import analysis.unmodified;
|
||||||
import analysis.if_statements;
|
import analysis.if_statements;
|
||||||
import analysis.redundant_parens;
|
import analysis.redundant_parens;
|
||||||
|
import analysis.label_var_same_name_check;
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
|
@ -196,6 +197,7 @@ MessageSet analyze(string fileName, const Module m,
|
||||||
if (analysisConfig.local_import_check) checks ~= new LocalImportCheck(fileName);
|
if (analysisConfig.local_import_check) checks ~= new LocalImportCheck(fileName);
|
||||||
if (analysisConfig.could_be_immutable_check) checks ~= new UnmodifiedFinder(fileName);
|
if (analysisConfig.could_be_immutable_check) checks ~= new UnmodifiedFinder(fileName);
|
||||||
if (analysisConfig.redundant_parens_check) checks ~= new RedundantParenCheck(fileName);
|
if (analysisConfig.redundant_parens_check) checks ~= new RedundantParenCheck(fileName);
|
||||||
|
if (analysisConfig.label_var_same_name_check) checks ~= new LabelVarNameCheck(fileName);
|
||||||
version(none) if (analysisConfig.redundant_if_check) checks ~= new IfStatementCheck(fileName);
|
version(none) if (analysisConfig.redundant_if_check) checks ~= new IfStatementCheck(fileName);
|
||||||
|
|
||||||
foreach (check; checks)
|
foreach (check; checks)
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
module analysis.unused_label;
|
module analysis.unused_label;
|
||||||
|
|
||||||
import std.stdio;
|
|
||||||
import std.d.ast;
|
import std.d.ast;
|
||||||
import std.d.lexer;
|
import std.d.lexer;
|
||||||
import analysis.base;
|
import analysis.base;
|
||||||
|
@ -19,39 +18,11 @@ class UnusedLabelCheck : BaseAnalyzer
|
||||||
super(fileName);
|
super(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct Label
|
override void visit(const Module mod)
|
||||||
{
|
{
|
||||||
string name;
|
pushScope();
|
||||||
size_t line;
|
mod.accept(this);
|
||||||
size_t column;
|
popScope();
|
||||||
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)
|
override void visit(const FunctionBody functionBody)
|
||||||
|
@ -99,15 +70,6 @@ class UnusedLabelCheck : BaseAnalyzer
|
||||||
labeledStatement.declarationOrStatement.accept(this);
|
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)
|
override void visit(const ContinueStatement contStatement)
|
||||||
{
|
{
|
||||||
if (contStatement.label.text.length)
|
if (contStatement.label.text.length)
|
||||||
|
@ -123,11 +85,58 @@ class UnusedLabelCheck : BaseAnalyzer
|
||||||
if (gotoStatement.label.text.length)
|
if (gotoStatement.label.text.length)
|
||||||
labelUsed(gotoStatement.label.text);
|
labelUsed(gotoStatement.label.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
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--;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
import analysis.config : StaticAnalysisConfig;
|
import analysis.config : StaticAnalysisConfig;
|
||||||
|
import std.stdio : stderr;
|
||||||
|
|
||||||
StaticAnalysisConfig sac;
|
StaticAnalysisConfig sac;
|
||||||
sac.unused_label_check = true;
|
sac.unused_label_check = true;
|
||||||
|
|
Loading…
Reference in New Issue