132 lines
2.9 KiB
D
132 lines
2.9 KiB
D
// Copyright Brian Schott (Hackerpilot) 2015.
|
|
// 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 dscanner.analysis.auto_ref_assignment;
|
|
|
|
import dparse.lexer;
|
|
import dparse.ast;
|
|
import dscanner.analysis.base;
|
|
|
|
/**
|
|
* Checks for assignment to auto-ref function parameters.
|
|
*/
|
|
final class AutoRefAssignmentCheck : BaseAnalyzer
|
|
{
|
|
mixin AnalyzerInfo!"auto_ref_assignment_check";
|
|
|
|
///
|
|
this(BaseAnalyzerArguments args)
|
|
{
|
|
super(args);
|
|
}
|
|
|
|
override void visit(const Module m)
|
|
{
|
|
pushScope();
|
|
m.accept(this);
|
|
popScope();
|
|
}
|
|
|
|
override void visit(const FunctionDeclaration func)
|
|
{
|
|
if (func.parameters is null || func.parameters.parameters.length == 0)
|
|
return;
|
|
pushScope();
|
|
scope (exit)
|
|
popScope();
|
|
func.accept(this);
|
|
}
|
|
|
|
override void visit(const Parameter param)
|
|
{
|
|
import std.algorithm.searching : canFind;
|
|
|
|
immutable bool isAuto = param.parameterAttributes.canFind!(a => a.idType == cast(ubyte) tok!"auto");
|
|
immutable bool isRef = param.parameterAttributes.canFind!(a => a.idType == cast(ubyte) tok!"ref");
|
|
if (!isAuto || !isRef)
|
|
return;
|
|
addSymbol(param.name.text);
|
|
}
|
|
|
|
override void visit(const AssignExpression assign)
|
|
{
|
|
if (assign.operator == tok!"" || scopes.length == 0)
|
|
return;
|
|
interest ~= assign;
|
|
assign.ternaryExpression.accept(this);
|
|
interest.length--;
|
|
}
|
|
|
|
override void visit(const IdentifierOrTemplateInstance ioti)
|
|
{
|
|
import std.algorithm.searching : canFind;
|
|
|
|
if (ioti.identifier == tok!"" || !interest.length)
|
|
return;
|
|
if (scopes[$ - 1].canFind(ioti.identifier.text))
|
|
addErrorMessage(interest[$ - 1], KEY, MESSAGE);
|
|
}
|
|
|
|
override void visit(const IdentifierChain ic)
|
|
{
|
|
import std.algorithm.searching : canFind;
|
|
|
|
if (ic.identifiers.length == 0 || !interest.length)
|
|
return;
|
|
if (scopes[$ - 1].canFind(ic.identifiers[0].text))
|
|
addErrorMessage(interest[$ - 1], KEY, MESSAGE);
|
|
}
|
|
|
|
alias visit = BaseAnalyzer.visit;
|
|
|
|
private:
|
|
|
|
enum string MESSAGE = "Assignment to auto-ref function parameter.";
|
|
enum string KEY = "dscanner.suspicious.auto_ref_assignment";
|
|
|
|
const(AssignExpression)[] interest;
|
|
|
|
void addSymbol(string symbolName)
|
|
{
|
|
scopes[$ - 1] ~= symbolName;
|
|
}
|
|
|
|
void pushScope()
|
|
{
|
|
scopes.length++;
|
|
}
|
|
|
|
void popScope()
|
|
{
|
|
scopes = scopes[0 .. $ - 1];
|
|
}
|
|
|
|
string[][] scopes;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
import std.stdio : stderr;
|
|
import std.format : format;
|
|
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
|
|
import dscanner.analysis.helpers : assertAnalyzerWarnings;
|
|
|
|
StaticAnalysisConfig sac = disabledConfig();
|
|
sac.auto_ref_assignment_check = Check.enabled;
|
|
assertAnalyzerWarnings(q{
|
|
int doStuff(T)(auto ref int a)
|
|
{
|
|
a = 10; /+
|
|
^^^^^^ [warn]: %s +/
|
|
}
|
|
|
|
int doStuff(T)(ref int a)
|
|
{
|
|
a = 10;
|
|
}
|
|
}c.format(AutoRefAssignmentCheck.MESSAGE), sac);
|
|
stderr.writeln("Unittest for AutoRefAssignmentCheck passed.");
|
|
}
|