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(
12 on(declRefExpr(to(parmVarDecl()))),
13 callee(functionDecl(isMozMustReturnFromCaller())),
14 anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")),
15 hasAncestor(functionDecl().bind("containing-func"))))
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");
29 if (ContainingLambda
) {
30 Body
= ContainingLambda
->getBody();
31 } else if (ContainingFunc
) {
32 Body
= ContainingFunc
->getBody();
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
);
46 // Determine which block in the CFG we want to look at the successors of.
47 StmtToBlockMap
BlockMap(TheCFG
.get(), Result
.Context
);
49 const auto *Block
= BlockMap
.blockContainingStmt(Call
, &CallIndex
);
51 // This statement is not within the CFG!
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
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
)) {
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()) {
84 // For UnaryOperator make sure we only accept arithmetic operations.
85 if (auto UO
= dyn_cast
<UnaryOperator
>(AfterTrivials
)) {
86 if (!UO
->isArithmeticOp()) {
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
)) {
101 if (Callee
&& isa
<CXXConversionDecl
>(Callee
)) {
108 bool MustReturnFromCallerChecker::immediatelyReturns(
109 RecurseGuard
<const CFGBlock
*> Block
, ASTContext
*TheContext
,
111 if (Block
.isRepeat()) {
115 for (size_t I
= FromIdx
; I
< Block
->size(); ++I
) {
116 Optional
<CFGStmt
> S
= (*Block
)[I
].getAs
<CFGStmt
>();
121 // Some statements should be ignored by default due to their CFG context.
122 if (isIgnorable(S
->getStmt())) {
126 // Otherwise, this expression is problematic.
130 for (auto Succ
= Block
->succ_begin(); Succ
!= Block
->succ_end(); ++Succ
) {
131 if (!immediatelyReturns(Block
.recurse(*Succ
), TheContext
, 0)) {