[clang][dataflow][NFC] Rename ControlFlowContext to AdornedCFG. (#85640)
This expresses better what the class actually does, and it reduces the number of `Context`s that we have in the codebase. A deprecated alias `ControlFlowContext` is available from the old header.
This commit is contained in:
@@ -122,6 +122,7 @@ clang/include/clang/Analysis/MacroExpansionContext.h
|
||||
clang/include/clang/Analysis/Analyses/CalledOnceCheck.h
|
||||
clang/include/clang/Analysis/Analyses/CFGReachabilityAnalysis.h
|
||||
clang/include/clang/Analysis/Analyses/ExprMutationAnalyzer.h
|
||||
clang/include/clang/Analysis/FlowSensitive/AdornedCFG.h
|
||||
clang/include/clang/Analysis/FlowSensitive/ControlFlowContext.h
|
||||
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysis.h
|
||||
clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h
|
||||
@@ -306,7 +307,7 @@ clang/include/clang-c/Index.h
|
||||
clang/lib/Analysis/CalledOnceCheck.cpp
|
||||
clang/lib/Analysis/CloneDetection.cpp
|
||||
clang/lib/Analysis/CodeInjector.cpp
|
||||
clang/lib/Analysis/FlowSensitive/ControlFlowContext.cpp
|
||||
clang/lib/Analysis/FlowSensitive/AdornedCFG.cpp
|
||||
clang/lib/Analysis/FlowSensitive/DataflowAnalysisContext.cpp
|
||||
clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp
|
||||
clang/lib/Analysis/FlowSensitive/DebugSupport.cpp
|
||||
|
||||
96
clang/include/clang/Analysis/FlowSensitive/AdornedCFG.h
Normal file
96
clang/include/clang/Analysis/FlowSensitive/AdornedCFG.h
Normal file
@@ -0,0 +1,96 @@
|
||||
//===-- AdornedCFG.h ------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// 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 an AdornedCFG class that is used by dataflow analyses that
|
||||
// run over Control-Flow Graphs (CFGs).
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_ADORNEDCFG_H
|
||||
#define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_ADORNEDCFG_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 <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
namespace dataflow {
|
||||
|
||||
/// Holds CFG with additional information derived from it that is needed to
|
||||
/// perform dataflow analysis.
|
||||
class AdornedCFG {
|
||||
public:
|
||||
/// Builds an `AdornedCFG` from a `FunctionDecl`.
|
||||
/// `Func.doesThisDeclarationHaveABody()` must be true, and
|
||||
/// `Func.isTemplated()` must be false.
|
||||
static llvm::Expected<AdornedCFG> build(const FunctionDecl &Func);
|
||||
|
||||
/// Builds an `AdornedCFG` from an AST node. `D` is the function in which
|
||||
/// `S` resides. `D.isTemplated()` must be false.
|
||||
static llvm::Expected<AdornedCFG> build(const Decl &D, Stmt &S,
|
||||
ASTContext &C);
|
||||
|
||||
/// Returns the `Decl` containing the statement used to construct the CFG, if
|
||||
/// available.
|
||||
const Decl &getDecl() const { return ContainingDecl; }
|
||||
|
||||
/// Returns the CFG that is stored in this context.
|
||||
const CFG &getCFG() const { return *Cfg; }
|
||||
|
||||
/// Returns a mapping from statements to basic blocks that contain them.
|
||||
const llvm::DenseMap<const Stmt *, const CFGBlock *> &getStmtToBlock() const {
|
||||
return StmtToBlock;
|
||||
}
|
||||
|
||||
/// Returns whether `B` is reachable from the entry block.
|
||||
bool isBlockReachable(const CFGBlock &B) const {
|
||||
return BlockReachable[B.getBlockID()];
|
||||
}
|
||||
|
||||
/// Returns whether `B` contains an expression that is consumed in a
|
||||
/// different block than `B` (i.e. the parent of the expression is in a
|
||||
/// different block).
|
||||
/// This happens if there is control flow within a full-expression (triggered
|
||||
/// by `&&`, `||`, or the conditional operator). Note that the operands of
|
||||
/// these operators are not the only expressions that can be consumed in a
|
||||
/// different block. For example, in the function call
|
||||
/// `f(&i, cond() ? 1 : 0)`, `&i` is in a different block than the `CallExpr`.
|
||||
bool containsExprConsumedInDifferentBlock(const CFGBlock &B) const {
|
||||
return ContainsExprConsumedInDifferentBlock.contains(&B);
|
||||
}
|
||||
|
||||
private:
|
||||
AdornedCFG(
|
||||
const Decl &D, std::unique_ptr<CFG> Cfg,
|
||||
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock,
|
||||
llvm::BitVector BlockReachable,
|
||||
llvm::DenseSet<const CFGBlock *> ContainsExprConsumedInDifferentBlock)
|
||||
: ContainingDecl(D), Cfg(std::move(Cfg)),
|
||||
StmtToBlock(std::move(StmtToBlock)),
|
||||
BlockReachable(std::move(BlockReachable)),
|
||||
ContainsExprConsumedInDifferentBlock(
|
||||
std::move(ContainsExprConsumedInDifferentBlock)) {}
|
||||
|
||||
/// The `Decl` containing the statement used to construct the CFG.
|
||||
const Decl &ContainingDecl;
|
||||
std::unique_ptr<CFG> Cfg;
|
||||
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock;
|
||||
llvm::BitVector BlockReachable;
|
||||
llvm::DenseSet<const CFGBlock *> ContainsExprConsumedInDifferentBlock;
|
||||
};
|
||||
|
||||
} // namespace dataflow
|
||||
} // namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_ADORNEDCFG_H
|
||||
@@ -6,89 +6,20 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines a ControlFlowContext class that is used by dataflow
|
||||
// analyses that run over Control-Flow Graphs (CFGs).
|
||||
// This file defines a deprecated alias for AdornedCFG.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_CONTROLFLOWCONTEXT_H
|
||||
#define LLVM_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 <memory>
|
||||
#include <utility>
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
|
||||
namespace clang {
|
||||
namespace dataflow {
|
||||
|
||||
/// Holds CFG and other derived context that is needed to perform dataflow
|
||||
/// analysis.
|
||||
class ControlFlowContext {
|
||||
public:
|
||||
/// Builds a ControlFlowContext from a `FunctionDecl`.
|
||||
/// `Func.doesThisDeclarationHaveABody()` must be true, and
|
||||
/// `Func.isTemplated()` must be false.
|
||||
static llvm::Expected<ControlFlowContext> build(const FunctionDecl &Func);
|
||||
|
||||
/// Builds a ControlFlowContext from an AST node. `D` is the function in which
|
||||
/// `S` resides. `D.isTemplated()` must be false.
|
||||
static llvm::Expected<ControlFlowContext> build(const Decl &D, Stmt &S,
|
||||
ASTContext &C);
|
||||
|
||||
/// Returns the `Decl` containing the statement used to construct the CFG, if
|
||||
/// available.
|
||||
const Decl &getDecl() const { return ContainingDecl; }
|
||||
|
||||
/// Returns the CFG that is stored in this context.
|
||||
const CFG &getCFG() const { return *Cfg; }
|
||||
|
||||
/// Returns a mapping from statements to basic blocks that contain them.
|
||||
const llvm::DenseMap<const Stmt *, const CFGBlock *> &getStmtToBlock() const {
|
||||
return StmtToBlock;
|
||||
}
|
||||
|
||||
/// Returns whether `B` is reachable from the entry block.
|
||||
bool isBlockReachable(const CFGBlock &B) const {
|
||||
return BlockReachable[B.getBlockID()];
|
||||
}
|
||||
|
||||
/// Returns whether `B` contains an expression that is consumed in a
|
||||
/// different block than `B` (i.e. the parent of the expression is in a
|
||||
/// different block).
|
||||
/// This happens if there is control flow within a full-expression (triggered
|
||||
/// by `&&`, `||`, or the conditional operator). Note that the operands of
|
||||
/// these operators are not the only expressions that can be consumed in a
|
||||
/// different block. For example, in the function call
|
||||
/// `f(&i, cond() ? 1 : 0)`, `&i` is in a different block than the `CallExpr`.
|
||||
bool containsExprConsumedInDifferentBlock(const CFGBlock &B) const {
|
||||
return ContainsExprConsumedInDifferentBlock.contains(&B);
|
||||
}
|
||||
|
||||
private:
|
||||
ControlFlowContext(
|
||||
const Decl &D, std::unique_ptr<CFG> Cfg,
|
||||
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock,
|
||||
llvm::BitVector BlockReachable,
|
||||
llvm::DenseSet<const CFGBlock *> ContainsExprConsumedInDifferentBlock)
|
||||
: ContainingDecl(D), Cfg(std::move(Cfg)),
|
||||
StmtToBlock(std::move(StmtToBlock)),
|
||||
BlockReachable(std::move(BlockReachable)),
|
||||
ContainsExprConsumedInDifferentBlock(
|
||||
std::move(ContainsExprConsumedInDifferentBlock)) {}
|
||||
|
||||
/// The `Decl` containing the statement used to construct the CFG.
|
||||
const Decl &ContainingDecl;
|
||||
std::unique_ptr<CFG> Cfg;
|
||||
llvm::DenseMap<const Stmt *, const CFGBlock *> StmtToBlock;
|
||||
llvm::BitVector BlockReachable;
|
||||
llvm::DenseSet<const CFGBlock *> ContainsExprConsumedInDifferentBlock;
|
||||
};
|
||||
// This is a deprecated alias. Use `AdornedCFG` instead.
|
||||
using ControlFlowContext = AdornedCFG;
|
||||
|
||||
} // namespace dataflow
|
||||
} // namespace clang
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Analysis/CFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
|
||||
#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
|
||||
@@ -195,8 +195,7 @@ template <typename AnalysisT>
|
||||
llvm::Expected<std::vector<
|
||||
std::optional<DataflowAnalysisState<typename AnalysisT::Lattice>>>>
|
||||
runDataflowAnalysis(
|
||||
const ControlFlowContext &CFCtx, AnalysisT &Analysis,
|
||||
const Environment &InitEnv,
|
||||
const AdornedCFG &ACFG, AnalysisT &Analysis, const Environment &InitEnv,
|
||||
std::function<void(const CFGElement &, const DataflowAnalysisState<
|
||||
typename AnalysisT::Lattice> &)>
|
||||
PostVisitCFG = nullptr,
|
||||
@@ -218,7 +217,7 @@ runDataflowAnalysis(
|
||||
}
|
||||
|
||||
auto TypeErasedBlockStates = runTypeErasedDataflowAnalysis(
|
||||
CFCtx, Analysis, InitEnv, PostVisitCFGClosure, MaxBlockVisits);
|
||||
ACFG, Analysis, InitEnv, PostVisitCFGClosure, MaxBlockVisits);
|
||||
if (!TypeErasedBlockStates)
|
||||
return TypeErasedBlockStates.takeError();
|
||||
|
||||
@@ -280,8 +279,7 @@ llvm::Expected<llvm::SmallVector<Diagnostic>> diagnoseFunction(
|
||||
Diagnoser,
|
||||
std::int64_t MaxSATIterations = 1'000'000'000,
|
||||
std::int32_t MaxBlockVisits = 20'000) {
|
||||
llvm::Expected<ControlFlowContext> Context =
|
||||
ControlFlowContext::build(FuncDecl);
|
||||
llvm::Expected<AdornedCFG> Context = AdornedCFG::build(FuncDecl);
|
||||
if (!Context)
|
||||
return Context.takeError();
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/TypeOrdering.h"
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/Arena.h"
|
||||
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/Solver.h"
|
||||
#include "clang/Analysis/FlowSensitive/StorageLocation.h"
|
||||
#include "clang/Analysis/FlowSensitive/Value.h"
|
||||
@@ -183,9 +183,9 @@ public:
|
||||
LLVM_DUMP_METHOD void dumpFlowCondition(Atom Token,
|
||||
llvm::raw_ostream &OS = llvm::dbgs());
|
||||
|
||||
/// Returns the `ControlFlowContext` registered for `F`, if any. Otherwise,
|
||||
/// Returns the `AdornedCFG` registered for `F`, if any. Otherwise,
|
||||
/// returns null.
|
||||
const ControlFlowContext *getControlFlowContext(const FunctionDecl *F);
|
||||
const AdornedCFG *getAdornedCFG(const FunctionDecl *F);
|
||||
|
||||
const Options &getOptions() { return Opts; }
|
||||
|
||||
@@ -296,7 +296,7 @@ private:
|
||||
llvm::DenseMap<Atom, const Formula *> FlowConditionConstraints;
|
||||
const Formula *Invariant = nullptr;
|
||||
|
||||
llvm::DenseMap<const FunctionDecl *, ControlFlowContext> FunctionContexts;
|
||||
llvm::DenseMap<const FunctionDecl *, AdornedCFG> FunctionContexts;
|
||||
|
||||
// Fields modeled by environments covered by this context.
|
||||
FieldSet ModeledFields;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace clang::dataflow {
|
||||
// Forward declarations so we can use Logger anywhere in the framework.
|
||||
class ControlFlowContext;
|
||||
class AdornedCFG;
|
||||
class TypeErasedDataflowAnalysis;
|
||||
struct TypeErasedDataflowAnalysisState;
|
||||
|
||||
@@ -40,8 +40,8 @@ public:
|
||||
|
||||
/// Called by the framework as we start analyzing a new function or statement.
|
||||
/// Forms a pair with endAnalysis().
|
||||
virtual void beginAnalysis(const ControlFlowContext &,
|
||||
TypeErasedDataflowAnalysis &) {}
|
||||
virtual void beginAnalysis(const AdornedCFG &, TypeErasedDataflowAnalysis &) {
|
||||
}
|
||||
virtual void endAnalysis() {}
|
||||
|
||||
// At any time during the analysis, we're computing the state for some target
|
||||
|
||||
@@ -29,12 +29,12 @@ public:
|
||||
// `CurState` is the pending state currently associated with this block. These
|
||||
// are supplied separately as the pending state for the current block may not
|
||||
// yet be represented in `BlockToState`.
|
||||
StmtToEnvMap(const ControlFlowContext &CFCtx,
|
||||
StmtToEnvMap(const AdornedCFG &ACFG,
|
||||
llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>>
|
||||
BlockToState,
|
||||
unsigned CurBlockID,
|
||||
const TypeErasedDataflowAnalysisState &CurState)
|
||||
: CFCtx(CFCtx), BlockToState(BlockToState), CurBlockID(CurBlockID),
|
||||
: ACFG(ACFG), BlockToState(BlockToState), CurBlockID(CurBlockID),
|
||||
CurState(CurState) {}
|
||||
|
||||
/// Returns the environment of the basic block that contains `S`.
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
const Environment *getEnvironment(const Stmt &S) const;
|
||||
|
||||
private:
|
||||
const ControlFlowContext &CFCtx;
|
||||
const AdornedCFG &ACFG;
|
||||
llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>> BlockToState;
|
||||
unsigned CurBlockID;
|
||||
const TypeErasedDataflowAnalysisState &CurState;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Stmt.h"
|
||||
#include "clang/Analysis/CFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowLattice.h"
|
||||
@@ -146,7 +146,7 @@ struct TypeErasedDataflowAnalysisState {
|
||||
/// from converging.
|
||||
llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>>
|
||||
runTypeErasedDataflowAnalysis(
|
||||
const ControlFlowContext &CFCtx, TypeErasedDataflowAnalysis &Analysis,
|
||||
const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
|
||||
const Environment &InitEnv,
|
||||
std::function<void(const CFGElement &,
|
||||
const TypeErasedDataflowAnalysisState &)>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//===- ControlFlowContext.cpp ---------------------------------------------===//
|
||||
//===- AdornedCFG.cpp ---------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
@@ -6,12 +6,12 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines a ControlFlowContext class that is used by dataflow
|
||||
// analyses that run over Control-Flow Graphs (CFGs).
|
||||
// This file defines an `AdornedCFG` class that is used by dataflow analyses
|
||||
// that run over Control-Flow Graphs (CFGs).
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/Stmt.h"
|
||||
@@ -126,8 +126,7 @@ buildContainsExprConsumedInDifferentBlock(
|
||||
return Result;
|
||||
}
|
||||
|
||||
llvm::Expected<ControlFlowContext>
|
||||
ControlFlowContext::build(const FunctionDecl &Func) {
|
||||
llvm::Expected<AdornedCFG> AdornedCFG::build(const FunctionDecl &Func) {
|
||||
if (!Func.doesThisDeclarationHaveABody())
|
||||
return llvm::createStringError(
|
||||
std::make_error_code(std::errc::invalid_argument),
|
||||
@@ -136,8 +135,8 @@ ControlFlowContext::build(const FunctionDecl &Func) {
|
||||
return build(Func, *Func.getBody(), Func.getASTContext());
|
||||
}
|
||||
|
||||
llvm::Expected<ControlFlowContext>
|
||||
ControlFlowContext::build(const Decl &D, Stmt &S, ASTContext &C) {
|
||||
llvm::Expected<AdornedCFG> AdornedCFG::build(const Decl &D, Stmt &S,
|
||||
ASTContext &C) {
|
||||
if (D.isTemplated())
|
||||
return llvm::createStringError(
|
||||
std::make_error_code(std::errc::invalid_argument),
|
||||
@@ -175,9 +174,9 @@ ControlFlowContext::build(const Decl &D, Stmt &S, ASTContext &C) {
|
||||
llvm::DenseSet<const CFGBlock *> ContainsExprConsumedInDifferentBlock =
|
||||
buildContainsExprConsumedInDifferentBlock(*Cfg, StmtToBlock);
|
||||
|
||||
return ControlFlowContext(D, std::move(Cfg), std::move(StmtToBlock),
|
||||
std::move(BlockReachable),
|
||||
std::move(ContainsExprConsumedInDifferentBlock));
|
||||
return AdornedCFG(D, std::move(Cfg), std::move(StmtToBlock),
|
||||
std::move(BlockReachable),
|
||||
std::move(ContainsExprConsumedInDifferentBlock));
|
||||
}
|
||||
|
||||
} // namespace dataflow
|
||||
@@ -1,6 +1,6 @@
|
||||
add_clang_library(clangAnalysisFlowSensitive
|
||||
AdornedCFG.cpp
|
||||
Arena.cpp
|
||||
ControlFlowContext.cpp
|
||||
DataflowAnalysisContext.cpp
|
||||
DataflowEnvironment.cpp
|
||||
Formula.cpp
|
||||
|
||||
@@ -288,8 +288,8 @@ void DataflowAnalysisContext::dumpFlowCondition(Atom Token,
|
||||
}
|
||||
}
|
||||
|
||||
const ControlFlowContext *
|
||||
DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) {
|
||||
const AdornedCFG *
|
||||
DataflowAnalysisContext::getAdornedCFG(const FunctionDecl *F) {
|
||||
// Canonicalize the key:
|
||||
F = F->getDefinition();
|
||||
if (F == nullptr)
|
||||
@@ -299,10 +299,10 @@ DataflowAnalysisContext::getControlFlowContext(const FunctionDecl *F) {
|
||||
return &It->second;
|
||||
|
||||
if (F->doesThisDeclarationHaveABody()) {
|
||||
auto CFCtx = ControlFlowContext::build(*F);
|
||||
auto ACFG = AdornedCFG::build(*F);
|
||||
// FIXME: Handle errors.
|
||||
assert(CFCtx);
|
||||
auto Result = FunctionContexts.insert({F, std::move(*CFCtx)});
|
||||
assert(ACFG);
|
||||
auto Result = FunctionContexts.insert({F, std::move(*ACFG)});
|
||||
return &Result.first->second;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/DebugSupport.h"
|
||||
#include "clang/Analysis/FlowSensitive/Logger.h"
|
||||
#include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
|
||||
@@ -162,7 +162,7 @@ class HTMLLogger : public Logger {
|
||||
llvm::raw_string_ostream JStringStream{JSON};
|
||||
llvm::json::OStream JOS{JStringStream, /*Indent=*/2};
|
||||
|
||||
const ControlFlowContext *CFC;
|
||||
const AdornedCFG *ACFG;
|
||||
// Timeline of iterations of CFG block visitation.
|
||||
std::vector<Iteration> Iters;
|
||||
// Indexes in `Iters` of the iterations for each block.
|
||||
@@ -176,15 +176,15 @@ class HTMLLogger : public Logger {
|
||||
|
||||
public:
|
||||
explicit HTMLLogger(StreamFactory Streams) : Streams(std::move(Streams)) {}
|
||||
void beginAnalysis(const ControlFlowContext &CFC,
|
||||
void beginAnalysis(const AdornedCFG &ACFG,
|
||||
TypeErasedDataflowAnalysis &A) override {
|
||||
OS = Streams();
|
||||
this->CFC = &CFC;
|
||||
this->ACFG = &ACFG;
|
||||
*OS << llvm::StringRef(HTMLLogger_html).split("<?INJECT?>").first;
|
||||
|
||||
BlockConverged.resize(CFC.getCFG().getNumBlockIDs());
|
||||
BlockConverged.resize(ACFG.getCFG().getNumBlockIDs());
|
||||
|
||||
const auto &D = CFC.getDecl();
|
||||
const auto &D = ACFG.getDecl();
|
||||
const auto &SM = A.getASTContext().getSourceManager();
|
||||
*OS << "<title>";
|
||||
if (const auto *ND = dyn_cast<NamedDecl>(&D))
|
||||
@@ -345,7 +345,7 @@ private:
|
||||
// tokens are associated with, and even which BB element (so that clicking
|
||||
// can select the right element).
|
||||
void writeCode() {
|
||||
const auto &AST = CFC->getDecl().getASTContext();
|
||||
const auto &AST = ACFG->getDecl().getASTContext();
|
||||
bool Invalid = false;
|
||||
|
||||
// Extract the source code from the original file.
|
||||
@@ -353,7 +353,7 @@ private:
|
||||
// indentation to worry about), but we need the boundaries of particular
|
||||
// AST nodes and the printer doesn't provide this.
|
||||
auto Range = clang::Lexer::makeFileCharRange(
|
||||
CharSourceRange::getTokenRange(CFC->getDecl().getSourceRange()),
|
||||
CharSourceRange::getTokenRange(ACFG->getDecl().getSourceRange()),
|
||||
AST.getSourceManager(), AST.getLangOpts());
|
||||
if (Range.isInvalid())
|
||||
return;
|
||||
@@ -419,7 +419,7 @@ private:
|
||||
// Construct one TokenInfo per character in a flat array.
|
||||
// This is inefficient (chars in a token all have the same info) but simple.
|
||||
std::vector<TokenInfo> State(Code.size());
|
||||
for (const auto *Block : CFC->getCFG()) {
|
||||
for (const auto *Block : ACFG->getCFG()) {
|
||||
unsigned EltIndex = 0;
|
||||
for (const auto& Elt : *Block) {
|
||||
++EltIndex;
|
||||
@@ -480,7 +480,7 @@ private:
|
||||
// out to `dot` to turn it into an SVG.
|
||||
void writeCFG() {
|
||||
*OS << "<template data-copy='cfg'>\n";
|
||||
if (auto SVG = renderSVG(buildCFGDot(CFC->getCFG())))
|
||||
if (auto SVG = renderSVG(buildCFGDot(ACFG->getCFG())))
|
||||
*OS << *SVG;
|
||||
else
|
||||
*OS << "Can't draw CFG: " << toString(SVG.takeError());
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Analysis/FlowSensitive/Logger.h"
|
||||
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
|
||||
#include "llvm/Support/WithColor.h"
|
||||
|
||||
@@ -33,17 +33,17 @@ struct TextualLogger final : Logger {
|
||||
TextualLogger(llvm::raw_ostream &OS)
|
||||
: OS(OS), ShowColors(llvm::WithColor::defaultAutoDetectFunction()(OS)) {}
|
||||
|
||||
virtual void beginAnalysis(const ControlFlowContext &CFG,
|
||||
virtual void beginAnalysis(const AdornedCFG &ACFG,
|
||||
TypeErasedDataflowAnalysis &Analysis) override {
|
||||
{
|
||||
llvm::WithColor Header(OS, llvm::raw_ostream::Colors::RED, /*Bold=*/true);
|
||||
OS << "=== Beginning data flow analysis ===\n";
|
||||
}
|
||||
auto &D = CFG.getDecl();
|
||||
auto &D = ACFG.getDecl();
|
||||
D.print(OS);
|
||||
OS << "\n";
|
||||
D.dump(OS);
|
||||
CurrentCFG = &CFG.getCFG();
|
||||
CurrentCFG = &ACFG.getCFG();
|
||||
CurrentCFG->print(OS, Analysis.getASTContext().getLangOpts(), ShowColors);
|
||||
CurrentAnalysis = &Analysis;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "clang/AST/OperationKinds.h"
|
||||
#include "clang/AST/Stmt.h"
|
||||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
|
||||
#include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
|
||||
#include "clang/Analysis/FlowSensitive/RecordOps.h"
|
||||
@@ -38,9 +38,9 @@ namespace clang {
|
||||
namespace dataflow {
|
||||
|
||||
const Environment *StmtToEnvMap::getEnvironment(const Stmt &S) const {
|
||||
auto BlockIt = CFCtx.getStmtToBlock().find(&ignoreCFGOmittedNodes(S));
|
||||
assert(BlockIt != CFCtx.getStmtToBlock().end());
|
||||
if (!CFCtx.isBlockReachable(*BlockIt->getSecond()))
|
||||
auto BlockIt = ACFG.getStmtToBlock().find(&ignoreCFGOmittedNodes(S));
|
||||
assert(BlockIt != ACFG.getStmtToBlock().end());
|
||||
if (!ACFG.isBlockReachable(*BlockIt->getSecond()))
|
||||
return nullptr;
|
||||
if (BlockIt->getSecond()->getBlockID() == CurBlockID)
|
||||
return &CurState.Env;
|
||||
@@ -855,27 +855,26 @@ private:
|
||||
Env.canDescend(Options.ContextSensitiveOpts->Depth, F)))
|
||||
return;
|
||||
|
||||
const ControlFlowContext *CFCtx =
|
||||
Env.getDataflowAnalysisContext().getControlFlowContext(F);
|
||||
if (!CFCtx)
|
||||
const AdornedCFG *ACFG = Env.getDataflowAnalysisContext().getAdornedCFG(F);
|
||||
if (!ACFG)
|
||||
return;
|
||||
|
||||
// FIXME: We don't support context-sensitive analysis of recursion, so
|
||||
// we should return early here if `F` is the same as the `FunctionDecl`
|
||||
// holding `S` itself.
|
||||
|
||||
auto ExitBlock = CFCtx->getCFG().getExit().getBlockID();
|
||||
auto ExitBlock = ACFG->getCFG().getExit().getBlockID();
|
||||
|
||||
auto CalleeEnv = Env.pushCall(S);
|
||||
|
||||
// FIXME: Use the same analysis as the caller for the callee. Note,
|
||||
// though, that doing so would require support for changing the analysis's
|
||||
// ASTContext.
|
||||
auto Analysis = NoopAnalysis(CFCtx->getDecl().getASTContext(),
|
||||
auto Analysis = NoopAnalysis(ACFG->getDecl().getASTContext(),
|
||||
DataflowAnalysisOptions{Options});
|
||||
|
||||
auto BlockToOutputState =
|
||||
dataflow::runDataflowAnalysis(*CFCtx, Analysis, CalleeEnv);
|
||||
dataflow::runDataflowAnalysis(*ACFG, Analysis, CalleeEnv);
|
||||
assert(BlockToOutputState);
|
||||
assert(ExitBlock < BlockToOutputState->size());
|
||||
|
||||
|
||||
@@ -150,20 +150,19 @@ private:
|
||||
|
||||
/// Holds data structures required for running dataflow analysis.
|
||||
struct AnalysisContext {
|
||||
AnalysisContext(const ControlFlowContext &CFCtx,
|
||||
TypeErasedDataflowAnalysis &Analysis,
|
||||
AnalysisContext(const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
|
||||
const Environment &InitEnv,
|
||||
llvm::ArrayRef<std::optional<TypeErasedDataflowAnalysisState>>
|
||||
BlockStates)
|
||||
: CFCtx(CFCtx), Analysis(Analysis), InitEnv(InitEnv),
|
||||
: ACFG(ACFG), Analysis(Analysis), InitEnv(InitEnv),
|
||||
Log(*InitEnv.getDataflowAnalysisContext().getOptions().Log),
|
||||
BlockStates(BlockStates) {
|
||||
Log.beginAnalysis(CFCtx, Analysis);
|
||||
Log.beginAnalysis(ACFG, Analysis);
|
||||
}
|
||||
~AnalysisContext() { Log.endAnalysis(); }
|
||||
|
||||
/// Contains the CFG being analyzed.
|
||||
const ControlFlowContext &CFCtx;
|
||||
const AdornedCFG &ACFG;
|
||||
/// The analysis to be run.
|
||||
TypeErasedDataflowAnalysis &Analysis;
|
||||
/// Initial state to start the analysis.
|
||||
@@ -176,19 +175,19 @@ struct AnalysisContext {
|
||||
|
||||
class PrettyStackTraceAnalysis : public llvm::PrettyStackTraceEntry {
|
||||
public:
|
||||
PrettyStackTraceAnalysis(const ControlFlowContext &CFCtx, const char *Message)
|
||||
: CFCtx(CFCtx), Message(Message) {}
|
||||
PrettyStackTraceAnalysis(const AdornedCFG &ACFG, const char *Message)
|
||||
: ACFG(ACFG), Message(Message) {}
|
||||
|
||||
void print(raw_ostream &OS) const override {
|
||||
OS << Message << "\n";
|
||||
OS << "Decl:\n";
|
||||
CFCtx.getDecl().dump(OS);
|
||||
ACFG.getDecl().dump(OS);
|
||||
OS << "CFG:\n";
|
||||
CFCtx.getCFG().print(OS, LangOptions(), false);
|
||||
ACFG.getCFG().print(OS, LangOptions(), false);
|
||||
}
|
||||
|
||||
private:
|
||||
const ControlFlowContext &CFCtx;
|
||||
const AdornedCFG &ACFG;
|
||||
const char *Message;
|
||||
};
|
||||
|
||||
@@ -303,7 +302,7 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
|
||||
// See `NoreturnDestructorTest` for concrete examples.
|
||||
if (Block.succ_begin()->getReachableBlock() != nullptr &&
|
||||
Block.succ_begin()->getReachableBlock()->hasNoReturnElement()) {
|
||||
auto &StmtToBlock = AC.CFCtx.getStmtToBlock();
|
||||
auto &StmtToBlock = AC.ACFG.getStmtToBlock();
|
||||
auto StmtBlock = StmtToBlock.find(Block.getTerminatorStmt());
|
||||
assert(StmtBlock != StmtToBlock.end());
|
||||
llvm::erase(Preds, StmtBlock->getSecond());
|
||||
@@ -319,7 +318,7 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) {
|
||||
// all predecessors have expression state consumed in a different block.
|
||||
Environment::ExprJoinBehavior JoinBehavior = Environment::DiscardExprState;
|
||||
for (const CFGBlock *Pred : Preds) {
|
||||
if (Pred && AC.CFCtx.containsExprConsumedInDifferentBlock(*Pred)) {
|
||||
if (Pred && AC.ACFG.containsExprConsumedInDifferentBlock(*Pred)) {
|
||||
JoinBehavior = Environment::KeepExprState;
|
||||
break;
|
||||
}
|
||||
@@ -368,7 +367,7 @@ builtinTransferStatement(unsigned CurBlockID, const CFGStmt &Elt,
|
||||
AnalysisContext &AC) {
|
||||
const Stmt *S = Elt.getStmt();
|
||||
assert(S != nullptr);
|
||||
transfer(StmtToEnvMap(AC.CFCtx, AC.BlockStates, CurBlockID, InputState), *S,
|
||||
transfer(StmtToEnvMap(AC.ACFG, AC.BlockStates, CurBlockID, InputState), *S,
|
||||
InputState.Env);
|
||||
}
|
||||
|
||||
@@ -511,9 +510,8 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC,
|
||||
// takes a `CFGElement` as input, but some expressions only show up as a
|
||||
// terminator condition, but not as a `CFGElement`. The condition of an if
|
||||
// statement is one such example.
|
||||
transfer(
|
||||
StmtToEnvMap(AC.CFCtx, AC.BlockStates, Block.getBlockID(), State),
|
||||
*TerminatorCond, State.Env);
|
||||
transfer(StmtToEnvMap(AC.ACFG, AC.BlockStates, Block.getBlockID(), State),
|
||||
*TerminatorCond, State.Env);
|
||||
|
||||
// If the transfer function didn't produce a value, create an atom so that
|
||||
// we have *some* value for the condition expression. This ensures that
|
||||
@@ -528,13 +526,13 @@ transferCFGBlock(const CFGBlock &Block, AnalysisContext &AC,
|
||||
|
||||
llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>>
|
||||
runTypeErasedDataflowAnalysis(
|
||||
const ControlFlowContext &CFCtx, TypeErasedDataflowAnalysis &Analysis,
|
||||
const AdornedCFG &ACFG, TypeErasedDataflowAnalysis &Analysis,
|
||||
const Environment &InitEnv,
|
||||
std::function<void(const CFGElement &,
|
||||
const TypeErasedDataflowAnalysisState &)>
|
||||
PostVisitCFG,
|
||||
std::int32_t MaxBlockVisits) {
|
||||
PrettyStackTraceAnalysis CrashInfo(CFCtx, "runTypeErasedDataflowAnalysis");
|
||||
PrettyStackTraceAnalysis CrashInfo(ACFG, "runTypeErasedDataflowAnalysis");
|
||||
|
||||
std::optional<Environment> MaybeStartingEnv;
|
||||
if (InitEnv.callStackSize() == 1) {
|
||||
@@ -544,7 +542,7 @@ runTypeErasedDataflowAnalysis(
|
||||
const Environment &StartingEnv =
|
||||
MaybeStartingEnv ? *MaybeStartingEnv : InitEnv;
|
||||
|
||||
const clang::CFG &CFG = CFCtx.getCFG();
|
||||
const clang::CFG &CFG = ACFG.getCFG();
|
||||
PostOrderCFGView POV(&CFG);
|
||||
ForwardDataflowWorklist Worklist(CFG, &POV);
|
||||
|
||||
@@ -557,7 +555,7 @@ runTypeErasedDataflowAnalysis(
|
||||
StartingEnv.fork()};
|
||||
Worklist.enqueueSuccessors(&Entry);
|
||||
|
||||
AnalysisContext AC(CFCtx, Analysis, StartingEnv, BlockStates);
|
||||
AnalysisContext AC(ACFG, Analysis, StartingEnv, BlockStates);
|
||||
std::int32_t BlockVisits = 0;
|
||||
while (const CFGBlock *Block = Worklist.dequeue()) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
@@ -615,7 +613,7 @@ runTypeErasedDataflowAnalysis(
|
||||
// state set to `std::nullopt` at this point) to also analyze dead code.
|
||||
|
||||
if (PostVisitCFG) {
|
||||
for (const CFGBlock *Block : CFCtx.getCFG()) {
|
||||
for (const CFGBlock *Block : ACFG.getCFG()) {
|
||||
// Skip blocks that were not evaluated.
|
||||
if (!BlockStates[Block->getBlockID()])
|
||||
continue;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "TestingSupport.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
|
||||
@@ -34,14 +34,14 @@ std::string analyzeAndPrintExitCondition(llvm::StringRef Code) {
|
||||
const auto *Target =
|
||||
cast<FunctionDecl>(test::findValueDecl(AST.context(), "target"));
|
||||
Environment InitEnv(DACtx, *Target);
|
||||
auto CFCtx = cantFail(ControlFlowContext::build(*Target));
|
||||
auto ACFG = cantFail(AdornedCFG::build(*Target));
|
||||
|
||||
NoopAnalysis Analysis(AST.context(), DataflowAnalysisOptions{});
|
||||
|
||||
auto Result = runDataflowAnalysis(CFCtx, Analysis, InitEnv);
|
||||
auto Result = runDataflowAnalysis(ACFG, Analysis, InitEnv);
|
||||
EXPECT_FALSE(!Result) << Result.takeError();
|
||||
|
||||
Atom FinalFC = (*Result)[CFCtx.getCFG().getExit().getBlockID()]
|
||||
Atom FinalFC = (*Result)[ACFG.getCFG().getExit().getBlockID()]
|
||||
->Env.getFlowConditionToken();
|
||||
std::string Textual;
|
||||
llvm::raw_string_ostream OS(Textual);
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
private:
|
||||
llvm::raw_string_ostream OS;
|
||||
|
||||
void beginAnalysis(const ControlFlowContext &,
|
||||
void beginAnalysis(const AdornedCFG &,
|
||||
TypeErasedDataflowAnalysis &) override {
|
||||
logText("beginAnalysis()");
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/ASTMatchers/ASTMatchersInternal.h"
|
||||
#include "clang/Analysis/CFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/AdornedCFG.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
|
||||
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
|
||||
@@ -100,7 +100,7 @@ struct AnalysisOutputs {
|
||||
const FunctionDecl *Target;
|
||||
/// Contains the control flow graph built from the body of the `Target`
|
||||
/// function and is analyzed.
|
||||
const ControlFlowContext &CFCtx;
|
||||
const AdornedCFG &ACFG;
|
||||
/// The analysis to be run.
|
||||
TypeErasedDataflowAnalysis &Analysis;
|
||||
/// Initial state to start the analysis.
|
||||
@@ -261,9 +261,10 @@ checkDataflow(AnalysisInputs<AnalysisT> AI,
|
||||
llvm::errc::invalid_argument, "Could not find the target function.");
|
||||
|
||||
// Build the control flow graph for the target function.
|
||||
auto MaybeCFCtx = ControlFlowContext::build(*Target);
|
||||
if (!MaybeCFCtx) return MaybeCFCtx.takeError();
|
||||
auto &CFCtx = *MaybeCFCtx;
|
||||
auto MaybeACFG = AdornedCFG::build(*Target);
|
||||
if (!MaybeACFG)
|
||||
return MaybeACFG.takeError();
|
||||
auto &ACFG = *MaybeACFG;
|
||||
|
||||
// Initialize states for running dataflow analysis.
|
||||
DataflowAnalysisContext DACtx(AI.SolverFactory(),
|
||||
@@ -271,7 +272,7 @@ checkDataflow(AnalysisInputs<AnalysisT> AI,
|
||||
Environment InitEnv(DACtx, *Target);
|
||||
auto Analysis = AI.MakeAnalysis(Context, InitEnv);
|
||||
|
||||
AnalysisOutputs AO{AnnotatedCode, Context, Target, CFCtx,
|
||||
AnalysisOutputs AO{AnnotatedCode, Context, Target, ACFG,
|
||||
Analysis, InitEnv, {}};
|
||||
|
||||
// Additional test setup.
|
||||
@@ -283,7 +284,7 @@ checkDataflow(AnalysisInputs<AnalysisT> AI,
|
||||
// the post-analysis states for the CFG blocks that have been evaluated.
|
||||
llvm::Expected<std::vector<std::optional<TypeErasedDataflowAnalysisState>>>
|
||||
MaybeBlockStates =
|
||||
runTypeErasedDataflowAnalysis(CFCtx, Analysis, InitEnv,
|
||||
runTypeErasedDataflowAnalysis(ACFG, Analysis, InitEnv,
|
||||
TypeErasedPostVisitCFG,
|
||||
MaxBlockVisitsInAnalysis);
|
||||
if (!MaybeBlockStates) return MaybeBlockStates.takeError();
|
||||
|
||||
@@ -66,20 +66,20 @@ protected:
|
||||
AST->getASTContext()));
|
||||
assert(Func != nullptr);
|
||||
|
||||
CFCtx = std::make_unique<ControlFlowContext>(
|
||||
llvm::cantFail(ControlFlowContext::build(*Func)));
|
||||
ACFG =
|
||||
std::make_unique<AdornedCFG>(llvm::cantFail(AdornedCFG::build(*Func)));
|
||||
|
||||
AnalysisT Analysis = MakeAnalysis(AST->getASTContext());
|
||||
DACtx = std::make_unique<DataflowAnalysisContext>(
|
||||
std::make_unique<WatchedLiteralsSolver>());
|
||||
Environment Env(*DACtx, *Func);
|
||||
|
||||
return runDataflowAnalysis(*CFCtx, Analysis, Env);
|
||||
return runDataflowAnalysis(*ACFG, Analysis, Env);
|
||||
}
|
||||
|
||||
/// Returns the `CFGBlock` containing `S` (and asserts that it exists).
|
||||
const CFGBlock *blockForStmt(const Stmt &S) {
|
||||
const CFGBlock *Block = CFCtx->getStmtToBlock().lookup(&S);
|
||||
const CFGBlock *Block = ACFG->getStmtToBlock().lookup(&S);
|
||||
assert(Block != nullptr);
|
||||
return Block;
|
||||
}
|
||||
@@ -105,7 +105,7 @@ protected:
|
||||
}
|
||||
|
||||
std::unique_ptr<ASTUnit> AST;
|
||||
std::unique_ptr<ControlFlowContext> CFCtx;
|
||||
std::unique_ptr<AdornedCFG> ACFG;
|
||||
std::unique_ptr<DataflowAnalysisContext> DACtx;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user