Skip to content

Commit 656d6fc

Browse files
committed
Merge pull request #267 from Hackerpilot/0.2.0-dev
0.2.0 dev
2 parents d251a7a + 7a0eb5f commit 656d6fc

11 files changed

+288
-104
lines changed

libdparse

src/analysis/config.d

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,7 @@ struct StaticAnalysisConfig
9292

9393
@INI("Checks for redundant parenthesis")
9494
bool redundant_parens_check;
95+
96+
@INI("Checks for labels with the same name as variables")
97+
bool label_var_same_name_check;
9598
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Distributed under the Boost Software License, Version 1.0.
2+
// (See accompanying file LICENSE_1_0.txt or copy at
3+
// http://www.boost.org/LICENSE_1_0.txt)
4+
5+
module analysis.label_var_same_name_check;
6+
7+
import std.d.ast;
8+
import std.d.lexer;
9+
10+
import analysis.base;
11+
import analysis.helpers;
12+
13+
/**
14+
* Checks for labels and variables that have the same name.
15+
*/
16+
class LabelVarNameCheck : BaseAnalyzer
17+
{
18+
this(string fileName)
19+
{
20+
super(fileName);
21+
}
22+
23+
override void visit(const Module mod)
24+
{
25+
pushScope();
26+
mod.accept(this);
27+
popScope();
28+
}
29+
30+
override void visit(const BlockStatement block)
31+
{
32+
pushScope();
33+
block.accept(this);
34+
popScope();
35+
}
36+
37+
override void visit(const StructBody structBody)
38+
{
39+
pushScope();
40+
structBody.accept(this);
41+
popScope();
42+
}
43+
44+
override void visit(const VariableDeclaration var)
45+
{
46+
foreach (dec; var.declarators)
47+
duplicateCheck(dec.name, false);
48+
}
49+
50+
override void visit(const LabeledStatement labeledStatement)
51+
{
52+
duplicateCheck(labeledStatement.identifier, true);
53+
if (labeledStatement.declarationOrStatement !is null)
54+
labeledStatement.declarationOrStatement.accept(this);
55+
}
56+
57+
alias visit = BaseAnalyzer.visit;
58+
59+
private:
60+
61+
Thing[string][] stack;
62+
63+
void duplicateCheck(const Token name, bool fromLabel)
64+
{
65+
import std.conv : to;
66+
const(Thing)* thing = name.text in currentScope;
67+
if (thing is null)
68+
currentScope[name.text] = Thing(name.text, name.line, name.column, false);
69+
else
70+
{
71+
immutable thisKind = fromLabel ? "Label" : "Variable";
72+
immutable otherKind = thing.isVar ? "variable" : "label";
73+
addErrorMessage(name.line, name.column, "dscanner.suspicious.label_var_same_name",
74+
thisKind ~ " \"" ~ name.text ~ "\" has the same name as a "
75+
~ otherKind ~ " defined on line " ~ to!string(thing.line) ~ ".");
76+
}
77+
}
78+
79+
static struct Thing
80+
{
81+
string name;
82+
size_t line;
83+
size_t column;
84+
bool isVar;
85+
}
86+
87+
ref currentScope() @property
88+
{
89+
return stack[$-1];
90+
}
91+
92+
void pushScope()
93+
{
94+
stack.length++;
95+
}
96+
97+
void popScope()
98+
{
99+
stack.length--;
100+
}
101+
}
102+
103+
unittest
104+
{
105+
import analysis.config : StaticAnalysisConfig;
106+
import std.stdio : stderr;
107+
108+
StaticAnalysisConfig sac;
109+
sac.label_var_same_name_check = true;
110+
assertAnalyzerWarnings(q{
111+
unittest
112+
{
113+
blah:
114+
int blah; // [warn]: Variable "blah" has the same name as a label defined on line 4.
115+
}
116+
int blah;
117+
}c, sac);
118+
stderr.writeln("Unittest for LabelVarNameCheck passed.");
119+
}

src/analysis/run.d

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import analysis.local_imports;
4343
import analysis.unmodified;
4444
import analysis.if_statements;
4545
import analysis.redundant_parens;
46+
import analysis.label_var_same_name_check;
4647

4748
bool first = true;
4849

