Replace libdparse with DMD in VcallCtorChecker (#145)
* Replace libdparse with DMD in VcallCtorChecker * Reformat the file
This commit is contained in:
parent
cec745a2e1
commit
ea4c90d703
|
|
@ -668,10 +668,6 @@ BaseAnalyzer[] getAnalyzersForModuleAndConfig(string fileName,
|
||||||
checks ~= new UndocumentedDeclarationCheck(args.setSkipTests(
|
checks ~= new UndocumentedDeclarationCheck(args.setSkipTests(
|
||||||
analysisConfig.undocumented_declaration_check == Check.skipTests && !ut));
|
analysisConfig.undocumented_declaration_check == Check.skipTests && !ut));
|
||||||
|
|
||||||
if (moduleName.shouldRun!VcallCtorChecker(analysisConfig))
|
|
||||||
checks ~= new VcallCtorChecker(args.setSkipTests(
|
|
||||||
analysisConfig.vcall_in_ctor == Check.skipTests && !ut));
|
|
||||||
|
|
||||||
if (moduleName.shouldRun!AllManCheck(analysisConfig))
|
if (moduleName.shouldRun!AllManCheck(analysisConfig))
|
||||||
checks ~= new AllManCheck(args.setSkipTests(
|
checks ~= new AllManCheck(args.setSkipTests(
|
||||||
analysisConfig.allman_braces_check == Check.skipTests && !ut));
|
analysisConfig.allman_braces_check == Check.skipTests && !ut));
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import dscanner.analysis.unused_result : UnusedResultChecker;
|
||||||
import dscanner.analysis.unused_variable : UnusedVariableCheck;
|
import dscanner.analysis.unused_variable : UnusedVariableCheck;
|
||||||
import dscanner.analysis.useless_assert : UselessAssertCheck;
|
import dscanner.analysis.useless_assert : UselessAssertCheck;
|
||||||
import dscanner.analysis.useless_initializer : UselessInitializerChecker;
|
import dscanner.analysis.useless_initializer : UselessInitializerChecker;
|
||||||
|
import dscanner.analysis.vcall_in_ctor : VcallCtorChecker;
|
||||||
|
|
||||||
version (unittest)
|
version (unittest)
|
||||||
enum ut = true;
|
enum ut = true;
|
||||||
|
|
@ -310,6 +311,12 @@ MessageSet analyzeDmd(string fileName, ASTCodegen.Module m, const char[] moduleN
|
||||||
config.unused_result == Check.skipTests && !ut
|
config.unused_result == Check.skipTests && !ut
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (moduleName.shouldRunDmd!(VcallCtorChecker!ASTCodegen)(config))
|
||||||
|
visitors ~= new VcallCtorChecker!ASTCodegen(
|
||||||
|
fileName,
|
||||||
|
config.vcall_in_ctor == Check.skipTests && !ut
|
||||||
|
);
|
||||||
|
|
||||||
foreach (visitor; visitors)
|
foreach (visitor; visitors)
|
||||||
{
|
{
|
||||||
m.accept(visitor);
|
m.accept(visitor);
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,7 @@
|
||||||
module dscanner.analysis.vcall_in_ctor;
|
module dscanner.analysis.vcall_in_ctor;
|
||||||
|
|
||||||
import dscanner.analysis.base;
|
import dscanner.analysis.base;
|
||||||
import dscanner.utils;
|
import dmd.astenums : STC;
|
||||||
import dparse.ast, dparse.lexer;
|
|
||||||
import std.algorithm.searching : canFind;
|
|
||||||
import std.range: retro;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks virtual calls from the constructor to methods defined in the same class.
|
* Checks virtual calls from the constructor to methods defined in the same class.
|
||||||
|
|
@ -16,337 +13,225 @@ import std.range: retro;
|
||||||
* When not used carefully, virtual calls from constructors can lead to a call
|
* When not used carefully, virtual calls from constructors can lead to a call
|
||||||
* in a derived instance that's not yet constructed.
|
* in a derived instance that's not yet constructed.
|
||||||
*/
|
*/
|
||||||
final class VcallCtorChecker : BaseAnalyzer
|
extern (C++) class VcallCtorChecker(AST) : BaseAnalyzerDmd
|
||||||
{
|
{
|
||||||
alias visit = BaseAnalyzer.visit;
|
alias visit = BaseAnalyzerDmd.visit;
|
||||||
|
|
||||||
mixin AnalyzerInfo!"vcall_in_ctor";
|
mixin AnalyzerInfo!"vcall_in_ctor";
|
||||||
|
|
||||||
private:
|
private enum string KEY = "dscanner.vcall_ctor";
|
||||||
|
private enum string MSG = "a virtual call inside a constructor may lead to unexpected results in the derived classes";
|
||||||
|
|
||||||
enum string KEY = "dscanner.vcall_ctor";
|
private static struct FuncContext
|
||||||
enum string MSG = "a virtual call inside a constructor may lead to"
|
|
||||||
~ " unexpected results in the derived classes";
|
|
||||||
|
|
||||||
// what's called in the ctor
|
|
||||||
Token[][] _ctorCalls;
|
|
||||||
// the virtual method in the classes
|
|
||||||
Token[][] _virtualMethods;
|
|
||||||
|
|
||||||
|
|
||||||
// The problem only happens in classes
|
|
||||||
bool[] _inClass = [false];
|
|
||||||
// The problem only happens in __ctor
|
|
||||||
bool[] _inCtor = [false];
|
|
||||||
// The problem only happens with call to virtual methods
|
|
||||||
bool[] _isVirtual = [true];
|
|
||||||
// The problem only happens with call to virtual methods
|
|
||||||
bool[] _isNestedFun = [false];
|
|
||||||
// The problem only happens in derived classes that override
|
|
||||||
bool[] _isFinal = [false];
|
|
||||||
|
|
||||||
void pushVirtual(bool value)
|
|
||||||
{
|
{
|
||||||
_isVirtual ~= value;
|
bool canBeVirtual;
|
||||||
|
bool hasNonVirtualVis;
|
||||||
|
bool hasNonVirtualStg;
|
||||||
|
bool inCtor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pushInClass(bool value)
|
private static struct CallContext
|
||||||
{
|
{
|
||||||
_inClass ~= value;
|
string funcName;
|
||||||
_ctorCalls.length += 1;
|
ulong lineNum;
|
||||||
_virtualMethods.length += 1;
|
ulong charNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pushInCtor(bool value)
|
private FuncContext[] contexts;
|
||||||
|
private bool[string] virtualFuncs;
|
||||||
|
private CallContext[] ctorCalls;
|
||||||
|
private bool isFinal;
|
||||||
|
|
||||||
|
extern (D) this(string filename, bool skipTests = false)
|
||||||
{
|
{
|
||||||
_inCtor ~= value;
|
super(filename, skipTests);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pushNestedFunc(bool value)
|
override void visit(AST.ClassDeclaration classDecl)
|
||||||
{
|
{
|
||||||
_isNestedFun ~= value;
|
pushContext((classDecl.storage_class & STC.final_) == 0 && !isFinal);
|
||||||
|
super.visit(classDecl);
|
||||||
|
checkForVirtualCalls();
|
||||||
|
popContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pushIsFinal(bool value)
|
override void visit(AST.StructDeclaration structDecl)
|
||||||
{
|
{
|
||||||
_isFinal ~= value;
|
pushContext(false);
|
||||||
|
super.visit(structDecl);
|
||||||
|
checkForVirtualCalls();
|
||||||
|
popContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
void popVirtual()
|
private void checkForVirtualCalls()
|
||||||
{
|
{
|
||||||
_isVirtual.length -= 1;
|
import std.algorithm : each, filter;
|
||||||
|
|
||||||
|
ctorCalls.filter!(call => call.funcName in virtualFuncs)
|
||||||
|
.each!(call => addErrorMessage(call.lineNum, call.charNum, KEY, MSG));
|
||||||
}
|
}
|
||||||
|
|
||||||
void popInClass()
|
override void visit(AST.VisibilityDeclaration visDecl)
|
||||||
{
|
{
|
||||||
_inClass.length -= 1;
|
import dmd.dsymbol : Visibility;
|
||||||
_ctorCalls.length -= 1;
|
|
||||||
_virtualMethods.length -= 1;
|
if (contexts.length == 0)
|
||||||
|
{
|
||||||
|
super.visit(visDecl);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void popInCtor()
|
bool oldVis = currentContext.hasNonVirtualVis;
|
||||||
{
|
currentContext.hasNonVirtualVis = visDecl.visibility.kind == Visibility.Kind.private_
|
||||||
_inCtor.length -= 1;
|
|| visDecl.visibility.kind == Visibility.Kind.package_;
|
||||||
|
super.visit(visDecl);
|
||||||
|
currentContext.hasNonVirtualVis = oldVis;
|
||||||
}
|
}
|
||||||
|
|
||||||
void popNestedFunc()
|
override void visit(AST.StorageClassDeclaration stgDecl)
|
||||||
{
|
{
|
||||||
_isNestedFun.length -= 1;
|
bool oldFinal = isFinal;
|
||||||
|
isFinal = (stgDecl.stc & STC.final_) != 0;
|
||||||
|
|
||||||
|
bool oldStg;
|
||||||
|
if (contexts.length > 0)
|
||||||
|
{
|
||||||
|
oldStg = currentContext.hasNonVirtualStg;
|
||||||
|
currentContext.hasNonVirtualStg = !(stgDecl.stc & STC.static_ || stgDecl.stc & STC.final_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void popIsFinal()
|
super.visit(stgDecl);
|
||||||
{
|
|
||||||
_isFinal.length -= 1;
|
isFinal = oldFinal;
|
||||||
|
if (contexts.length > 0)
|
||||||
|
currentContext.hasNonVirtualStg = oldStg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void overwriteVirtual(bool value)
|
override void visit(AST.FuncDeclaration funcDecl)
|
||||||
{
|
{
|
||||||
_isVirtual[$-1] = value;
|
if (contexts.length == 0)
|
||||||
|
{
|
||||||
|
super.visit(funcDecl);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isVirtual()
|
bool hasVirtualBody;
|
||||||
|
if (funcDecl.fbody !is null)
|
||||||
{
|
{
|
||||||
return _isVirtual[$-1];
|
auto funcBody = funcDecl.fbody.isCompoundStatement();
|
||||||
|
hasVirtualBody = funcBody !is null && funcBody.statements !is null && (*funcBody.statements).length == 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hasVirtualBody = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInClass()
|
bool hasNonVirtualStg = currentContext.hasNonVirtualStg
|
||||||
|
|| funcDecl.storage_class & STC.static_ || funcDecl.storage_class & STC.final_;
|
||||||
|
|
||||||
|
if (!currentContext.canBeVirtual || currentContext.hasNonVirtualVis || hasNonVirtualStg || !hasVirtualBody)
|
||||||
{
|
{
|
||||||
return _inClass[$-1];
|
super.visit(funcDecl);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInCtor()
|
string funcName = cast(string) funcDecl.ident.toString();
|
||||||
{
|
virtualFuncs[funcName] = true;
|
||||||
return _inCtor[$-1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFinal()
|
override void visit(AST.CtorDeclaration ctorDecl)
|
||||||
{
|
{
|
||||||
return _isFinal[$-1];
|
if (contexts.length == 0)
|
||||||
|
{
|
||||||
|
super.visit(ctorDecl);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isInNestedFunc()
|
currentContext.inCtor = true;
|
||||||
{
|
super.visit(ctorDecl);
|
||||||
return _isNestedFun[$-1];
|
currentContext.inCtor = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check()
|
override void visit(AST.CallExp callExp)
|
||||||
{
|
{
|
||||||
foreach (call; _ctorCalls[$-1])
|
super.visit(callExp);
|
||||||
foreach (vm; _virtualMethods[$-1])
|
|
||||||
{
|
if (contexts.length == 0)
|
||||||
if (call == vm)
|
return;
|
||||||
{
|
|
||||||
addErrorMessage(call, KEY, MSG);
|
auto identExp = callExp.e1.isIdentifierExp();
|
||||||
break;
|
if (!currentContext.inCtor || identExp is null)
|
||||||
}
|
return;
|
||||||
}
|
|
||||||
|
string funcCall = cast(string) identExp.ident.toString();
|
||||||
|
ctorCalls ~= CallContext(funcCall, callExp.loc.linnum, callExp.loc.charnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
private ref currentContext() @property
|
||||||
|
|
||||||
///
|
|
||||||
this(BaseAnalyzerArguments args)
|
|
||||||
{
|
{
|
||||||
super(args);
|
return contexts[$ - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(const(ClassDeclaration) decl)
|
private void pushContext(bool inClass)
|
||||||
{
|
{
|
||||||
pushVirtual(true);
|
contexts ~= FuncContext(inClass);
|
||||||
pushInClass(true);
|
|
||||||
pushNestedFunc(false);
|
|
||||||
decl.accept(this);
|
|
||||||
check();
|
|
||||||
popVirtual();
|
|
||||||
popInClass();
|
|
||||||
popNestedFunc();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override void visit(const(StructDeclaration) decl)
|
private void popContext()
|
||||||
{
|
{
|
||||||
pushVirtual(false);
|
contexts.length--;
|
||||||
pushInClass(false);
|
|
||||||
pushNestedFunc(false);
|
|
||||||
decl.accept(this);
|
|
||||||
check();
|
|
||||||
popVirtual();
|
|
||||||
popInClass();
|
|
||||||
popNestedFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(const(Constructor) ctor)
|
|
||||||
{
|
|
||||||
pushInCtor(isInClass);
|
|
||||||
ctor.accept(this);
|
|
||||||
popInCtor();
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(const(Declaration) d)
|
|
||||||
{
|
|
||||||
// "<protection>:"
|
|
||||||
if (d.attributeDeclaration && d.attributeDeclaration.attribute)
|
|
||||||
{
|
|
||||||
const tp = d.attributeDeclaration.attribute.attribute.type;
|
|
||||||
overwriteVirtual(isProtection(tp) & (tp != tok!"private"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// "protection {}"
|
|
||||||
bool pop;
|
|
||||||
scope(exit) if (pop)
|
|
||||||
popVirtual;
|
|
||||||
|
|
||||||
const bool hasAttribs = d.attributes !is null;
|
|
||||||
const bool hasStatic = hasAttribs ? d.attributes.canFind!(a => a.attribute.type == tok!"static") : false;
|
|
||||||
const bool hasFinal = hasAttribs ? d.attributes.canFind!(a => a.attribute.type == tok!"final") : false;
|
|
||||||
|
|
||||||
if (d.attributes) foreach (attr; d.attributes.retro)
|
|
||||||
{
|
|
||||||
if (!hasStatic &&
|
|
||||||
(attr.attribute == tok!"public" || attr.attribute == tok!"protected"))
|
|
||||||
{
|
|
||||||
pushVirtual(true);
|
|
||||||
pop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (hasStatic || attr.attribute == tok!"private" || attr.attribute == tok!"package")
|
|
||||||
{
|
|
||||||
pushVirtual(false);
|
|
||||||
pop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// final class... final function
|
|
||||||
if ((d.classDeclaration || d.functionDeclaration) && hasFinal)
|
|
||||||
pushIsFinal(true);
|
|
||||||
|
|
||||||
d.accept(this);
|
|
||||||
|
|
||||||
if ((d.classDeclaration || d.functionDeclaration) && hasFinal)
|
|
||||||
popIsFinal;
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(const(FunctionCallExpression) exp)
|
|
||||||
{
|
|
||||||
// nested function are not virtual
|
|
||||||
pushNestedFunc(true);
|
|
||||||
exp.accept(this);
|
|
||||||
popNestedFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(const(UnaryExpression) exp)
|
|
||||||
{
|
|
||||||
if (isInCtor)
|
|
||||||
// get function identifier for a call, only for this member (so no ident chain)
|
|
||||||
if (const IdentifierOrTemplateInstance iot = safeAccess(exp)
|
|
||||||
.functionCallExpression.unaryExpression.primaryExpression.identifierOrTemplateInstance)
|
|
||||||
{
|
|
||||||
const Token t = iot.identifier;
|
|
||||||
if (t != tok!"")
|
|
||||||
{
|
|
||||||
_ctorCalls[$-1] ~= t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exp.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
override void visit(const(FunctionDeclaration) d)
|
|
||||||
{
|
|
||||||
if (isInClass() && !isInNestedFunc() && !isFinal() && !d.templateParameters)
|
|
||||||
{
|
|
||||||
bool virtualOnce;
|
|
||||||
bool notVirtualOnce;
|
|
||||||
|
|
||||||
const bool hasAttribs = d.attributes !is null;
|
|
||||||
const bool hasStatic = hasAttribs ? d.attributes.canFind!(a => a.attribute.type == tok!"static") : false;
|
|
||||||
|
|
||||||
// handle "private", "public"... for this declaration
|
|
||||||
if (d.attributes) foreach (attr; d.attributes.retro)
|
|
||||||
{
|
|
||||||
if (!hasStatic &&
|
|
||||||
(attr.attribute == tok!"public" || attr.attribute == tok!"protected"))
|
|
||||||
{
|
|
||||||
if (!isVirtual)
|
|
||||||
{
|
|
||||||
virtualOnce = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (hasStatic || attr.attribute == tok!"private" || attr.attribute == tok!"package")
|
|
||||||
{
|
|
||||||
if (isVirtual)
|
|
||||||
{
|
|
||||||
notVirtualOnce = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isVirtual && virtualOnce)
|
|
||||||
_virtualMethods[$-1] ~= d.name;
|
|
||||||
else if (isVirtual && !virtualOnce)
|
|
||||||
_virtualMethods[$-1] ~= d.name;
|
|
||||||
|
|
||||||
}
|
|
||||||
d.accept(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
|
import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig;
|
||||||
import dscanner.analysis.helpers : assertAnalyzerWarnings;
|
import dscanner.analysis.helpers : assertAnalyzerWarningsDMD;
|
||||||
import std.stdio : stderr;
|
|
||||||
import std.format : format;
|
import std.format : format;
|
||||||
|
import std.stdio : stderr;
|
||||||
|
|
||||||
StaticAnalysisConfig sac = disabledConfig();
|
StaticAnalysisConfig sac = disabledConfig();
|
||||||
sac.vcall_in_ctor = Check.enabled;
|
sac.vcall_in_ctor = Check.enabled;
|
||||||
|
string MSG = "a virtual call inside a constructor may lead to unexpected results in the derived classes";
|
||||||
|
|
||||||
// fails
|
// fails
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Bar
|
class Bar
|
||||||
{
|
{
|
||||||
this(){foo();} /+
|
this(){foo();} // [warn]: %s
|
||||||
^^^ [warn]: %s +/
|
|
||||||
private:
|
private:
|
||||||
public
|
public void foo(){}
|
||||||
void foo(){}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}c.format(VcallCtorChecker.MSG), sac);
|
}c.format(MSG), sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Bar
|
class Bar
|
||||||
{
|
{
|
||||||
this()
|
this()
|
||||||
{
|
{
|
||||||
foo(); /+
|
foo(); // [warn]: %s
|
||||||
^^^ [warn]: %s +/
|
foo(); // [warn]: %s
|
||||||
foo(); /+
|
|
||||||
^^^ [warn]: %s +/
|
|
||||||
bar();
|
bar();
|
||||||
}
|
}
|
||||||
private: void bar();
|
private: void bar();
|
||||||
public{void foo(){}}
|
public{void foo(){}}
|
||||||
}
|
}
|
||||||
}c.format(VcallCtorChecker.MSG, VcallCtorChecker.MSG), sac);
|
}c.format(MSG, MSG), sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Bar
|
class Bar
|
||||||
{
|
{
|
||||||
this()
|
this()
|
||||||
{
|
{
|
||||||
foo();
|
foo();
|
||||||
bar(); /+
|
bar(); // [warn]: %s
|
||||||
^^^ [warn]: %s +/
|
|
||||||
}
|
}
|
||||||
private: public void bar();
|
private: public void bar();
|
||||||
public private {void foo(){}}
|
public private {void foo(){}}
|
||||||
}
|
}
|
||||||
}c.format(VcallCtorChecker.MSG), sac);
|
}c.format(MSG), sac);
|
||||||
|
|
||||||
// passes
|
// passes
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Bar
|
class Bar
|
||||||
{
|
{
|
||||||
this(){foo();}
|
this(){foo();}
|
||||||
|
|
@ -354,7 +239,7 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Bar
|
class Bar
|
||||||
{
|
{
|
||||||
this(){foo();}
|
this(){foo();}
|
||||||
|
|
@ -362,7 +247,7 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Bar
|
class Bar
|
||||||
{
|
{
|
||||||
this(){foo();}
|
this(){foo();}
|
||||||
|
|
@ -370,7 +255,7 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Bar
|
class Bar
|
||||||
{
|
{
|
||||||
this(){foo();}
|
this(){foo();}
|
||||||
|
|
@ -378,7 +263,7 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Bar
|
class Bar
|
||||||
{
|
{
|
||||||
this(){foo();}
|
this(){foo();}
|
||||||
|
|
@ -386,7 +271,7 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
final class Bar
|
final class Bar
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -395,7 +280,7 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Bar
|
class Bar
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -404,7 +289,7 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Foo
|
class Foo
|
||||||
{
|
{
|
||||||
static void nonVirtual();
|
static void nonVirtual();
|
||||||
|
|
@ -412,7 +297,7 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class Foo
|
class Foo
|
||||||
{
|
{
|
||||||
package void nonVirtual();
|
package void nonVirtual();
|
||||||
|
|
@ -420,7 +305,7 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
assertAnalyzerWarnings(q{
|
assertAnalyzerWarningsDMD(q{
|
||||||
class C {
|
class C {
|
||||||
static struct S {
|
static struct S {
|
||||||
public:
|
public:
|
||||||
|
|
@ -432,7 +317,5 @@ unittest
|
||||||
}
|
}
|
||||||
}, sac);
|
}, sac);
|
||||||
|
|
||||||
import std.stdio: writeln;
|
stderr.writeln("Unittest for VcallCtorChecker passed");
|
||||||
writeln("Unittest for VcallCtorChecker passed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue