Improvements to the onmodified variable checker
This commit is contained in:
parent
f7ffced8ee
commit
8dc46e3992
|
@ -29,8 +29,6 @@ class BuiltinPropertyNameCheck : BaseAnalyzer
|
||||||
{
|
{
|
||||||
alias visit = BaseAnalyzer.visit;
|
alias visit = BaseAnalyzer.visit;
|
||||||
|
|
||||||
enum string KEY = "dscanner.confusing.builtin_property_names";
|
|
||||||
|
|
||||||
this(string fileName)
|
this(string fileName)
|
||||||
{
|
{
|
||||||
super(fileName);
|
super(fileName);
|
||||||
|
@ -47,7 +45,7 @@ class BuiltinPropertyNameCheck : BaseAnalyzer
|
||||||
|
|
||||||
override void visit(const FunctionBody functionBody)
|
override void visit(const FunctionBody functionBody)
|
||||||
{
|
{
|
||||||
int d = depth;
|
immutable int d = depth;
|
||||||
scope(exit) depth = d;
|
scope(exit) depth = d;
|
||||||
depth = 0;
|
depth = 0;
|
||||||
functionBody.accept(this);
|
functionBody.accept(this);
|
||||||
|
@ -77,16 +75,18 @@ class BuiltinPropertyNameCheck : BaseAnalyzer
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
enum string KEY = "dscanner.confusing.builtin_property_names";
|
||||||
|
|
||||||
string generateErrorMessage(string name)
|
string generateErrorMessage(string name)
|
||||||
{
|
{
|
||||||
import std.string;
|
import std.string : format;
|
||||||
return format("Avoid naming members '%s'. This can"
|
return format("Avoid naming members '%s'. This can"
|
||||||
~ " confuse code that depends on the '.%s' property of a type.", name, name);
|
~ " confuse code that depends on the '.%s' property of a type.", name, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isBuiltinProperty(string name)
|
bool isBuiltinProperty(string name)
|
||||||
{
|
{
|
||||||
import std.algorithm;
|
import std.algorithm : canFind;
|
||||||
return builtinProperties.canFind(name);
|
return builtinProperties.canFind(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ private:
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
import analysis.config;
|
import analysis.config : StaticAnalysisConfig;
|
||||||
StaticAnalysisConfig sac;
|
StaticAnalysisConfig sac;
|
||||||
sac.builtin_property_names_check = true;
|
sac.builtin_property_names_check = true;
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarnings(q{
|
||||||
|
|
|
@ -18,11 +18,11 @@ class ConstructorCheck : BaseAnalyzer
|
||||||
|
|
||||||
override void visit(const ClassDeclaration classDeclaration)
|
override void visit(const ClassDeclaration classDeclaration)
|
||||||
{
|
{
|
||||||
bool oldHasDefault = hasDefaultArgConstructor;
|
immutable bool oldHasDefault = hasDefaultArgConstructor;
|
||||||
bool oldHasNoArg = hasNoArgConstructor;
|
immutable bool oldHasNoArg = hasNoArgConstructor;
|
||||||
hasNoArgConstructor = false;
|
hasNoArgConstructor = false;
|
||||||
hasDefaultArgConstructor = false;
|
hasDefaultArgConstructor = false;
|
||||||
State prev = state;
|
immutable State prev = state;
|
||||||
state = State.inClass;
|
state = State.inClass;
|
||||||
classDeclaration.accept(this);
|
classDeclaration.accept(this);
|
||||||
if (hasNoArgConstructor && hasDefaultArgConstructor)
|
if (hasNoArgConstructor && hasDefaultArgConstructor)
|
||||||
|
@ -39,7 +39,7 @@ class ConstructorCheck : BaseAnalyzer
|
||||||
|
|
||||||
override void visit(const StructDeclaration structDeclaration)
|
override void visit(const StructDeclaration structDeclaration)
|
||||||
{
|
{
|
||||||
State prev = state;
|
immutable State prev = state;
|
||||||
state = State.inStruct;
|
state = State.inStruct;
|
||||||
structDeclaration.accept(this);
|
structDeclaration.accept(this);
|
||||||
state = prev;
|
state = prev;
|
||||||
|
|
|
@ -69,7 +69,7 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, stri
|
||||||
foreach (rawWarning; rawWarnings[])
|
foreach (rawWarning; rawWarnings[])
|
||||||
{
|
{
|
||||||
// Skip the warning if it is on line zero
|
// Skip the warning if it is on line zero
|
||||||
size_t rawLine = rawWarning.line;
|
immutable size_t rawLine = rawWarning.line;
|
||||||
if (rawLine == 0)
|
if (rawLine == 0)
|
||||||
{
|
{
|
||||||
stderr.writefln("!!! Skipping warning because it is on line zero:\n%s", rawWarning.message);
|
stderr.writefln("!!! Skipping warning because it is on line zero:\n%s", rawWarning.message);
|
||||||
|
@ -89,8 +89,8 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, stri
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Skip if there is no comment or code
|
// Skip if there is no comment or code
|
||||||
string codePart = codeLine.before("// ");
|
immutable string codePart = codeLine.before("// ");
|
||||||
string commentPart = codeLine.after("// ");
|
immutable string commentPart = codeLine.after("// ");
|
||||||
if (!codePart.length || !commentPart.length)
|
if (!codePart.length || !commentPart.length)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, stri
|
||||||
// No warning
|
// No warning
|
||||||
if (lineNo !in warnings)
|
if (lineNo !in warnings)
|
||||||
{
|
{
|
||||||
string errors = "Expected warning:\n%s\nFrom source code at (%s:?):\n%s".format(
|
immutable string errors = "Expected warning:\n%s\nFrom source code at (%s:?):\n%s".format(
|
||||||
messages[lineNo],
|
messages[lineNo],
|
||||||
lineNo,
|
lineNo,
|
||||||
codeLines[lineNo - line]
|
codeLines[lineNo - line]
|
||||||
|
@ -117,7 +117,7 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, stri
|
||||||
// Different warning
|
// Different warning
|
||||||
else if (warnings[lineNo] != messages[lineNo])
|
else if (warnings[lineNo] != messages[lineNo])
|
||||||
{
|
{
|
||||||
string errors = "Expected warning:\n%s\nBut was:\n%s\nFrom source code at (%s:?):\n%s".format(
|
immutable string errors = "Expected warning:\n%s\nBut was:\n%s\nFrom source code at (%s:?):\n%s".format(
|
||||||
messages[lineNo],
|
messages[lineNo],
|
||||||
warnings[lineNo],
|
warnings[lineNo],
|
||||||
lineNo,
|
lineNo,
|
||||||
|
@ -143,7 +143,7 @@ void assertAnalyzerWarnings(string code, const StaticAnalysisConfig config, stri
|
||||||
}
|
}
|
||||||
if (unexpectedWarnings.length)
|
if (unexpectedWarnings.length)
|
||||||
{
|
{
|
||||||
string message = "Unexpected warnings:\n" ~ unexpectedWarnings.join("\n");
|
immutable string message = "Unexpected warnings:\n" ~ unexpectedWarnings.join("\n");
|
||||||
throw new core.exception.AssertError(message, file, line);
|
throw new core.exception.AssertError(message, file, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,8 @@ class ImmutableFinder:BaseAnalyzer
|
||||||
{
|
{
|
||||||
foreach (d; dec.declarators)
|
foreach (d; dec.declarators)
|
||||||
{
|
{
|
||||||
|
if (initializedFromCast(d.initializer))
|
||||||
|
continue;
|
||||||
tree[$ - 1].insert(new VariableInfo(d.name.text, d.name.line,
|
tree[$ - 1].insert(new VariableInfo(d.name.text, d.name.line,
|
||||||
d.name.column));
|
d.name.column));
|
||||||
}
|
}
|
||||||
|
@ -68,9 +70,13 @@ class ImmutableFinder:BaseAnalyzer
|
||||||
&& autoDeclaration.storageClass.token != tok!"enum"
|
&& autoDeclaration.storageClass.token != tok!"enum"
|
||||||
&& autoDeclaration.storageClass.token != tok!"immutable"))
|
&& autoDeclaration.storageClass.token != tok!"immutable"))
|
||||||
{
|
{
|
||||||
foreach (id; autoDeclaration.identifiers)
|
foreach (size_t i, id; autoDeclaration.identifiers)
|
||||||
|
{
|
||||||
|
if (initializedFromCast(autoDeclaration.initializers[i]))
|
||||||
|
continue;
|
||||||
tree[$ - 1].insert(new VariableInfo(id.text, id.line,
|
tree[$ - 1].insert(new VariableInfo(id.text, id.line,
|
||||||
id.column));
|
id.column));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
autoDeclaration.accept(this);
|
autoDeclaration.accept(this);
|
||||||
}
|
}
|
||||||
|
@ -130,6 +136,17 @@ class ImmutableFinder:BaseAnalyzer
|
||||||
unary.accept(this);
|
unary.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override void visit(const ForeachStatement foreachStatement)
|
||||||
|
{
|
||||||
|
if (foreachStatement.low !is null)
|
||||||
|
{
|
||||||
|
interest++;
|
||||||
|
foreachStatement.low.accept(this);
|
||||||
|
interest--;
|
||||||
|
}
|
||||||
|
foreachStatement.declarationOrStatement.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
template PartsMightModify(T)
|
template PartsMightModify(T)
|
||||||
|
@ -156,6 +173,28 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool initializedFromCast(const Initializer initializer)
|
||||||
|
{
|
||||||
|
import std.typecons : scoped;
|
||||||
|
|
||||||
|
static class CastFinder : ASTVisitor
|
||||||
|
{
|
||||||
|
alias visit = ASTVisitor.visit;
|
||||||
|
override void visit(const CastExpression castExpression)
|
||||||
|
{
|
||||||
|
foundCast = true;
|
||||||
|
castExpression.accept(this);
|
||||||
|
}
|
||||||
|
bool foundCast = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initializer is null)
|
||||||
|
return false;
|
||||||
|
auto finder = scoped!CastFinder();
|
||||||
|
finder.visit(initializer);
|
||||||
|
return finder.foundCast;
|
||||||
|
}
|
||||||
|
|
||||||
bool canFindImmutable(const Declaration dec)
|
bool canFindImmutable(const Declaration dec)
|
||||||
{
|
{
|
||||||
import std.algorithm : canFind, map;
|
import std.algorithm : canFind, map;
|
||||||
|
@ -195,7 +234,7 @@ private:
|
||||||
foreach (vi; tree[$ - 1])
|
foreach (vi; tree[$ - 1])
|
||||||
{
|
{
|
||||||
immutable string errorMessage = "Variable " ~ vi.name
|
immutable string errorMessage = "Variable " ~ vi.name
|
||||||
~ " could have been declared immutable";
|
~ " is never modified and could have been declared immutable.";
|
||||||
addErrorMessage(vi.line, vi.column, "dscanner.suspicious.could_be_immutable",
|
addErrorMessage(vi.line, vi.column, "dscanner.suspicious.could_be_immutable",
|
||||||
errorMessage);
|
errorMessage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,8 +29,8 @@ class LengthSubtractionCheck : BaseAnalyzer
|
||||||
{
|
{
|
||||||
if (addExpression.operator == tok!"-")
|
if (addExpression.operator == tok!"-")
|
||||||
{
|
{
|
||||||
UnaryExpression l = cast(UnaryExpression) addExpression.left;
|
const UnaryExpression l = cast(const UnaryExpression) addExpression.left;
|
||||||
UnaryExpression r = cast(UnaryExpression) addExpression.right;
|
const UnaryExpression r = cast(const UnaryExpression) addExpression.right;
|
||||||
if (l is null || r is null)
|
if (l is null || r is null)
|
||||||
{
|
{
|
||||||
// stderr.writeln(__FILE__, " ", __LINE__);
|
// stderr.writeln(__FILE__, " ", __LINE__);
|
||||||
|
|
|
@ -57,7 +57,7 @@ class OpEqualsWithoutToHashCheck : BaseAnalyzer
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Check if opEquals or toHash
|
// Check if opEquals or toHash
|
||||||
string methodName = declaration.functionDeclaration.name.text;
|
immutable string methodName = declaration.functionDeclaration.name.text;
|
||||||
if (methodName == "opEquals")
|
if (methodName == "opEquals")
|
||||||
hasOpEquals = true;
|
hasOpEquals = true;
|
||||||
else if (methodName == "toHash")
|
else if (methodName == "toHash")
|
||||||
|
|
|
@ -44,7 +44,7 @@ class UnusedVariableCheck : BaseAnalyzer
|
||||||
pushScope();
|
pushScope();
|
||||||
if (functionDec.functionBody !is null)
|
if (functionDec.functionBody !is null)
|
||||||
{
|
{
|
||||||
bool ias = inAggregateScope;
|
immutable bool ias = inAggregateScope;
|
||||||
inAggregateScope = false;
|
inAggregateScope = false;
|
||||||
if (!isOverride)
|
if (!isOverride)
|
||||||
functionDec.parameters.accept(this);
|
functionDec.parameters.accept(this);
|
||||||
|
|
11
src/main.d
11
src/main.d
|
@ -133,7 +133,7 @@ int run(string[] args)
|
||||||
}
|
}
|
||||||
else if (tokenDump || highlight)
|
else if (tokenDump || highlight)
|
||||||
{
|
{
|
||||||
bool usingStdin = args.length == 1;
|
immutable bool usingStdin = args.length == 1;
|
||||||
ubyte[] bytes = usingStdin ? readStdin() : readFile(args[1]);
|
ubyte[] bytes = usingStdin ? readStdin() : readFile(args[1]);
|
||||||
LexerConfig config;
|
LexerConfig config;
|
||||||
config.stringBehavior = StringBehavior.source;
|
config.stringBehavior = StringBehavior.source;
|
||||||
|
@ -211,7 +211,7 @@ int run(string[] args)
|
||||||
}
|
}
|
||||||
else if (imports)
|
else if (imports)
|
||||||
{
|
{
|
||||||
string[] fileNames = usingStdin ? ["stdin"] : args[1 .. $];
|
const string[] fileNames = usingStdin ? ["stdin"] : args[1 .. $];
|
||||||
LexerConfig config;
|
LexerConfig config;
|
||||||
config.stringBehavior = StringBehavior.source;
|
config.stringBehavior = StringBehavior.source;
|
||||||
auto visitor = new ImportPrinter;
|
auto visitor = new ImportPrinter;
|
||||||
|
@ -236,12 +236,6 @@ int run(string[] args)
|
||||||
auto tokens = getTokensForParser(
|
auto tokens = getTokensForParser(
|
||||||
usingStdin ? readStdin() : readFile(args[1]),
|
usingStdin ? readStdin() : readFile(args[1]),
|
||||||
config, &cache);
|
config, &cache);
|
||||||
// writeln("text blank\tindex\tline\tcolumn\ttype\tcomment");
|
|
||||||
// foreach (token; tokens)
|
|
||||||
// {
|
|
||||||
// writefln("<<%20s>>%b\t%d\t%d\t%d\t%d\t%s", token.text is null ? str(token.type) : token.text,
|
|
||||||
// token.text !is null, token.index, token.line, token.column, token.type, token.comment);
|
|
||||||
// }
|
|
||||||
auto mod = parseModule(tokens, fileName, null, &doNothing);
|
auto mod = parseModule(tokens, fileName, null, &doNothing);
|
||||||
|
|
||||||
if (ast)
|
if (ast)
|
||||||
|
@ -387,6 +381,7 @@ string getConfigurationLocation()
|
||||||
{
|
{
|
||||||
version (useXDG)
|
version (useXDG)
|
||||||
{
|
{
|
||||||
|
int x;
|
||||||
import std.process;
|
import std.process;
|
||||||
string configDir = environment.get("XDG_CONFIG_HOME", null);
|
string configDir = environment.get("XDG_CONFIG_HOME", null);
|
||||||
if (configDir is null)
|
if (configDir is null)
|
||||||
|
|
Loading…
Reference in New Issue