120 lines
2.3 KiB
D
120 lines
2.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.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.");
|
|
}
|