DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (c68fe15a81fc)

VCS Links

Line Code
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
/* 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);
  assert(Block && "This statement should be within the CFG!");

  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) {
    Optional<CFGStmt> 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;
}