Bug 1814798 - pt 1. Add bool to enable/disable PHC at runtime r=glandium
[gecko.git] / build / clang-plugin / MustReturnFromCallerChecker.cpp
blobdf28ddb4d38425bc7e7232abaf1dee5934a2f451
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/. */
5 #include "MustReturnFromCallerChecker.h"
6 #include "CustomMatchers.h"
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);
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");
28 Stmt *Body = nullptr;
29 if (ContainingLambda) {
30 Body = ContainingLambda->getBody();
31 } else if (ContainingFunc) {
32 Body = ContainingFunc->getBody();
33 } else {
34 return;
36 assert(Body && "Should have a body by this point");
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;
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;
55 if (!immediatelyReturns(Block, Result.Context, CallIndex + 1)) {
56 diag(Call->getBeginLoc(),
57 "You must immediately return after calling this function",
58 DiagnosticIDs::Error);
62 bool MustReturnFromCallerChecker::isIgnorable(const Stmt *S) {
63 auto AfterTrivials = IgnoreTrivials(S);
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;
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;
81 return false;
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;
89 return isIgnorable(UO->getSubExpr());
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;
101 if (Callee && isa<CXXConversionDecl>(Callee)) {
102 return true;
105 return false;
108 bool MustReturnFromCallerChecker::immediatelyReturns(
109 RecurseGuard<const CFGBlock *> Block, ASTContext *TheContext,
110 size_t FromIdx) {
111 if (Block.isRepeat()) {
112 return false;
115 for (size_t I = FromIdx; I < Block->size(); ++I) {
116 auto S = (*Block)[I].getAs<CFGStmt>();
117 if (!S) {
118 continue;
121 // Some statements should be ignored by default due to their CFG context.
122 if (isIgnorable(S->getStmt())) {
123 continue;
126 // Otherwise, this expression is problematic.
127 return false;
130 for (auto Succ = Block->succ_begin(); Succ != Block->succ_end(); ++Succ) {
131 if (!immediatelyReturns(Block.recurse(*Succ), TheContext, 0)) {
132 return false;
135 return true;