@@ -115,8 +116,11 @@ void generateReport(string[] fileNames, const StaticAnalysisConfig config)
115116
writeln("}");
116117
}
117118

118-
/// For multiple files
119-
/// Returns: true if there were errors
119+
/**
120+
* For multiple files
121+
*
122+
* Returns: true if there were errors or if there were warnings and `staticAnalyze` was true.
123+
*/
120124
bool analyze(string[] fileNames, const StaticAnalysisConfig config, bool staticAnalyze = true)
121125
{
122126
bool hasErrors = false;
@@ -129,10 +133,11 @@ bool analyze(string[] fileNames, const StaticAnalysisConfig config, bool staticA
129133
ParseAllocator p = new ParseAllocator;
130134
StringCache cache = StringCache(StringCache.defaultBucketCount);
131135
uint errorCount = 0;
136+
uint warningCount = 0;
132137
const Module m = parseModule(fileName, code, p, cache, false, null,
133-
&errorCount, null);
138+
&errorCount, &warningCount);
134139
assert (m);
135-
if (errorCount > 0)
140+
if (errorCount > 0 || (staticAnalyze && warningCount > 0))
136141
hasErrors = true;
137142
MessageSet results = analyze(fileName, m, config, staticAnalyze);
138143
if (results is null)
@@ -192,6 +197,7 @@ MessageSet analyze(string fileName, const Module m,
192197
if (analysisConfig.local_import_check) checks ~= new LocalImportCheck(fileName);
193198
if (analysisConfig.could_be_immutable_check) checks ~= new UnmodifiedFinder(fileName);
194199
if (analysisConfig.redundant_parens_check) checks ~= new RedundantParenCheck(fileName);
200+
if (analysisConfig.label_var_same_name_check) checks ~= new LabelVarNameCheck(fileName);
195201
version(none) if (analysisConfig.redundant_if_check) checks ~= new IfStatementCheck(fileName);
196202

197203
foreach (check; checks)

src/analysis/undocumented.d

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import std.d.ast;
99
import std.d.lexer;
1010
import analysis.base;
1111

12+
import std.regex : ctRegex, matchAll;
1213
import std.stdio;
1314

1415
/**
@@ -38,13 +39,19 @@ class UndocumentedDeclarationCheck : BaseAnalyzer
3839
if (isProtection(attr.attribute.type))
3940
set(attr.attribute.type);
4041
else if (attr.attribute == tok!"override")
41-
{
4242
setOverride(true);
43-
}
43+
else if (attr.deprecated_ !is null)
44+
setDeprecated(true);
45+
else if (attr.atAttribute !is null && attr.atAttribute.identifier.text == "disable")
46+
setDisabled(true);
4447
}
4548

4649
immutable bool shouldPop = dec.attributeDeclaration is null;
4750
immutable bool prevOverride = getOverride();
51+
immutable bool prevDisabled = getDisabled();
52+
immutable bool prevDeprecated = getDeprecated();
53+
bool dis = false;
54+
bool dep = false;
4855
bool ovr = false;
4956
bool pushed = false;
5057
foreach (attribute; dec.attributes)
@@ -61,14 +68,26 @@ class UndocumentedDeclarationCheck : BaseAnalyzer
6168
}
6269
else if (attribute.attribute == tok!"override")
6370
ovr = true;
71+
else if (attribute.deprecated_ !is null)
72+
dep = true;
73+
else if (attribute.atAttribute !is null && attribute.atAttribute.identifier.text == "disable")
74+
dis = true;
6475
}
6576
if (ovr)
6677
setOverride(true);
78+
if (dis)
79+
setDisabled(true);
80+
if (dep)
81+
setDeprecated(true);
6782
dec.accept(this);
6883
if (shouldPop && pushed)
6984
pop();
7085
if (ovr)
7186
setOverride(prevOverride);
87+
if (dis)
88+
setDisabled(prevDisabled);
89+
if (dep)
90+
setDeprecated(prevDeprecated);
7291
}
7392

7493
override void visit(const VariableDeclaration variable)
@@ -92,14 +111,15 @@ class UndocumentedDeclarationCheck : BaseAnalyzer
92111
override void visit(const ConditionalDeclaration cond)
93112
{
94113
const VersionCondition ver = cond.compileCondition.versionCondition;
95-
if (ver is null || ver.token != tok!"unittest" && ver.token.text != "none")
114+
if ((ver is null || ver.token != tok!"unittest") && ver.token.text != "none")
96115
cond.accept(this);
97116
else if (cond.falseDeclaration !is null)
98117
visit(cond.falseDeclaration);
99118
}
100119

101120
override void visit(const FunctionBody fb) {}
102121
override void visit(const Unittest u) {}
122+
override void visit(const TraitsExpression t) {}
103123

104124
mixin V!ClassDeclaration;
105125
mixin V!InterfaceDeclaration;
@@ -156,8 +176,7 @@ private:
156176

157177
static bool isGetterOrSetter(string name)
158178
{
159-
import std.algorithm:startsWith;
160-
return name.startsWith("get") || name.startsWith("set");
179+
return !matchAll(name, getSetRe).empty;
161180
}
162181

163182
static bool isProperty(const FunctionDeclaration dec)
@@ -190,9 +209,30 @@ private:
190209
stack[$ - 1].isOverride = o;
191210
}
192211

212+
bool getDisabled()
213+
{
214+
return stack[$ - 1].isDisabled;
215+
}
216+
217+
void setDisabled(bool d = true)
218+
{
219+
stack[$ - 1].isDisabled = d;
220+
}
221+
222+
bool getDeprecated()
223+
{
224+
return stack[$ - 1].isDeprecated;
225+
}
226+
227+
void setDeprecated(bool d = true)
228+
{
229+
stack[$ - 1].isDeprecated = d;
230+
}
231+
193232
bool currentIsInteresting()
194233
{
195-
return stack[$ - 1].protection == tok!"public" && !(stack[$ - 1].isOverride);
234+
return stack[$ - 1].protection == tok!"public" && !stack[$ - 1].isOverride
235+
&& !stack[$ - 1].isDisabled && !stack[$ - 1].isDeprecated;
196236
}
197237

198238
void set(IdType p)
@@ -219,6 +259,8 @@ private:
219259
{
220260
IdType protection;
221261
bool isOverride;
262+
bool isDeprecated;
263+
bool isDisabled;
222264
}
223265

224266
ProtectionInfo[] stack;
@@ -232,3 +274,5 @@ private immutable string[] ignoredFunctionNames = [
232274
"toHash",
233275
"main"
234276
];
277+
278+
private enum getSetRe = ctRegex!`^(?:get|set)(?:\p{Lu}|_).*`;

src/analysis/unmodified.d

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ class UnmodifiedFinder:BaseAnalyzer
153153
foreachStatement.declarationOrStatement.accept(this);
154154
}
155155

156+
override void visit(const TraitsExpression)
157+
{
158+
// Issue #266. Ignore everything inside of __traits expressions.
159+
}
160+
156161
private:
157162

158163
template PartsMightModify(T)

src/analysis/unused.d

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,6 @@ class UnusedVariableCheck : BaseAnalyzer
277277
import std.array : array;
278278
if (parameter.name != tok!"")
279279
{
280-
// stderr.writeln("Adding parameter ", parameter.name.text);
281280
immutable bool isRef = canFind(parameter.parameterAttributes, cast(IdType) tok!"ref")
282281
|| canFind(parameter.parameterAttributes, cast(IdType) tok!"in")
283282
|| canFind(parameter.parameterAttributes, cast(IdType) tok!"out");
@@ -317,6 +316,11 @@ class UnusedVariableCheck : BaseAnalyzer
317316
variableUsed(primary.identifierChain.identifiers[0].text);
318317
}
319318

319+
override void visit(const TraitsExpression)
320+
{
321+
// Issue #266. Ignore everything inside of __traits expressions.
322+
}
323+
320324
private:
321325

322326
mixin template PartsUseVariables(NodeType)
@@ -334,13 +338,11 @@ private:
334338
{
335339
if (inAggregateScope)
336340
return;
337-
// stderr.writeln("Adding ", name, " ", isParameter, " ", isRef);
338341
tree[$ - 1].insert(new UnUsed(name, line, column, isParameter, isRef));
339342
}
340343

341344
void variableUsed(string name)
342345
{
343-
// writeln("Marking ", name, " used");
344346
size_t treeIndex = tree.length - 1;
345347
auto uu = UnUsed(name);
346348
while (true)

0 commit comments

Comments
 (0)