From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- build/clang-plugin/MustReturnFromCallerChecker.cpp | 136 +++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 build/clang-plugin/MustReturnFromCallerChecker.cpp (limited to 'build/clang-plugin/MustReturnFromCallerChecker.cpp') diff --git a/build/clang-plugin/MustReturnFromCallerChecker.cpp b/build/clang-plugin/MustReturnFromCallerChecker.cpp new file mode 100644 index 0000000000..df28ddb4d3 --- /dev/null +++ b/build/clang-plugin/MustReturnFromCallerChecker.cpp @@ -0,0 +1,136 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MustReturnFromCallerChecker.h" +#include "CustomMatchers.h" + +void MustReturnFromCallerChecker::registerMatchers(MatchFinder *AstMatcher) { + // Look for a call to a MOZ_MUST_RETURN_FROM_CALLER member + AstMatcher->addMatcher( + cxxMemberCallExpr( + on(declRefExpr(to(parmVarDecl()))), + callee(functionDecl(isMozMustReturnFromCaller())), + anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")), + hasAncestor(functionDecl().bind("containing-func")))) + .bind("call"), + this); +} + +void MustReturnFromCallerChecker::check( + const MatchFinder::MatchResult &Result) { + const auto *ContainingLambda = + Result.Nodes.getNodeAs("containing-lambda"); + const auto *ContainingFunc = + Result.Nodes.getNodeAs("containing-func"); + const auto *Call = Result.Nodes.getNodeAs("call"); + + Stmt *Body = nullptr; + if (ContainingLambda) { + Body = ContainingLambda->getBody(); + } else if (ContainingFunc) { + Body = ContainingFunc->getBody(); + } else { + return; + } + assert(Body && "Should have a body by this point"); + + // Generate the CFG for the enclosing function or decl. + CFG::BuildOptions Options; + std::unique_ptr TheCFG = + CFG::buildCFG(nullptr, Body, Result.Context, Options); + if (!TheCFG) { + return; + } + + // Determine which block in the CFG we want to look at the successors of. + StmtToBlockMap BlockMap(TheCFG.get(), Result.Context); + size_t CallIndex; + const auto *Block = BlockMap.blockContainingStmt(Call, &CallIndex); + if (!Block) { + // This statement is not within the CFG! + return; + } + + if (!immediatelyReturns(Block, Result.Context, CallIndex + 1)) { + diag(Call->getBeginLoc(), + "You must immediately return after calling this function", + DiagnosticIDs::Error); + } +} + +bool MustReturnFromCallerChecker::isIgnorable(const Stmt *S) { + auto AfterTrivials = IgnoreTrivials(S); + + // After a call to MOZ_MUST_RETURN_FROM_CALLER function it's ok to have any of + // these expressions. + if (isa(AfterTrivials) || isa(AfterTrivials) || + isa(AfterTrivials) || isa(AfterTrivials) || + isa(AfterTrivials) || + isa(AfterTrivials) || + isa(AfterTrivials) || + isa(AfterTrivials)) { + return true; + } + + // Solitary `this` should be permited, like in the context `return this;` + if (auto TE = dyn_cast(AfterTrivials)) { + if (TE->child_begin() == TE->child_end()) { + return true; + } + return false; + } + + // For UnaryOperator make sure we only accept arithmetic operations. + if (auto UO = dyn_cast(AfterTrivials)) { + if (!UO->isArithmeticOp()) { + return false; + } + return isIgnorable(UO->getSubExpr()); + } + + // It's also OK to call any function or method which is annotated with + // MOZ_MAY_CALL_AFTER_MUST_RETURN. We consider all CXXConversionDecls + // to be MOZ_MAY_CALL_AFTER_MUST_RETURN (like operator T*()). + if (auto CE = dyn_cast(AfterTrivials)) { + auto Callee = CE->getDirectCallee(); + if (Callee && hasCustomAttribute(Callee)) { + return true; + } + + if (Callee && isa(Callee)) { + return true; + } + } + return false; +} + +bool MustReturnFromCallerChecker::immediatelyReturns( + RecurseGuard Block, ASTContext *TheContext, + size_t FromIdx) { + if (Block.isRepeat()) { + return false; + } + + for (size_t I = FromIdx; I < Block->size(); ++I) { + auto S = (*Block)[I].getAs(); + if (!S) { + continue; + } + + // Some statements should be ignored by default due to their CFG context. + if (isIgnorable(S->getStmt())) { + continue; + } + + // Otherwise, this expression is problematic. + return false; + } + + for (auto Succ = Block->succ_begin(); Succ != Block->succ_end(); ++Succ) { + if (!immediatelyReturns(Block.recurse(*Succ), TheContext, 0)) { + return false; + } + } + return true; +} -- cgit v1.2.3