In various places, we would previously call `FunctionDecl::hasBody()` (which checks whether any redeclaration of the function has a body, not necessarily the one on which `hasBody()` is being called). This is bug-prone, as a recent bug in Crubit's nullability checker has shown ([fix](4b01ed0f14), [fix for the fix](e0c5d8ddd7)). Instead, we now use `FunctionDecl::doesThisDeclarationHaveABody()` which, as the name implies, checks whether the specific redeclaration it is being called on has a body. Alternatively, I considered being more lenient and "canonicalizing" to the `FunctionDecl` that has the body if the `FunctionDecl` being passed is a different redeclaration. However, this also risks hiding bugs: A caller might inadverently perform the analysis for all redeclarations of a function and end up duplicating work without realizing it. By accepting only the redeclaration that contains the body, we prevent this. I've checked, and all clients that I'm aware of do currently pass in the redeclaration that contains the function body. Typically this is because they use the `ast_matchers::hasBody()` matcher which, unlike `FunctionDecl::hasBody()`, only matches for the redeclaration containing the body.
122 lines
3.8 KiB
C++
122 lines
3.8 KiB
C++
//===- ControlFlowContext.cpp ---------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines a ControlFlowContext class that is used by dataflow
|
|
// analyses that run over Control-Flow Graphs (CFGs).
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/Stmt.h"
|
|
#include "clang/Analysis/CFG.h"
|
|
#include "llvm/ADT/BitVector.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include <utility>
|
|
|
|
namespace clang {
|
|
namespace dataflow {
|
|
|
|
/// Returns a map from statements to basic blocks that contain them.
|
|
static llvm::DenseMap<const Stmt *, const CFGBlock *>
|
|
buildStmtToBasicBlockMap(const CFG &Cfg) {
|
|
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock;
|
|
for (const CFGBlock *Block : Cfg) {
|
|
if (Block == nullptr)
|
|
continue;
|
|
|
|
for (const CFGElement &Element : *Block) {
|
|
auto Stmt = Element.getAs<CFGStmt>();
|
|
if (!Stmt)
|
|
continue;
|
|
|
|
StmtToBlock[Stmt->getStmt()] = Block;
|
|
}
|
|
if (const Stmt *TerminatorStmt = Block->getTerminatorStmt())
|
|
StmtToBlock[TerminatorStmt] = Block;
|
|
}
|
|
return StmtToBlock;
|
|
}
|
|
|
|
static llvm::BitVector findReachableBlocks(const CFG &Cfg) {
|
|
llvm::BitVector BlockReachable(Cfg.getNumBlockIDs(), false);
|
|
|
|
llvm::SmallVector<const CFGBlock *> BlocksToVisit;
|
|
BlocksToVisit.push_back(&Cfg.getEntry());
|
|
while (!BlocksToVisit.empty()) {
|
|
const CFGBlock *Block = BlocksToVisit.back();
|
|
BlocksToVisit.pop_back();
|
|
|
|
if (BlockReachable[Block->getBlockID()])
|
|
continue;
|
|
|
|
BlockReachable[Block->getBlockID()] = true;
|
|
|
|
for (const CFGBlock *Succ : Block->succs())
|
|
if (Succ)
|
|
BlocksToVisit.push_back(Succ);
|
|
}
|
|
|
|
return BlockReachable;
|
|
}
|
|
|
|
llvm::Expected<ControlFlowContext>
|
|
ControlFlowContext::build(const FunctionDecl &Func) {
|
|
if (!Func.doesThisDeclarationHaveABody())
|
|
return llvm::createStringError(
|
|
std::make_error_code(std::errc::invalid_argument),
|
|
"Cannot analyze function without a body");
|
|
|
|
return build(Func, *Func.getBody(), Func.getASTContext());
|
|
}
|
|
|
|
llvm::Expected<ControlFlowContext>
|
|
ControlFlowContext::build(const Decl &D, Stmt &S, ASTContext &C) {
|
|
if (D.isTemplated())
|
|
return llvm::createStringError(
|
|
std::make_error_code(std::errc::invalid_argument),
|
|
"Cannot analyze templated declarations");
|
|
|
|
// The shape of certain elements of the AST can vary depending on the
|
|
// language. We currently only support C++.
|
|
if (!C.getLangOpts().CPlusPlus)
|
|
return llvm::createStringError(
|
|
std::make_error_code(std::errc::invalid_argument),
|
|
"Can only analyze C++");
|
|
|
|
CFG::BuildOptions Options;
|
|
Options.PruneTriviallyFalseEdges = true;
|
|
Options.AddImplicitDtors = true;
|
|
Options.AddTemporaryDtors = true;
|
|
Options.AddInitializers = true;
|
|
Options.AddCXXDefaultInitExprInCtors = true;
|
|
Options.AddLifetime = true;
|
|
|
|
// Ensure that all sub-expressions in basic blocks are evaluated.
|
|
Options.setAllAlwaysAdd();
|
|
|
|
auto Cfg = CFG::buildCFG(&D, &S, &C, Options);
|
|
if (Cfg == nullptr)
|
|
return llvm::createStringError(
|
|
std::make_error_code(std::errc::invalid_argument),
|
|
"CFG::buildCFG failed");
|
|
|
|
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock =
|
|
buildStmtToBasicBlockMap(*Cfg);
|
|
|
|
llvm::BitVector BlockReachable = findReachableBlocks(*Cfg);
|
|
|
|
return ControlFlowContext(D, std::move(Cfg), std::move(StmtToBlock),
|
|
std::move(BlockReachable));
|
|
}
|
|
|
|
} // namespace dataflow
|
|
} // namespace clang
|