summaryrefslogtreecommitdiffstats
path: root/build/clang-plugin/MustReturnFromCallerChecker.cpp
blob: df28ddb4d38425bc7e7232abaf1dee5934a2f451 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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<LambdaExpr>("containing-lambda");
  const auto *ContainingFunc =
      Result.Nodes.getNodeAs<FunctionDecl>("containing-func");
  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("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<CFG> 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<ReturnStmt>(AfterTrivials) || isa<CXXConstructExpr>(AfterTrivials) ||
      isa<DeclRefExpr>(AfterTrivials) || isa<MemberExpr>(AfterTrivials) ||
      isa<IntegerLiteral>(AfterTrivials) ||
      isa<FloatingLiteral>(AfterTrivials) ||
      isa<CXXNullPtrLiteralExpr>(AfterTrivials) ||
      isa<CXXBoolLiteralExpr>(AfterTrivials)) {
    return true;
  }

  // Solitary `this` should be permited, like in the context `return this;`
  if (auto TE = dyn_cast<CXXThisExpr>(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<UnaryOperator>(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<CallExpr>(AfterTrivials)) {
    auto Callee = CE->getDirectCallee();
    if (Callee && hasCustomAttribute<moz_may_call_after_must_return>(Callee)) {
      return true;
    }

    if (Callee && isa<CXXConversionDecl>(Callee)) {
      return true;
    }
  }
  return false;
}

bool MustReturnFromCallerChecker::immediatelyReturns(
    RecurseGuard<const CFGBlock *> 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<CFGStmt>();
    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;
}