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