VariableUsageHelpers.cpp (9808B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "VariableUsageHelpers.h" 6 #include "Utils.h" 7 8 std::vector<const Stmt *> getUsageAsRvalue(const ValueDecl *ValueDeclaration, 9 const FunctionDecl *FuncDecl) { 10 std::vector<const Stmt *> UsageStatements; 11 12 // We check the function declaration has a body. 13 auto Body = FuncDecl->getBody(); 14 if (!Body) { 15 return std::vector<const Stmt *>(); 16 } 17 18 // We build a Control Flow Graph (CFG) fron the body of the function 19 // declaration. 20 std::unique_ptr<CFG> StatementCFG = CFG::buildCFG( 21 FuncDecl, Body, &FuncDecl->getASTContext(), CFG::BuildOptions()); 22 23 // We iterate through all the CFGBlocks, which basically means that we go over 24 // all the possible branches of the code and therefore cover all statements. 25 for (auto &Block : *StatementCFG) { 26 // We iterate through all the statements of the block. 27 for (auto &BlockItem : *Block) { 28 auto CFGStatement = BlockItem.getAs<CFGStmt>(); 29 if (!CFGStatement) { 30 continue; 31 } 32 33 // FIXME: Right now this function/if chain is very basic and only covers 34 // the cases we need for escapesFunction() 35 if (auto BinOp = dyn_cast<BinaryOperator>(CFGStatement->getStmt())) { 36 // We only care about assignments. 37 if (BinOp->getOpcode() != BO_Assign) { 38 continue; 39 } 40 41 // We want our declaration to be used on the right hand side of the 42 // assignment. 43 auto DeclRef = dyn_cast<DeclRefExpr>(IgnoreTrivials(BinOp->getRHS())); 44 if (!DeclRef) { 45 continue; 46 } 47 48 if (DeclRef->getDecl() != ValueDeclaration) { 49 continue; 50 } 51 } else if (auto Return = dyn_cast<ReturnStmt>(CFGStatement->getStmt())) { 52 // We want our declaration to be used as the expression of the return 53 // statement. 54 auto DeclRef = dyn_cast_or_null<DeclRefExpr>( 55 IgnoreTrivials(Return->getRetValue())); 56 if (!DeclRef) { 57 continue; 58 } 59 60 if (DeclRef->getDecl() != ValueDeclaration) { 61 continue; 62 } 63 } else { 64 continue; 65 } 66 67 // We didn't early-continue, so we add the statement to the list. 68 UsageStatements.push_back(CFGStatement->getStmt()); 69 } 70 } 71 72 return UsageStatements; 73 } 74 75 // We declare our EscapesFunctionError enum to be an error code enum. 76 namespace std { 77 template <> struct is_error_code_enum<EscapesFunctionError> : true_type {}; 78 } // namespace std 79 80 // We define the EscapesFunctionErrorCategory which contains the error messages 81 // corresponding to each enum variant. 82 namespace { 83 struct EscapesFunctionErrorCategory : std::error_category { 84 const char *name() const noexcept override; 85 std::string message(int ev) const override; 86 }; 87 88 const char *EscapesFunctionErrorCategory::name() const noexcept { 89 return "escapes function"; 90 } 91 92 std::string EscapesFunctionErrorCategory::message(int ev) const { 93 switch (static_cast<EscapesFunctionError>(ev)) { 94 case EscapesFunctionError::ConstructorDeclNotFound: 95 return "constructor declaration not found"; 96 97 case EscapesFunctionError::FunctionDeclNotFound: 98 return "function declaration not found"; 99 100 case EscapesFunctionError::FunctionIsBuiltin: 101 return "function is builtin"; 102 103 case EscapesFunctionError::FunctionIsVariadic: 104 return "function is variadic"; 105 106 case EscapesFunctionError::ExprNotInCall: 107 return "expression is not in call"; 108 109 case EscapesFunctionError::NoParamForArg: 110 return "no parameter for argument"; 111 112 case EscapesFunctionError::ArgAndParamNotPointers: 113 return "argument and parameter are not pointers"; 114 } 115 } 116 117 const EscapesFunctionErrorCategory TheEscapesFunctionErrorCategory{}; 118 } // namespace 119 120 std::error_code make_error_code(EscapesFunctionError e) { 121 return {static_cast<int>(e), TheEscapesFunctionErrorCategory}; 122 } 123 124 ErrorOr<std::tuple<const Stmt *, const Decl *>> 125 escapesFunction(const Expr *Arg, const CXXConstructExpr *Construct) { 126 // We get the function declaration corresponding to the call. 127 auto CtorDecl = Construct->getConstructor(); 128 if (!CtorDecl) { 129 return EscapesFunctionError::ConstructorDeclNotFound; 130 } 131 132 return escapesFunction(Arg, CtorDecl, Construct->getArgs(), 133 Construct->getNumArgs()); 134 } 135 136 ErrorOr<std::tuple<const Stmt *, const Decl *>> 137 escapesFunction(const Expr *Arg, const CallExpr *Call) { 138 // We get the function declaration corresponding to the call. 139 auto FuncDecl = Call->getDirectCallee(); 140 if (!FuncDecl) { 141 return EscapesFunctionError::FunctionDeclNotFound; 142 } 143 144 return escapesFunction(Arg, FuncDecl, Call->getArgs(), Call->getNumArgs()); 145 } 146 147 ErrorOr<std::tuple<const Stmt *, const Decl *>> 148 escapesFunction(const Expr *Arg, const CXXOperatorCallExpr *OpCall) { 149 // We get the function declaration corresponding to the operator call. 150 auto FuncDecl = OpCall->getDirectCallee(); 151 if (!FuncDecl) { 152 return EscapesFunctionError::FunctionDeclNotFound; 153 } 154 155 auto Args = OpCall->getArgs(); 156 auto NumArgs = OpCall->getNumArgs(); 157 // If this is an infix binary operator defined as a one-param method, we 158 // remove the first argument as it is inserted explicitly and creates a 159 // mismatch with the parameters of the method declaration. 160 if (isInfixBinaryOp(OpCall) && FuncDecl->getNumParams() == 1) { 161 Args++; 162 NumArgs--; 163 } 164 165 return escapesFunction(Arg, FuncDecl, Args, NumArgs); 166 } 167 168 ErrorOr<std::tuple<const Stmt *, const Decl *>> 169 escapesFunction(const Expr *Arg, const FunctionDecl *FuncDecl, 170 const Expr *const *Arguments, unsigned NumArgs) { 171 if (!NumArgs) { 172 return std::make_tuple((const Stmt *)nullptr, (const Decl *)nullptr); 173 } 174 175 if (FuncDecl->getBuiltinID() != 0 || 176 ASTIsInSystemHeader(FuncDecl->getASTContext(), *FuncDecl)) { 177 return EscapesFunctionError::FunctionIsBuiltin; 178 } 179 180 // FIXME: should probably be handled at some point, but it's too annoying 181 // for now. 182 if (FuncDecl->isVariadic()) { 183 return EscapesFunctionError::FunctionIsVariadic; 184 } 185 186 // We find the argument number corresponding to the Arg expression. 187 unsigned ArgNum = 0; 188 for (unsigned i = 0; i < NumArgs; i++) { 189 if (IgnoreTrivials(Arg) == IgnoreTrivials(Arguments[i])) { 190 break; 191 } 192 ++ArgNum; 193 } 194 // If we don't find it, we early-return NoneType. 195 if (ArgNum >= NumArgs) { 196 return EscapesFunctionError::ExprNotInCall; 197 } 198 199 // Now we get the associated parameter. 200 if (ArgNum >= FuncDecl->getNumParams()) { 201 return EscapesFunctionError::NoParamForArg; 202 } 203 auto Param = FuncDecl->getParamDecl(ArgNum); 204 205 // We want both the argument and the parameter to be of pointer type. 206 // FIXME: this is enough for the DanglingOnTemporaryChecker, because the 207 // analysed methods only return pointers, but more cases should probably be 208 // handled when we want to use this function more broadly. 209 if ((!Arg->getType().getNonReferenceType()->isPointerType() && 210 Arg->getType().getNonReferenceType()->isBuiltinType()) || 211 (!Param->getType().getNonReferenceType()->isPointerType() && 212 Param->getType().getNonReferenceType()->isBuiltinType())) { 213 return EscapesFunctionError::ArgAndParamNotPointers; 214 } 215 216 // We retrieve the usages of the parameter in the function. 217 auto Usages = getUsageAsRvalue(Param, FuncDecl); 218 219 // For each usage, we check if it doesn't allow the parameter to escape the 220 // function scope. 221 for (auto Usage : Usages) { 222 // In the case of an assignment. 223 if (auto BinOp = dyn_cast<BinaryOperator>(Usage)) { 224 // We retrieve the declaration the parameter is assigned to. 225 auto DeclRef = dyn_cast<DeclRefExpr>(BinOp->getLHS()); 226 if (!DeclRef) { 227 continue; 228 } 229 230 if (auto ParamDeclaration = dyn_cast<ParmVarDecl>(DeclRef->getDecl())) { 231 // This is the case where the parameter escapes through another 232 // parameter. 233 234 // FIXME: for now we only care about references because we only detect 235 // trivial LHS with just a DeclRefExpr, and not more complex cases like: 236 // void func(Type* param1, Type** param2) { 237 // *param2 = param1; 238 // } 239 // This should be fixed when we have better/more helper functions to 240 // help deal with this kind of lvalue expressions. 241 if (!ParamDeclaration->getType()->isReferenceType()) { 242 continue; 243 } 244 245 return std::make_tuple(Usage, (const Decl *)ParamDeclaration); 246 } else if (auto VarDeclaration = dyn_cast<VarDecl>(DeclRef->getDecl())) { 247 // This is the case where the parameter escapes through a global/static 248 // variable. 249 if (!VarDeclaration->hasGlobalStorage()) { 250 continue; 251 } 252 253 return std::make_tuple(Usage, (const Decl *)VarDeclaration); 254 } else if (auto FieldDeclaration = 255 dyn_cast<FieldDecl>(DeclRef->getDecl())) { 256 // This is the case where the parameter escapes through a field. 257 258 return std::make_tuple(Usage, (const Decl *)FieldDeclaration); 259 } 260 } else if (isa<ReturnStmt>(Usage)) { 261 // This is the case where the parameter escapes through the return value 262 // of the function. 263 if (!FuncDecl->getReturnType()->isPointerType() && 264 !FuncDecl->getReturnType()->isReferenceType()) { 265 continue; 266 } 267 268 return std::make_tuple(Usage, (const Decl *)FuncDecl); 269 } 270 } 271 272 // No early-return, this means that we haven't found any case of funciton 273 // escaping and that therefore the parameter remains in the function scope. 274 return std::make_tuple((const Stmt *)nullptr, (const Decl *)nullptr); 275 }