1 // Copyright Brian Schott (Hackerpilot) 2015. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt or copy at 4 // http://www.boost.org/LICENSE_1_0.txt) 5 6 module dscanner.analysis.auto_ref_assignment; 7 8 import dparse.lexer; 9 import dparse.ast; 10 import dscanner.analysis.base; 11 12 /** 13 * Checks for assignment to auto-ref function parameters. 14 */ 15 class AutoRefAssignmentCheck : BaseAnalyzer 16 { 17 /// 18 this(string fileName, bool skipTests = false) 19 { 20 super(fileName, null, skipTests); 21 } 22 23 override void visit(const Module m) 24 { 25 pushScope(); 26 m.accept(this); 27 popScope(); 28 } 29 30 override void visit(const FunctionDeclaration func) 31 { 32 if (func.parameters is null || func.parameters.parameters.length == 0) 33 return; 34 pushScope(); 35 scope (exit) 36 popScope(); 37 func.accept(this); 38 } 39 40 override void visit(const Parameter param) 41 { 42 import std.algorithm.searching : canFind; 43 44 immutable bool isAuto = param.parameterAttributes.canFind(cast(ubyte) tok!"auto"); 45 immutable bool isRef = param.parameterAttributes.canFind(cast(ubyte) tok!"ref"); 46 if (!isAuto || !isRef) 47 return; 48 addSymbol(param.name.text); 49 } 50 51 override void visit(const AssignExpression assign) 52 { 53 if (assign.operator == tok!"" || scopes.length == 0) 54 return; 55 interest++; 56 assign.ternaryExpression.accept(this); 57 interest--; 58 } 59 60 override void visit(const IdentifierOrTemplateInstance ioti) 61 { 62 import std.algorithm.searching : canFind; 63 64 if (ioti.identifier == tok!"" || interest <= 0) 65 return; 66 if (scopes[$ - 1].canFind(ioti.identifier.text)) 67 addErrorMessage(ioti.identifier.line, ioti.identifier.column, KEY, MESSAGE); 68 } 69 70 override void visit(const IdentifierChain ic) 71 { 72 import std.algorithm.searching : canFind; 73 74 if (ic.identifiers.length == 0 || interest <= 0) 75 return; 76 if (scopes[$ - 1].canFind(ic.identifiers[0].text)) 77 addErrorMessage(ic.identifiers[0].line, ic.identifiers[0].column, KEY, MESSAGE); 78 } 79 80 alias visit = BaseAnalyzer.visit; 81 82 private: 83 84 enum string MESSAGE = "Assignment to auto-ref function parameter."; 85 enum string KEY = "dscanner.suspicious.auto_ref_assignment"; 86 87 invariant 88 { 89 assert(interest >= 0); 90 } 91 92 int interest; 93 94 void addSymbol(string symbolName) 95 { 96 scopes[$ - 1] ~= symbolName; 97 } 98 99 void pushScope() 100 { 101 scopes.length++; 102 } 103 104 void popScope() 105 { 106 scopes = scopes[0 .. $ - 1]; 107 } 108 109 string[][] scopes; 110 } 111 112 unittest 113 { 114 import std.stdio : stderr; 115 import std.format : format; 116 import dscanner.analysis.config : StaticAnalysisConfig, Check, disabledConfig; 117 import dscanner.analysis.helpers : assertAnalyzerWarnings; 118 119 StaticAnalysisConfig sac = disabledConfig(); 120 sac.auto_ref_assignment_check = Check.enabled; 121 assertAnalyzerWarnings(q{ 122 int doStuff(T)(auto ref int a) 123 { 124 a = 10; // [warn]: %s 125 } 126 127 int doStuff(T)(ref int a) 128 { 129 a = 10; 130 } 131 }}.format(AutoRefAssignmentCheck.MESSAGE), sac); 132 stderr.writeln("Unittest for AutoRefAssignmentCheck passed."); 133 }