MustReturnFromCallerChecker.cpp (4329B)
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 "MustReturnFromCallerChecker.h" 6 #include "CustomMatchers.h" 7 8 void MustReturnFromCallerChecker::registerMatchers(MatchFinder *AstMatcher) { 9 // Look for a call to a MOZ_MUST_RETURN_FROM_CALLER member 10 AstMatcher->addMatcher( 11 cxxMemberCallExpr( 12 on(declRefExpr(to(parmVarDecl()))), 13 callee(functionDecl(isMozMustReturnFromCaller())), 14 anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")), 15 hasAncestor(functionDecl().bind("containing-func")))) 16 .bind("call"), 17 this); 18 } 19 20 void MustReturnFromCallerChecker::check( 21 const MatchFinder::MatchResult &Result) { 22 const auto *ContainingLambda = 23 Result.Nodes.getNodeAs<LambdaExpr>("containing-lambda"); 24 const auto *ContainingFunc = 25 Result.Nodes.getNodeAs<FunctionDecl>("containing-func"); 26 const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call"); 27 28 Stmt *Body = nullptr; 29 if (ContainingLambda) { 30 Body = ContainingLambda->getBody(); 31 } else if (ContainingFunc) { 32 Body = ContainingFunc->getBody(); 33 } else { 34 return; 35 } 36 assert(Body && "Should have a body by this point"); 37 38 // Generate the CFG for the enclosing function or decl. 39 CFG::BuildOptions Options; 40 std::unique_ptr<CFG> TheCFG = 41 CFG::buildCFG(nullptr, Body, Result.Context, Options); 42 if (!TheCFG) { 43 return; 44 } 45 46 // Determine which block in the CFG we want to look at the successors of. 47 StmtToBlockMap BlockMap(TheCFG.get(), Result.Context); 48 size_t CallIndex; 49 const auto *Block = BlockMap.blockContainingStmt(Call, &CallIndex); 50 if (!Block) { 51 // This statement is not within the CFG! 52 return; 53 } 54 55 if (!immediatelyReturns(Block, Result.Context, CallIndex + 1)) { 56 diag(Call->getBeginLoc(), 57 "You must immediately return after calling this function", 58 DiagnosticIDs::Error); 59 } 60 } 61 62 bool MustReturnFromCallerChecker::isIgnorable(const Stmt *S) { 63 auto AfterTrivials = IgnoreTrivials(S); 64 65 // After a call to MOZ_MUST_RETURN_FROM_CALLER function it's ok to have any of 66 // these expressions. 67 if (isa<ReturnStmt>(AfterTrivials) || isa<CXXConstructExpr>(AfterTrivials) || 68 isa<DeclRefExpr>(AfterTrivials) || isa<MemberExpr>(AfterTrivials) || 69 isa<IntegerLiteral>(AfterTrivials) || 70 isa<FloatingLiteral>(AfterTrivials) || 71 isa<CXXNullPtrLiteralExpr>(AfterTrivials) || 72 isa<CXXBoolLiteralExpr>(AfterTrivials)) { 73 return true; 74 } 75 76 // Solitary `this` should be permited, like in the context `return this;` 77 if (auto TE = dyn_cast<CXXThisExpr>(AfterTrivials)) { 78 if (TE->child_begin() == TE->child_end()) { 79 return true; 80 } 81 return false; 82 } 83 84 // For UnaryOperator make sure we only accept arithmetic operations. 85 if (auto UO = dyn_cast<UnaryOperator>(AfterTrivials)) { 86 if (!UO->isArithmeticOp()) { 87 return false; 88 } 89 return isIgnorable(UO->getSubExpr()); 90 } 91 92 // It's also OK to call any function or method which is annotated with 93 // MOZ_MAY_CALL_AFTER_MUST_RETURN. We consider all CXXConversionDecls 94 // to be MOZ_MAY_CALL_AFTER_MUST_RETURN (like operator T*()). 95 if (auto CE = dyn_cast<CallExpr>(AfterTrivials)) { 96 auto Callee = CE->getDirectCallee(); 97 if (Callee && hasCustomAttribute<moz_may_call_after_must_return>(Callee)) { 98 return true; 99 } 100 101 if (Callee && isa<CXXConversionDecl>(Callee)) { 102 return true; 103 } 104 } 105 return false; 106 } 107 108 bool MustReturnFromCallerChecker::immediatelyReturns( 109 RecurseGuard<const CFGBlock *> Block, ASTContext *TheContext, 110 size_t FromIdx) { 111 if (Block.isRepeat()) { 112 return false; 113 } 114 115 for (size_t I = FromIdx; I < Block->size(); ++I) { 116 auto S = (*Block)[I].getAs<CFGStmt>(); 117 if (!S) { 118 continue; 119 } 120 121 // Some statements should be ignored by default due to their CFG context. 122 if (isIgnorable(S->getStmt())) { 123 continue; 124 } 125 126 // Otherwise, this expression is problematic. 127 return false; 128 } 129 130 for (auto Succ = Block->succ_begin(); Succ != Block->succ_end(); ++Succ) { 131 if (!immediatelyReturns(Block.recurse(*Succ), TheContext, 0)) { 132 return false; 133 } 134 } 135 return true; 136 }