Bug 1523562 [wpt PR 14965] - Sync Mozilla CSS tests as of 2019-01-20, a=testonly
[gecko.git] / build / clang-plugin / MustReturnFromCallerChecker.cpp
blob186c53e1d94565e7045527262bfa37d1e8e07c0d
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 function
10 AstMatcher->addMatcher(
11 callExpr(callee(functionDecl(isMozMustReturnFromCaller())),
12 anyOf(hasAncestor(lambdaExpr().bind("containing-lambda")),
13 hasAncestor(functionDecl().bind("containing-func"))))
14 .bind("call"),
15 this);
18 void MustReturnFromCallerChecker::check(
19 const MatchFinder::MatchResult &Result) {
20 const auto *ContainingLambda =
21 Result.Nodes.getNodeAs<LambdaExpr>("containing-lambda");
22 const auto *ContainingFunc =
23 Result.Nodes.getNodeAs<FunctionDecl>("containing-func");
24 const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
26 Stmt *Body = nullptr;
27 if (ContainingLambda) {
28 Body = ContainingLambda->getBody();
29 } else if (ContainingFunc) {
30 Body = ContainingFunc->getBody();
31 } else {
32 return;
34 assert(Body && "Should have a body by this point");
36 // Generate the CFG for the enclosing function or decl.
37 CFG::BuildOptions Options;
38 std::unique_ptr<CFG> TheCFG =
39 CFG::buildCFG(nullptr, Body, Result.Context, Options);
40 if (!TheCFG) {
41 return;
44 // Determine which block in the CFG we want to look at the successors of.
45 StmtToBlockMap BlockMap(TheCFG.get(), Result.Context);
46 size_t CallIndex;
47 const auto *Block = BlockMap.blockContainingStmt(Call, &CallIndex);
48 assert(Block && "This statement should be within the CFG!");
50 if (!immediatelyReturns(Block, Result.Context, CallIndex + 1)) {
51 diag(Call->getBeginLoc(),
52 "You must immediately return after calling this function",
53 DiagnosticIDs::Error);
57 bool MustReturnFromCallerChecker::immediatelyReturns(
58 RecurseGuard<const CFGBlock *> Block, ASTContext *TheContext,
59 size_t FromIdx) {
60 if (Block.isRepeat()) {
61 return false;
64 for (size_t I = FromIdx; I < Block->size(); ++I) {
65 Optional<CFGStmt> S = (*Block)[I].getAs<CFGStmt>();
66 if (!S) {
67 continue;
70 auto AfterTrivials = IgnoreTrivials(S->getStmt());
72 // If we are looking at a ConstructExpr, a DeclRefExpr or a MemberExpr it's
73 // OK to use them after a call to a MOZ_MUST_RETURN_FROM_CALLER function.
74 // It is also, of course, OK to look at a ReturnStmt.
75 if (isa<ReturnStmt>(AfterTrivials) ||
76 isa<CXXConstructExpr>(AfterTrivials) ||
77 isa<DeclRefExpr>(AfterTrivials) || isa<MemberExpr>(AfterTrivials)) {
78 continue;
81 // It's also OK to call any function or method which is annotated with
82 // MOZ_MAY_CALL_AFTER_MUST_RETURN. We consider all CXXConversionDecls
83 // to be MOZ_MAY_CALL_AFTER_MUST_RETURN (like operator T*()).
84 if (auto CE = dyn_cast<CallExpr>(AfterTrivials)) {
85 auto Callee = CE->getDirectCallee();
86 if (Callee &&
87 hasCustomAttribute<moz_may_call_after_must_return>(Callee)) {
88 continue;
91 if (Callee && isa<CXXConversionDecl>(Callee)) {
92 continue;
96 // Otherwise, this expression is problematic.
97 return false;
100 for (auto Succ = Block->succ_begin(); Succ != Block->succ_end(); ++Succ) {
101 if (!immediatelyReturns(Block.recurse(*Succ), TheContext, 0)) {
102 return false;
105 return true;