#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprObjC.h"
#include "clang/AST/ParentMap.h"
#include "clang/AST/Stmt.h"
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/Analysis/Support/BumpVector.h"
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include <cassert>
#include <memory>
using namespace clang;
using namespace ento;
ExplodedGraph::ExplodedGraph() = default;
ExplodedGraph::~ExplodedGraph() = default;
bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) {
if (!Ex->isLValue())
return false;
return isa<DeclRefExpr, MemberExpr, ObjCIvarRefExpr, ArraySubscriptExpr>(Ex);
}
bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
if (node->pred_size() != 1 || node->succ_size() != 1)
return false;
const ExplodedNode *pred = *(node->pred_begin());
if (pred->succ_size() != 1)
return false;
const ExplodedNode *succ = *(node->succ_begin());
if (succ->pred_size() != 1)
return false;
ProgramPoint progPoint = node->getLocation();
if (progPoint.getAs<PreStmtPurgeDeadSymbols>())
return !progPoint.getTag();
if (!progPoint.getAs<PostStmt>() || progPoint.getAs<PostStore>())
return false;
if (progPoint.getTag())
return false;
ProgramStateRef state = node->getState();
ProgramStateRef pred_state = pred->getState();
if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
progPoint.getLocationContext() != pred->getLocationContext())
return false;
const Expr *Ex = dyn_cast<Expr>(progPoint.castAs<PostStmt>().getStmt());
if (!Ex)
return false;
if (isInterestingLValueExpr(Ex))
return false;
const ParentMap &PM = progPoint.getLocationContext()->getParentMap();
if (!PM.isConsumedExpr(Ex))
return false;
const ProgramPoint SuccLoc = succ->getLocation();
if (Optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>())
if (CallEvent::isCallStmt(SP->getStmt()))
return false;
if (SuccLoc.getAs<CallEnter>() || SuccLoc.getAs<PreImplicitCall>())
return false;
return true;
}
void ExplodedGraph::collectNode(ExplodedNode *node) {
assert(node->pred_size() == 1 || node->succ_size() == 1);
ExplodedNode *pred = *(node->pred_begin());
ExplodedNode *succ = *(node->succ_begin());
pred->replaceSuccessor(succ);
succ->replacePredecessor(pred);
FreeNodes.push_back(node);
Nodes.RemoveNode(node);
--NumNodes;
node->~ExplodedNode();
}
void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
if (ChangedNodes.empty())
return;
assert(ReclaimCounter > 0);
if (--ReclaimCounter != 0)
return;
ReclaimCounter = ReclaimNodeInterval;
for (const auto node : ChangedNodes)
if (shouldCollect(node))
collectNode(node);
ChangedNodes.clear();
}
using ExplodedNodeVector = BumpVector<ExplodedNode *>;
using GroupStorage = llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *>;
void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) {
assert(!V->isSink());
Preds.addNode(V, G);
V->Succs.addNode(this, G);
}
void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) {
assert(!getFlag());
GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
assert(Storage.is<ExplodedNode *>());
Storage = node;
assert(Storage.is<ExplodedNode *>());
}
void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) {
assert(!getFlag());
GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
if (Storage.isNull()) {
Storage = N;
assert(Storage.is<ExplodedNode *>());
return;
}
ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>();
if (!V) {
ExplodedNode *Old = Storage.get<ExplodedNode *>();
BumpVectorContext &Ctx = G.getNodeAllocator();
V = G.getAllocator().Allocate<ExplodedNodeVector>();
new (V) ExplodedNodeVector(Ctx, 4);
V->push_back(Old, Ctx);
Storage = V;
assert(!getFlag());
assert(Storage.is<ExplodedNodeVector *>());
}
V->push_back(N, G.getNodeAllocator());
}
unsigned ExplodedNode::NodeGroup::size() const {
if (getFlag())
return 0;
const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
if (Storage.isNull())
return 0;
if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
return V->size();
return 1;
}
ExplodedNode * const *ExplodedNode::NodeGroup::begin() const {
if (getFlag())
return nullptr;
const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
if (Storage.isNull())
return nullptr;
if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
return V->begin();
return Storage.getAddrOfPtr1();
}
ExplodedNode * const *ExplodedNode::NodeGroup::end() const {
if (getFlag())
return nullptr;
const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
if (Storage.isNull())
return nullptr;
if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
return V->end();
return Storage.getAddrOfPtr1() + 1;
}
bool ExplodedNode::isTrivial() const {
return pred_size() == 1 && succ_size() == 1 &&
getFirstPred()->getState()->getID() == getState()->getID() &&
getFirstPred()->succ_size() == 1;
}
const CFGBlock *ExplodedNode::getCFGBlock() const {
ProgramPoint P = getLocation();
if (auto BEP = P.getAs<BlockEntrance>())
return BEP->getBlock();
if (const Stmt *S = getStmtForDiagnostics())
return getLocationContext()
->getAnalysisDeclContext()
->getCFGStmtMap()
->getBlock(S);
return nullptr;
}
static const LocationContext *
findTopAutosynthesizedParentContext(const LocationContext *LC) {
assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized());
const LocationContext *ParentLC = LC->getParent();
assert(ParentLC && "We don't start analysis from autosynthesized code");
while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
LC = ParentLC;
ParentLC = LC->getParent();
assert(ParentLC && "We don't start analysis from autosynthesized code");
}
return LC;
}
const Stmt *ExplodedNode::getStmtForDiagnostics() const {
const LocationContext *LC = getLocationContext();
if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC))
->getCallSite();
}
ProgramPoint P = getLocation();
if (auto SP = P.getAs<StmtPoint>())
return SP->getStmt();
if (auto BE = P.getAs<BlockEdge>())
return BE->getSrc()->getTerminatorStmt();
if (auto CE = P.getAs<CallEnter>())
return CE->getCallExpr();
if (auto CEE = P.getAs<CallExitEnd>())
return CEE->getCalleeContext()->getCallSite();
if (auto PIPP = P.getAs<PostInitializer>())
return PIPP->getInitializer()->getInit();
if (auto CEB = P.getAs<CallExitBegin>())
return CEB->getReturnStmt();
if (auto FEP = P.getAs<FunctionExitPoint>())
return FEP->getStmt();
return nullptr;
}
const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {
for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) {
if (const Stmt *S = N->getStmtForDiagnostics()) {
switch (S->getStmtClass()) {
case Stmt::ChooseExprClass:
case Stmt::BinaryConditionalOperatorClass:
case Stmt::ConditionalOperatorClass:
continue;
case Stmt::BinaryOperatorClass: {
BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
if (Op == BO_LAnd || Op == BO_LOr)
continue;
break;
}
default:
break;
}
return S;
}
}
return nullptr;
}
const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const {
for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred())
if (const Stmt *S = N->getStmtForDiagnostics())
return S;
return nullptr;
}
const Stmt *ExplodedNode::getCurrentOrPreviousStmtForDiagnostics() const {
if (const Stmt *S = getStmtForDiagnostics())
return S;
return getPreviousStmtForDiagnostics();
}
ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
ProgramStateRef State,
bool IsSink,
bool* IsNew) {
llvm::FoldingSetNodeID profile;
void *InsertPos = nullptr;
NodeTy::Profile(profile, L, State, IsSink);
NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos);
if (!V) {
if (!FreeNodes.empty()) {
V = FreeNodes.back();
FreeNodes.pop_back();
}
else {
V = (NodeTy*) getAllocator().Allocate<NodeTy>();
}
++NumNodes;
new (V) NodeTy(L, State, NumNodes, IsSink);
if (ReclaimNodeInterval)
ChangedNodes.push_back(V);
Nodes.InsertNode(V, InsertPos);
if (IsNew) *IsNew = true;
}
else
if (IsNew) *IsNew = false;
return V;
}
ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L,
ProgramStateRef State,
int64_t Id,
bool IsSink) {
NodeTy *V = (NodeTy *) getAllocator().Allocate<NodeTy>();
new (V) NodeTy(L, State, Id, IsSink);
return V;
}
std::unique_ptr<ExplodedGraph>
ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
InterExplodedGraphMap *ForwardMap,
InterExplodedGraphMap *InverseMap) const {
if (Nodes.empty())
return nullptr;
using Pass1Ty = llvm::DenseSet<const ExplodedNode *>;
Pass1Ty Pass1;
using Pass2Ty = InterExplodedGraphMap;
InterExplodedGraphMap Pass2Scratch;
Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch;
SmallVector<const ExplodedNode*, 10> WL1, WL2;
for (const auto Sink : Sinks)
if (Sink)
WL1.push_back(Sink);
while (!WL1.empty()) {
const ExplodedNode *N = WL1.pop_back_val();
if (!Pass1.insert(N).second)
continue;
if (N->Preds.empty()) {
WL2.push_back(N);
continue;
}
WL1.append(N->Preds.begin(), N->Preds.end());
}
if (WL2.empty())
return nullptr;
std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph();
while (!WL2.empty()) {
const ExplodedNode *N = WL2.pop_back_val();
if (Pass2.find(N) != Pass2.end())
continue;
ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State,
N->getID(), N->isSink());
Pass2[N] = NewN;
if (InverseMap) (*InverseMap)[NewN] = N;
if (N->Preds.empty())
G->addRoot(NewN);
for (ExplodedNode::pred_iterator I = N->Preds.begin(), E = N->Preds.end();
I != E; ++I) {
Pass2Ty::iterator PI = Pass2.find(*I);
if (PI == Pass2.end())
continue;
NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G);
}
for (ExplodedNode::succ_iterator I = N->Succs.begin(), E = N->Succs.end();
I != E; ++I) {
Pass2Ty::iterator PI = Pass2.find(*I);
if (PI != Pass2.end()) {
const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G);
continue;
}
if (Pass1.count(*I))
WL2.push_back(*I);
}
}
return G;
}