Source code

Revision control

Other Tools

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
assert(Block && "This statement should be within the CFG!");
51
52
if (!immediatelyReturns(Block, Result.Context, CallIndex + 1)) {
53
diag(Call->getBeginLoc(),
54
"You must immediately return after calling this function",
55
DiagnosticIDs::Error);
56
}
57
}
58
59
bool MustReturnFromCallerChecker::immediatelyReturns(
60
RecurseGuard<const CFGBlock *> Block, ASTContext *TheContext,
61
size_t FromIdx) {
62
if (Block.isRepeat()) {
63
return false;
64
}
65
66
for (size_t I = FromIdx; I < Block->size(); ++I) {
67
Optional<CFGStmt> S = (*Block)[I].getAs<CFGStmt>();
68
if (!S) {
69
continue;
70
}
71
72
auto AfterTrivials = IgnoreTrivials(S->getStmt());
73
74
// If we are looking at a ConstructExpr, a DeclRefExpr or a MemberExpr it's
75
// OK to use them after a call to a MOZ_MUST_RETURN_FROM_CALLER function.
76
// It is also, of course, OK to look at a ReturnStmt.
77
if (isa<ReturnStmt>(AfterTrivials) ||
78
isa<CXXConstructExpr>(AfterTrivials) ||
79
isa<DeclRefExpr>(AfterTrivials) || isa<MemberExpr>(AfterTrivials)) {
80
continue;
81
}
82
83
// It's also OK to call any function or method which is annotated with
84
// MOZ_MAY_CALL_AFTER_MUST_RETURN. We consider all CXXConversionDecls
85
// to be MOZ_MAY_CALL_AFTER_MUST_RETURN (like operator T*()).
86
if (auto CE = dyn_cast<CallExpr>(AfterTrivials)) {
87
auto Callee = CE->getDirectCallee();
88
if (Callee &&
89
hasCustomAttribute<moz_may_call_after_must_return>(Callee)) {
90
continue;
91
}
92
93
if (Callee && isa<CXXConversionDecl>(Callee)) {
94
continue;
95
}
96
}
97
98
// Otherwise, this expression is problematic.
99
return false;
100
}
101
102
for (auto Succ = Block->succ_begin(); Succ != Block->succ_end(); ++Succ) {
103
if (!immediatelyReturns(Block.recurse(*Succ), TheContext, 0)) {
104
return false;
105
}
106
}
107
return true;
108
}