forked from OSchip/llvm-project
[CFG] NFC: Refactor ConstructionContext into a finite set of cases.
ConstructionContext is moved into a separate translation unit and is separated into multiple classes. The "old" "raw" ConstructionContext is renamed into ConstructionContextLayer - which corresponds to the idea of building the context gradually layer-by-layer, but it isn't easy to use in the clients. Once CXXConstructExpr is reached, layers that we've gathered so far are transformed into the actual, "new-style" "flat" ConstructionContext, which is put into the CFGConstructor element and has no layers whatsoever (until it actually needs them, eg. aggregate initialization). The new-style ConstructionContext is instead presented as a variety of sub-classes that enumerate different ways of constructing an object in C++. There are 5 of these supported for now, which is around a half of what needs to be supported. The layer-by-layer buildup process is still a little bit weird, but it hides all the weirdness in one place, that sounds like a good thing. Differential Revision: https://reviews.llvm.org/D43533 llvm-svn: 326238
This commit is contained in:
parent
301991080e
commit
4068481bdb
|
@ -38,6 +38,7 @@ namespace clang {
|
|||
class ASTContext;
|
||||
class BinaryOperator;
|
||||
class CFG;
|
||||
class ConstructionContext;
|
||||
class CXXBaseSpecifier;
|
||||
class CXXBindTemporaryExpr;
|
||||
class CXXCtorInitializer;
|
||||
|
@ -140,94 +141,6 @@ protected:
|
|||
CFGStmt() = default;
|
||||
};
|
||||
|
||||
// This is bulky data for CFGConstructor which would not fit into the
|
||||
// CFGElement's room (pair of pointers). Contains the information
|
||||
// necessary to express what memory is being initialized by
|
||||
// the construction.
|
||||
class ConstructionContext {
|
||||
public:
|
||||
typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
|
||||
|
||||
private:
|
||||
// The construction site - the statement that triggered the construction
|
||||
// for one of its parts. For instance, stack variable declaration statement
|
||||
// triggers construction of itself or its elements if it's an array,
|
||||
// new-expression triggers construction of the newly allocated object(s).
|
||||
TriggerTy Trigger;
|
||||
|
||||
// Sometimes a single trigger is not enough to describe the construction site.
|
||||
// In this case we'd have a chain of "partial" construction contexts.
|
||||
// Some examples:
|
||||
// - A constructor within in an aggregate initializer list within a variable
|
||||
// would have a construction context of the initializer list with the parent
|
||||
// construction context of a variable.
|
||||
// - A constructor for a temporary that needs to be both destroyed
|
||||
// and materialized into an elidable copy constructor would have a
|
||||
// construction context of a CXXBindTemporaryExpr with the parent
|
||||
// construction context of a MaterializeTemproraryExpr.
|
||||
// Not all of these are currently supported.
|
||||
const ConstructionContext *Parent = nullptr;
|
||||
|
||||
ConstructionContext() = default;
|
||||
ConstructionContext(TriggerTy Trigger, const ConstructionContext *Parent)
|
||||
: Trigger(Trigger), Parent(Parent) {}
|
||||
|
||||
public:
|
||||
static const ConstructionContext *
|
||||
create(BumpVectorContext &C, TriggerTy Trigger,
|
||||
const ConstructionContext *Parent = nullptr) {
|
||||
ConstructionContext *CC = C.getAllocator().Allocate<ConstructionContext>();
|
||||
return new (CC) ConstructionContext(Trigger, Parent);
|
||||
}
|
||||
|
||||
bool isNull() const { return Trigger.isNull(); }
|
||||
|
||||
TriggerTy getTrigger() const { return Trigger; }
|
||||
const ConstructionContext *getParent() const { return Parent; }
|
||||
|
||||
const Stmt *getTriggerStmt() const {
|
||||
return Trigger.dyn_cast<Stmt *>();
|
||||
}
|
||||
|
||||
const CXXCtorInitializer *getTriggerInit() const {
|
||||
return Trigger.dyn_cast<CXXCtorInitializer *>();
|
||||
}
|
||||
|
||||
const MaterializeTemporaryExpr *getMaterializedTemporary() const {
|
||||
// TODO: Be more careful to ensure that there's only one MTE around.
|
||||
for (const ConstructionContext *CC = this; CC; CC = CC->getParent()) {
|
||||
if (const auto *MTE = dyn_cast_or_null<MaterializeTemporaryExpr>(
|
||||
CC->getTriggerStmt())) {
|
||||
return MTE;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isSameAsPartialContext(const ConstructionContext *Other) const {
|
||||
assert(Other);
|
||||
return (Trigger == Other->Trigger);
|
||||
}
|
||||
|
||||
// See if Other is a proper initial segment of this construction context
|
||||
// in terms of the parent chain - i.e. a few first parents coincide and
|
||||
// then the other context terminates but our context goes further - i.e.,
|
||||
// we are providing the same context that the other context provides,
|
||||
// and a bit more above that.
|
||||
bool isStrictlyMoreSpecificThan(const ConstructionContext *Other) const {
|
||||
const ConstructionContext *Self = this;
|
||||
while (true) {
|
||||
if (!Other)
|
||||
return Self;
|
||||
if (!Self || !Self->isSameAsPartialContext(Other))
|
||||
return false;
|
||||
Self = Self->getParent();
|
||||
Other = Other->getParent();
|
||||
}
|
||||
llvm_unreachable("The above loop can only be terminated via return!");
|
||||
}
|
||||
};
|
||||
|
||||
/// CFGConstructor - Represents C++ constructor call. Maintains information
|
||||
/// necessary to figure out what memory is being initialized by the
|
||||
/// constructor expression. For now this is only used by the analyzer's CFG.
|
||||
|
@ -235,7 +148,7 @@ class CFGConstructor : public CFGStmt {
|
|||
public:
|
||||
explicit CFGConstructor(CXXConstructExpr *CE, const ConstructionContext *C)
|
||||
: CFGStmt(CE, Constructor) {
|
||||
assert(!C->isNull());
|
||||
assert(C);
|
||||
Data2.setPointer(const_cast<ConstructionContext *>(C));
|
||||
}
|
||||
|
||||
|
@ -247,22 +160,6 @@ public:
|
|||
return cast<CXXConstructExpr>(getStmt())->getType();
|
||||
}
|
||||
|
||||
ConstructionContext::TriggerTy getTrigger() const {
|
||||
return getConstructionContext()->getTrigger();
|
||||
}
|
||||
|
||||
const Stmt *getTriggerStmt() const {
|
||||
return getConstructionContext()->getTriggerStmt();
|
||||
}
|
||||
|
||||
const CXXCtorInitializer *getTriggerInit() const {
|
||||
return getConstructionContext()->getTriggerInit();
|
||||
}
|
||||
|
||||
const MaterializeTemporaryExpr *getMaterializedTemporary() const {
|
||||
return getConstructionContext()->getMaterializedTemporary();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class CFGElement;
|
||||
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
//===- ConstructionContext.h - CFG constructor information ------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the ConstructionContext class and its sub-classes,
|
||||
// which represent various different ways of constructing C++ objects
|
||||
// with the additional information the users may want to know about
|
||||
// the constructor.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
|
||||
#define LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
|
||||
|
||||
#include "clang/Analysis/Support/BumpVector.h"
|
||||
#include "clang/AST/ExprCXX.h"
|
||||
|
||||
namespace clang {
|
||||
|
||||
/// Construction context is a linked list of multiple layers. Layers are
|
||||
/// created gradually while traversing the AST, and layers that represent
|
||||
/// the outmost AST nodes are built first, while the node that immediately
|
||||
/// contains the constructor would be built last and capture the previous
|
||||
/// layers as its parents. Construction context captures the last layer
|
||||
/// (which has links to the previous layers) and classifies the seemingly
|
||||
/// arbitrary chain of layers into one of the possible ways of constructing
|
||||
/// an object in C++ for user-friendly experience.
|
||||
class ConstructionContextLayer {
|
||||
public:
|
||||
typedef llvm::PointerUnion<Stmt *, CXXCtorInitializer *> TriggerTy;
|
||||
|
||||
private:
|
||||
/// The construction site - the statement that triggered the construction
|
||||
/// for one of its parts. For instance, stack variable declaration statement
|
||||
/// triggers construction of itself or its elements if it's an array,
|
||||
/// new-expression triggers construction of the newly allocated object(s).
|
||||
TriggerTy Trigger;
|
||||
|
||||
/// Sometimes a single trigger is not enough to describe the construction
|
||||
/// site. In this case we'd have a chain of "partial" construction context
|
||||
/// layers.
|
||||
/// Some examples:
|
||||
/// - A constructor within in an aggregate initializer list within a variable
|
||||
/// would have a construction context of the initializer list with
|
||||
/// the parent construction context of a variable.
|
||||
/// - A constructor for a temporary that needs to be both destroyed
|
||||
/// and materialized into an elidable copy constructor would have a
|
||||
/// construction context of a CXXBindTemporaryExpr with the parent
|
||||
/// construction context of a MaterializeTemproraryExpr.
|
||||
/// Not all of these are currently supported.
|
||||
const ConstructionContextLayer *Parent = nullptr;
|
||||
|
||||
ConstructionContextLayer(TriggerTy Trigger,
|
||||
const ConstructionContextLayer *Parent)
|
||||
: Trigger(Trigger), Parent(Parent) {}
|
||||
|
||||
public:
|
||||
static const ConstructionContextLayer *
|
||||
create(BumpVectorContext &C, TriggerTy Trigger,
|
||||
const ConstructionContextLayer *Parent = nullptr);
|
||||
|
||||
const ConstructionContextLayer *getParent() const { return Parent; }
|
||||
bool isLast() const { return !Parent; }
|
||||
|
||||
const Stmt *getTriggerStmt() const {
|
||||
return Trigger.dyn_cast<Stmt *>();
|
||||
}
|
||||
|
||||
const CXXCtorInitializer *getTriggerInit() const {
|
||||
return Trigger.dyn_cast<CXXCtorInitializer *>();
|
||||
}
|
||||
|
||||
/// Returns true if these layers are equal as individual layers, even if
|
||||
/// their parents are different.
|
||||
bool isSameLayer(const ConstructionContextLayer *Other) const {
|
||||
assert(Other);
|
||||
return (Trigger == Other->Trigger);
|
||||
}
|
||||
|
||||
/// See if Other is a proper initial segment of this construction context
|
||||
/// in terms of the parent chain - i.e. a few first parents coincide and
|
||||
/// then the other context terminates but our context goes further - i.e.,
|
||||
/// we are providing the same context that the other context provides,
|
||||
/// and a bit more above that.
|
||||
bool isStrictlyMoreSpecificThan(const ConstructionContextLayer *Other) const;
|
||||
};
|
||||
|
||||
|
||||
/// ConstructionContext's subclasses describe different ways of constructing
|
||||
/// an object in C++. The context re-captures the essential parent AST nodes
|
||||
/// of the CXXConstructExpr it is assigned to and presents these nodes
|
||||
/// through easy-to-understand accessor methods.
|
||||
class ConstructionContext {
|
||||
public:
|
||||
enum Kind {
|
||||
SimpleVariableKind,
|
||||
ConstructorInitializerKind,
|
||||
NewAllocatedObjectKind,
|
||||
TemporaryObjectKind,
|
||||
ReturnedValueKind
|
||||
};
|
||||
|
||||
protected:
|
||||
Kind K;
|
||||
|
||||
protected:
|
||||
// Do not make public! These need to only be constructed
|
||||
// via createFromLayers().
|
||||
explicit ConstructionContext(Kind K) : K(K) {}
|
||||
|
||||
public:
|
||||
|
||||
/// Consume the construction context layer, together with its parent layers,
|
||||
/// and wrap it up into a complete construction context.
|
||||
static const ConstructionContext *
|
||||
createFromLayers(BumpVectorContext &C,
|
||||
const ConstructionContextLayer *TopLayer);
|
||||
|
||||
Kind getKind() const { return K; }
|
||||
};
|
||||
|
||||
/// Represents construction into a simple local variable, eg. T var(123);.
|
||||
class SimpleVariableConstructionContext : public ConstructionContext {
|
||||
const DeclStmt *DS;
|
||||
|
||||
public:
|
||||
explicit SimpleVariableConstructionContext(const DeclStmt *DS)
|
||||
: ConstructionContext(ConstructionContext::SimpleVariableKind), DS(DS) {
|
||||
assert(DS);
|
||||
}
|
||||
|
||||
const DeclStmt *getDeclStmt() const { return DS; }
|
||||
|
||||
static bool classof(const ConstructionContext *CC) {
|
||||
return CC->getKind() == SimpleVariableKind;
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents construction into a field or a base class within a bigger object
|
||||
/// via a constructor initializer, eg. T(): field(123) { ... }.
|
||||
class ConstructorInitializerConstructionContext : public ConstructionContext {
|
||||
const CXXCtorInitializer *I;
|
||||
|
||||
public:
|
||||
explicit ConstructorInitializerConstructionContext(
|
||||
const CXXCtorInitializer *I)
|
||||
: ConstructionContext(ConstructionContext::ConstructorInitializerKind),
|
||||
I(I) {
|
||||
assert(I);
|
||||
}
|
||||
|
||||
const CXXCtorInitializer *getCXXCtorInitializer() const { return I; }
|
||||
|
||||
static bool classof(const ConstructionContext *CC) {
|
||||
return CC->getKind() == ConstructorInitializerKind;
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents immediate initialization of memory allocated by operator new,
|
||||
/// eg. new T(123);.
|
||||
class NewAllocatedObjectConstructionContext : public ConstructionContext {
|
||||
const CXXNewExpr *NE;
|
||||
|
||||
public:
|
||||
explicit NewAllocatedObjectConstructionContext(const CXXNewExpr *NE)
|
||||
: ConstructionContext(ConstructionContext::NewAllocatedObjectKind),
|
||||
NE(NE) {
|
||||
assert(NE);
|
||||
}
|
||||
|
||||
const CXXNewExpr *getCXXNewExpr() const { return NE; }
|
||||
|
||||
static bool classof(const ConstructionContext *CC) {
|
||||
return CC->getKind() == NewAllocatedObjectKind;
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents a temporary object, eg. T(123), that does not immediately cross
|
||||
/// function boundaries "by value"; constructors that construct function
|
||||
/// value-type arguments or values that are immediately returned from the
|
||||
/// function that returns a value receive separate construction context kinds.
|
||||
class TemporaryObjectConstructionContext : public ConstructionContext {
|
||||
const CXXBindTemporaryExpr *BTE;
|
||||
const MaterializeTemporaryExpr *MTE;
|
||||
|
||||
public:
|
||||
explicit TemporaryObjectConstructionContext(
|
||||
const CXXBindTemporaryExpr *BTE, const MaterializeTemporaryExpr *MTE)
|
||||
: ConstructionContext(ConstructionContext::TemporaryObjectKind),
|
||||
BTE(BTE), MTE(MTE) {
|
||||
// Both BTE and MTE can be null here, all combinations possible.
|
||||
// Even though for now at least one should be non-null, we simply haven't
|
||||
// implemented this case yet (this would be a temporary in the middle of
|
||||
// nowhere that doesn't have a non-trivial destructor).
|
||||
}
|
||||
|
||||
/// CXXBindTemporaryExpr here is non-null as long as the temporary has
|
||||
/// a non-trivial destructor.
|
||||
const CXXBindTemporaryExpr *getCXXBindTemporaryExpr() const {
|
||||
return BTE;
|
||||
}
|
||||
|
||||
/// MaterializeTemporaryExpr is non-null as long as the temporary is actually
|
||||
/// used after construction, eg. by binding to a reference (lifetime
|
||||
/// extension), accessing a field, calling a method, or passing it into
|
||||
/// a function (an elidable copy or move constructor would be a common
|
||||
/// example) by reference.
|
||||
const MaterializeTemporaryExpr *getMaterializedTemporaryExpr() const {
|
||||
return MTE;
|
||||
}
|
||||
|
||||
static bool classof(const ConstructionContext *CC) {
|
||||
return CC->getKind() == TemporaryObjectKind;
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents a temporary object that is being immediately returned from a
|
||||
/// function by value, eg. return t; or return T(123);. In this case there is
|
||||
/// always going to be a constructor at the return site. However, the usual
|
||||
/// temporary-related bureaucracy (CXXBindTemporaryExpr,
|
||||
/// MaterializeTemporaryExpr) is normally located in the caller function's AST.
|
||||
class ReturnedValueConstructionContext : public ConstructionContext {
|
||||
const ReturnStmt *RS;
|
||||
|
||||
public:
|
||||
explicit ReturnedValueConstructionContext(const ReturnStmt *RS)
|
||||
: ConstructionContext(ConstructionContext::ReturnedValueKind), RS(RS) {
|
||||
assert(RS);
|
||||
}
|
||||
|
||||
const ReturnStmt *getReturnStmt() const { return RS; }
|
||||
|
||||
static bool classof(const ConstructionContext *CC) {
|
||||
return CC->getKind() == ReturnedValueKind;
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
#endif // LLVM_CLANG_ANALYSIS_CONSTRUCTIONCONTEXT_H
|
|
@ -29,6 +29,7 @@
|
|||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/Analysis/Support/BumpVector.h"
|
||||
#include "clang/Analysis/ConstructionContext.h"
|
||||
#include "clang/Basic/Builtins.h"
|
||||
#include "clang/Basic/ExceptionSpecificationType.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
|
@ -475,7 +476,7 @@ class CFGBuilder {
|
|||
// Information about the currently visited C++ object construction site.
|
||||
// This is set in the construction trigger and read when the constructor
|
||||
// itself is being visited.
|
||||
llvm::DenseMap<CXXConstructExpr *, const ConstructionContext *>
|
||||
llvm::DenseMap<CXXConstructExpr *, const ConstructionContextLayer *>
|
||||
ConstructionContextMap;
|
||||
|
||||
bool badCFG = false;
|
||||
|
@ -652,18 +653,19 @@ private:
|
|||
return Block;
|
||||
}
|
||||
|
||||
// Remember to apply \p CC when constructing the CFG element for \p CE.
|
||||
void consumeConstructionContext(const ConstructionContext *CC,
|
||||
// Remember to apply the construction context based on the current \p Layer
|
||||
// when constructing the CFG element for \p CE.
|
||||
void consumeConstructionContext(const ConstructionContextLayer *Layer,
|
||||
CXXConstructExpr *CE);
|
||||
|
||||
// Scan the child statement \p Child to find the constructor that might
|
||||
// have been directly triggered by the current node, \p Trigger. If such
|
||||
// constructor has been found, set current construction context to point
|
||||
// to the trigger statement. The construction context will be unset once
|
||||
// it is consumed when the CFG building procedure processes the
|
||||
// construct-expression and adds the respective CFGConstructor element.
|
||||
void findConstructionContexts(const ConstructionContext *ContextSoFar,
|
||||
// Scan \p Child statement to find constructors in it, while keeping in mind
|
||||
// that its parent statement is providing a partial construction context
|
||||
// described by \p Layer. If a constructor is found, it would be assigned
|
||||
// the context based on the layer. If an additional construction context layer
|
||||
// is found, the function recurses into that.
|
||||
void findConstructionContexts(const ConstructionContextLayer *Layer,
|
||||
Stmt *Child);
|
||||
|
||||
// Unset the construction context after consuming it. This is done immediately
|
||||
// after adding the CFGConstructor element, so there's no need to
|
||||
// do this manually in every Visit... function.
|
||||
|
@ -710,7 +712,11 @@ private:
|
|||
|
||||
void appendConstructor(CFGBlock *B, CXXConstructExpr *CE) {
|
||||
if (BuildOpts.AddRichCXXConstructors) {
|
||||
if (const ConstructionContext *CC = ConstructionContextMap.lookup(CE)) {
|
||||
if (const ConstructionContextLayer *Layer =
|
||||
ConstructionContextMap.lookup(CE)) {
|
||||
const ConstructionContext *CC =
|
||||
ConstructionContext::createFromLayers(cfg->getBumpVectorContext(),
|
||||
Layer);
|
||||
B->appendConstructor(CE, CC, cfg->getBumpVectorContext());
|
||||
cleanupConstructionContext(CE);
|
||||
return;
|
||||
|
@ -1155,20 +1161,21 @@ static const VariableArrayType *FindVA(const Type *t) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void CFGBuilder::consumeConstructionContext(const ConstructionContext *CC, CXXConstructExpr *CE) {
|
||||
if (const ConstructionContext *PreviousContext =
|
||||
void CFGBuilder::consumeConstructionContext(
|
||||
const ConstructionContextLayer *Layer, CXXConstructExpr *CE) {
|
||||
if (const ConstructionContextLayer *PreviouslyStoredLayer =
|
||||
ConstructionContextMap.lookup(CE)) {
|
||||
// We might have visited this child when we were finding construction
|
||||
// contexts within its parents.
|
||||
assert(PreviousContext->isStrictlyMoreSpecificThan(CC) &&
|
||||
assert(PreviouslyStoredLayer->isStrictlyMoreSpecificThan(Layer) &&
|
||||
"Already within a different construction context!");
|
||||
} else {
|
||||
ConstructionContextMap[CE] = CC;
|
||||
ConstructionContextMap[CE] = Layer;
|
||||
}
|
||||
}
|
||||
|
||||
void CFGBuilder::findConstructionContexts(
|
||||
const ConstructionContext *ContextSoFar, Stmt *Child) {
|
||||
const ConstructionContextLayer *Layer, Stmt *Child) {
|
||||
if (!BuildOpts.AddRichCXXConstructors)
|
||||
return;
|
||||
|
||||
|
@ -1178,36 +1185,36 @@ void CFGBuilder::findConstructionContexts(
|
|||
switch(Child->getStmtClass()) {
|
||||
case Stmt::CXXConstructExprClass:
|
||||
case Stmt::CXXTemporaryObjectExprClass: {
|
||||
consumeConstructionContext(ContextSoFar, cast<CXXConstructExpr>(Child));
|
||||
consumeConstructionContext(Layer, cast<CXXConstructExpr>(Child));
|
||||
break;
|
||||
}
|
||||
case Stmt::ExprWithCleanupsClass: {
|
||||
auto *Cleanups = cast<ExprWithCleanups>(Child);
|
||||
findConstructionContexts(ContextSoFar, Cleanups->getSubExpr());
|
||||
findConstructionContexts(Layer, Cleanups->getSubExpr());
|
||||
break;
|
||||
}
|
||||
case Stmt::CXXFunctionalCastExprClass: {
|
||||
auto *Cast = cast<CXXFunctionalCastExpr>(Child);
|
||||
findConstructionContexts(ContextSoFar, Cast->getSubExpr());
|
||||
findConstructionContexts(Layer, Cast->getSubExpr());
|
||||
break;
|
||||
}
|
||||
case Stmt::ImplicitCastExprClass: {
|
||||
auto *Cast = cast<ImplicitCastExpr>(Child);
|
||||
findConstructionContexts(ContextSoFar, Cast->getSubExpr());
|
||||
findConstructionContexts(Layer, Cast->getSubExpr());
|
||||
break;
|
||||
}
|
||||
case Stmt::CXXBindTemporaryExprClass: {
|
||||
auto *BTE = cast<CXXBindTemporaryExpr>(Child);
|
||||
findConstructionContexts(
|
||||
ConstructionContext::create(cfg->getBumpVectorContext(), BTE,
|
||||
ContextSoFar),
|
||||
ConstructionContextLayer::create(cfg->getBumpVectorContext(),
|
||||
BTE, Layer),
|
||||
BTE->getSubExpr());
|
||||
break;
|
||||
}
|
||||
case Stmt::ConditionalOperatorClass: {
|
||||
auto *CO = cast<ConditionalOperator>(Child);
|
||||
findConstructionContexts(ContextSoFar, CO->getLHS());
|
||||
findConstructionContexts(ContextSoFar, CO->getRHS());
|
||||
findConstructionContexts(Layer, CO->getLHS());
|
||||
findConstructionContexts(Layer, CO->getRHS());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -1356,7 +1363,7 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
|
|||
|
||||
if (Init) {
|
||||
findConstructionContexts(
|
||||
ConstructionContext::create(cfg->getBumpVectorContext(), I),
|
||||
ConstructionContextLayer::create(cfg->getBumpVectorContext(), I),
|
||||
Init);
|
||||
|
||||
if (HasTemporaries) {
|
||||
|
@ -2448,7 +2455,7 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
|
|||
appendStmt(Block, DS);
|
||||
|
||||
findConstructionContexts(
|
||||
ConstructionContext::create(cfg->getBumpVectorContext(), DS),
|
||||
ConstructionContextLayer::create(cfg->getBumpVectorContext(), DS),
|
||||
Init);
|
||||
|
||||
// Keep track of the last non-null block, as 'Block' can be nulled out
|
||||
|
@ -2642,7 +2649,7 @@ CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) {
|
|||
addAutomaticObjHandling(ScopePos, LocalScope::const_iterator(), R);
|
||||
|
||||
findConstructionContexts(
|
||||
ConstructionContext::create(cfg->getBumpVectorContext(), R),
|
||||
ConstructionContextLayer::create(cfg->getBumpVectorContext(), R),
|
||||
R->getRetValue());
|
||||
|
||||
// If the one of the destructors does not return, we already have the Exit
|
||||
|
@ -3015,7 +3022,7 @@ CFGBlock *
|
|||
CFGBuilder::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *MTE,
|
||||
AddStmtChoice asc) {
|
||||
findConstructionContexts(
|
||||
ConstructionContext::create(cfg->getBumpVectorContext(), MTE),
|
||||
ConstructionContextLayer::create(cfg->getBumpVectorContext(), MTE),
|
||||
MTE->getTemporary());
|
||||
|
||||
return VisitStmt(MTE, asc);
|
||||
|
@ -4002,7 +4009,7 @@ CFGBlock *CFGBuilder::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E,
|
|||
appendStmt(Block, E);
|
||||
|
||||
findConstructionContexts(
|
||||
ConstructionContext::create(cfg->getBumpVectorContext(), E),
|
||||
ConstructionContextLayer::create(cfg->getBumpVectorContext(), E),
|
||||
E->getSubExpr());
|
||||
|
||||
// We do not want to propagate the AlwaysAdd property.
|
||||
|
@ -4025,7 +4032,7 @@ CFGBlock *CFGBuilder::VisitCXXNewExpr(CXXNewExpr *NE,
|
|||
appendStmt(Block, NE);
|
||||
|
||||
findConstructionContexts(
|
||||
ConstructionContext::create(cfg->getBumpVectorContext(), NE),
|
||||
ConstructionContextLayer::create(cfg->getBumpVectorContext(), NE),
|
||||
const_cast<CXXConstructExpr *>(NE->getConstructExpr()));
|
||||
|
||||
if (NE->getInitializer())
|
||||
|
@ -4752,19 +4759,44 @@ static void print_elem(raw_ostream &OS, StmtPrinterHelper &Helper,
|
|||
} else if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(S)) {
|
||||
OS << " (CXXConstructExpr, ";
|
||||
if (Optional<CFGConstructor> CE = E.getAs<CFGConstructor>()) {
|
||||
// TODO: Refactor into ConstructionContext::print().
|
||||
if (const Stmt *S = CE->getTriggerStmt())
|
||||
Helper.handledStmt(const_cast<Stmt *>(S), OS);
|
||||
else if (const CXXCtorInitializer *I = CE->getTriggerInit())
|
||||
print_initializer(OS, Helper, I);
|
||||
else
|
||||
llvm_unreachable("Unexpected trigger kind!");
|
||||
OS << ", ";
|
||||
if (const Stmt *S = CE->getMaterializedTemporary()) {
|
||||
if (S != CE->getTriggerStmt()) {
|
||||
Helper.handledStmt(const_cast<Stmt *>(S), OS);
|
||||
OS << ", ";
|
||||
}
|
||||
const ConstructionContext *CC = CE->getConstructionContext();
|
||||
const Stmt *S1 = nullptr, *S2 = nullptr;
|
||||
switch (CC->getKind()) {
|
||||
case ConstructionContext::ConstructorInitializerKind: {
|
||||
const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
|
||||
print_initializer(OS, Helper, ICC->getCXXCtorInitializer());
|
||||
OS << ", ";
|
||||
break;
|
||||
}
|
||||
case ConstructionContext::SimpleVariableKind: {
|
||||
const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
|
||||
S1 = DSCC->getDeclStmt();
|
||||
break;
|
||||
}
|
||||
case ConstructionContext::NewAllocatedObjectKind: {
|
||||
const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
|
||||
S1 = NECC->getCXXNewExpr();
|
||||
break;
|
||||
}
|
||||
case ConstructionContext::ReturnedValueKind: {
|
||||
const auto *RSCC = cast<ReturnedValueConstructionContext>(CC);
|
||||
S1 = RSCC->getReturnStmt();
|
||||
break;
|
||||
}
|
||||
case ConstructionContext::TemporaryObjectKind: {
|
||||
const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
|
||||
S1 = TOCC->getCXXBindTemporaryExpr();
|
||||
S2 = TOCC->getMaterializedTemporaryExpr();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (S1) {
|
||||
Helper.handledStmt(const_cast<Stmt *>(S1), OS);
|
||||
OS << ", ";
|
||||
}
|
||||
if (S2) {
|
||||
Helper.handledStmt(const_cast<Stmt *>(S2), OS);
|
||||
OS << ", ";
|
||||
}
|
||||
}
|
||||
OS << CCE->getType().getAsString() << ")";
|
||||
|
|
|
@ -11,6 +11,7 @@ add_clang_library(clangAnalysis
|
|||
CallGraph.cpp
|
||||
CloneDetection.cpp
|
||||
CocoaConventions.cpp
|
||||
ConstructionContext.cpp
|
||||
Consumed.cpp
|
||||
CodeInjector.cpp
|
||||
Dominators.cpp
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
//===- ConstructionContext.cpp - CFG constructor information --------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the ConstructionContext class and its sub-classes,
|
||||
// which represent various different ways of constructing C++ objects
|
||||
// with the additional information the users may want to know about
|
||||
// the constructor.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Analysis/ConstructionContext.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
const ConstructionContextLayer *
|
||||
ConstructionContextLayer::create(BumpVectorContext &C, TriggerTy Trigger,
|
||||
const ConstructionContextLayer *Parent) {
|
||||
ConstructionContextLayer *CC =
|
||||
C.getAllocator().Allocate<ConstructionContextLayer>();
|
||||
return new (CC) ConstructionContextLayer(Trigger, Parent);
|
||||
}
|
||||
|
||||
bool ConstructionContextLayer::isStrictlyMoreSpecificThan(
|
||||
const ConstructionContextLayer *Other) const {
|
||||
const ConstructionContextLayer *Self = this;
|
||||
while (true) {
|
||||
if (!Other)
|
||||
return Self;
|
||||
if (!Self || !Self->isSameLayer(Other))
|
||||
return false;
|
||||
Self = Self->getParent();
|
||||
Other = Other->getParent();
|
||||
}
|
||||
llvm_unreachable("The above loop can only be terminated via return!");
|
||||
}
|
||||
|
||||
const ConstructionContext *ConstructionContext::createFromLayers(
|
||||
BumpVectorContext &C, const ConstructionContextLayer *TopLayer) {
|
||||
// Before this point all we've had was a stockpile of arbitrary layers.
|
||||
// Now validate that it is shaped as one of the finite amount of expected
|
||||
// patterns.
|
||||
if (const Stmt *S = TopLayer->getTriggerStmt()) {
|
||||
if (const auto *DS = dyn_cast<DeclStmt>(S)) {
|
||||
assert(TopLayer->isLast());
|
||||
auto *CC =
|
||||
C.getAllocator().Allocate<SimpleVariableConstructionContext>();
|
||||
return new (CC) SimpleVariableConstructionContext(DS);
|
||||
} else if (const auto *NE = dyn_cast<CXXNewExpr>(S)) {
|
||||
assert(TopLayer->isLast());
|
||||
auto *CC =
|
||||
C.getAllocator().Allocate<NewAllocatedObjectConstructionContext>();
|
||||
return new (CC) NewAllocatedObjectConstructionContext(NE);
|
||||
} else if (const auto *BTE = dyn_cast<CXXBindTemporaryExpr>(S)) {
|
||||
const MaterializeTemporaryExpr *MTE = nullptr;
|
||||
assert(BTE->getType().getCanonicalType()
|
||||
->getAsCXXRecordDecl()->hasNonTrivialDestructor());
|
||||
// For temporaries with destructors, there may or may not be
|
||||
// lifetime extension on the parent layer.
|
||||
if (const ConstructionContextLayer *ParentLayer = TopLayer->getParent()) {
|
||||
assert(ParentLayer->isLast());
|
||||
MTE = cast<MaterializeTemporaryExpr>(ParentLayer->getTriggerStmt());
|
||||
}
|
||||
auto *CC =
|
||||
C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
|
||||
return new (CC) TemporaryObjectConstructionContext(BTE, MTE);
|
||||
} else if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(S)) {
|
||||
assert(MTE->getType().getCanonicalType()
|
||||
->getAsCXXRecordDecl()->hasTrivialDestructor());
|
||||
assert(TopLayer->isLast());
|
||||
auto *CC =
|
||||
C.getAllocator().Allocate<TemporaryObjectConstructionContext>();
|
||||
return new (CC) TemporaryObjectConstructionContext(nullptr, MTE);
|
||||
} else if (const auto *RS = dyn_cast<ReturnStmt>(S)) {
|
||||
assert(TopLayer->isLast());
|
||||
auto *CC =
|
||||
C.getAllocator().Allocate<ReturnedValueConstructionContext>();
|
||||
return new (CC) ReturnedValueConstructionContext(RS);
|
||||
}
|
||||
} else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {
|
||||
assert(TopLayer->isLast());
|
||||
auto *CC =
|
||||
C.getAllocator().Allocate<ConstructorInitializerConstructionContext>();
|
||||
return new (CC) ConstructorInitializerConstructionContext(I);
|
||||
}
|
||||
llvm_unreachable("Unexpected construction context!");
|
||||
}
|
|
@ -12,6 +12,7 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
|
||||
#include "clang/Analysis/ConstructionContext.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/StmtCXX.h"
|
||||
#include "clang/AST/ParentMap.h"
|
||||
|
@ -111,47 +112,20 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
|
|||
// See if we're constructing an existing region by looking at the
|
||||
// current construction context.
|
||||
if (CC) {
|
||||
if (const Stmt *TriggerStmt = CC->getTriggerStmt()) {
|
||||
if (const CXXNewExpr *CNE = dyn_cast<CXXNewExpr>(TriggerStmt)) {
|
||||
if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
|
||||
// TODO: Detect when the allocator returns a null pointer.
|
||||
// Constructor shall not be called in this case.
|
||||
if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(
|
||||
getCXXNewAllocatorValue(State, CNE, LCtx).getAsRegion())) {
|
||||
if (CNE->isArray()) {
|
||||
// TODO: In fact, we need to call the constructor for every
|
||||
// allocated element, not just the first one!
|
||||
CallOpts.IsArrayCtorOrDtor = true;
|
||||
return getStoreManager().GetElementZeroRegion(
|
||||
MR, CNE->getType()->getPointeeType());
|
||||
}
|
||||
return MR;
|
||||
}
|
||||
}
|
||||
} else if (auto *DS = dyn_cast<DeclStmt>(TriggerStmt)) {
|
||||
const auto *Var = cast<VarDecl>(DS->getSingleDecl());
|
||||
SVal LValue = State->getLValue(Var, LCtx);
|
||||
QualType Ty = Var->getType();
|
||||
LValue = makeZeroElementRegion(State, LValue, Ty,
|
||||
CallOpts.IsArrayCtorOrDtor);
|
||||
return LValue.getAsRegion();
|
||||
} else if (isa<ReturnStmt>(TriggerStmt)) {
|
||||
// TODO: We should construct into a CXXBindTemporaryExpr or a
|
||||
// MaterializeTemporaryExpr around the call-expression on the previous
|
||||
// stack frame. Currently we re-bind the temporary to the correct region
|
||||
// later, but that's not semantically correct. This of course does not
|
||||
// apply when we're in the top frame. But if we are in an inlined
|
||||
// function, we should be able to take the call-site CFG element,
|
||||
// and it should contain (but right now it wouldn't) some sort of
|
||||
// construction context that'd give us the right temporary expression.
|
||||
CallOpts.IsTemporaryCtorOrDtor = true;
|
||||
return MRMgr.getCXXTempObjectRegion(CE, LCtx);
|
||||
} else if (isa<CXXBindTemporaryExpr>(TriggerStmt)) {
|
||||
CallOpts.IsTemporaryCtorOrDtor = true;
|
||||
return MRMgr.getCXXTempObjectRegion(CE, LCtx);
|
||||
}
|
||||
// TODO: Consider other directly initialized elements.
|
||||
} else if (const CXXCtorInitializer *Init = CC->getTriggerInit()) {
|
||||
switch (CC->getKind()) {
|
||||
case ConstructionContext::SimpleVariableKind: {
|
||||
const auto *DSCC = cast<SimpleVariableConstructionContext>(CC);
|
||||
const auto *DS = DSCC->getDeclStmt();
|
||||
const auto *Var = cast<VarDecl>(DS->getSingleDecl());
|
||||
SVal LValue = State->getLValue(Var, LCtx);
|
||||
QualType Ty = Var->getType();
|
||||
LValue =
|
||||
makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
|
||||
return LValue.getAsRegion();
|
||||
}
|
||||
case ConstructionContext::ConstructorInitializerKind: {
|
||||
const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
|
||||
const auto *Init = ICC->getCXXCtorInitializer();
|
||||
assert(Init->isAnyMemberInitializer());
|
||||
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
|
||||
Loc ThisPtr =
|
||||
|
@ -173,10 +147,45 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
|
|||
CallOpts.IsArrayCtorOrDtor);
|
||||
return FieldVal.getAsRegion();
|
||||
}
|
||||
|
||||
// FIXME: This will eventually need to handle new-expressions as well.
|
||||
// Don't forget to update the pre-constructor initialization code in
|
||||
// ExprEngine::VisitCXXConstructExpr.
|
||||
case ConstructionContext::NewAllocatedObjectKind: {
|
||||
if (AMgr.getAnalyzerOptions().mayInlineCXXAllocator()) {
|
||||
const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
|
||||
const auto *NE = NECC->getCXXNewExpr();
|
||||
// TODO: Detect when the allocator returns a null pointer.
|
||||
// Constructor shall not be called in this case.
|
||||
if (const SubRegion *MR = dyn_cast_or_null<SubRegion>(
|
||||
getCXXNewAllocatorValue(State, NE, LCtx).getAsRegion())) {
|
||||
if (NE->isArray()) {
|
||||
// TODO: In fact, we need to call the constructor for every
|
||||
// allocated element, not just the first one!
|
||||
CallOpts.IsArrayCtorOrDtor = true;
|
||||
return getStoreManager().GetElementZeroRegion(
|
||||
MR, NE->getType()->getPointeeType());
|
||||
}
|
||||
return MR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ConstructionContext::TemporaryObjectKind: {
|
||||
// TODO: Support temporaries lifetime-extended via static references.
|
||||
// They'd need a getCXXStaticTempObjectRegion().
|
||||
CallOpts.IsTemporaryCtorOrDtor = true;
|
||||
return MRMgr.getCXXTempObjectRegion(CE, LCtx);
|
||||
}
|
||||
case ConstructionContext::ReturnedValueKind: {
|
||||
// TODO: We should construct into a CXXBindTemporaryExpr or a
|
||||
// MaterializeTemporaryExpr around the call-expression on the previous
|
||||
// stack frame. Currently we re-bind the temporary to the correct region
|
||||
// later, but that's not semantically correct. This of course does not
|
||||
// apply when we're in the top frame. But if we are in an inlined
|
||||
// function, we should be able to take the call-site CFG element,
|
||||
// and it should contain (but right now it wouldn't) some sort of
|
||||
// construction context that'd give us the right temporary expression.
|
||||
CallOpts.IsTemporaryCtorOrDtor = true;
|
||||
return MRMgr.getCXXTempObjectRegion(CE, LCtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we couldn't find an existing region to construct into, assume we're
|
||||
// constructing a temporary. Notify the caller of our failure.
|
||||
|
@ -227,6 +236,7 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
|
|||
|
||||
EvalCallOptions CallOpts;
|
||||
auto C = getCurrentCFGElement().getAs<CFGConstructor>();
|
||||
assert(C || getCurrentCFGElement().getAs<CFGStmt>());
|
||||
const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
|
||||
|
||||
const CXXBindTemporaryExpr *BTE = nullptr;
|
||||
|
@ -235,15 +245,27 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
|
|||
switch (CE->getConstructionKind()) {
|
||||
case CXXConstructExpr::CK_Complete: {
|
||||
Target = getRegionForConstructedObject(CE, Pred, CC, CallOpts);
|
||||
if (CC && AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG() &&
|
||||
!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion &&
|
||||
CallOpts.IsTemporaryCtorOrDtor) {
|
||||
MTE = CC->getMaterializedTemporary();
|
||||
if (!MTE || MTE->getStorageDuration() == SD_FullExpression) {
|
||||
// If the temporary is lifetime-extended, don't save the BTE,
|
||||
// because we don't need a temporary destructor, but an automatic
|
||||
// destructor. The cast may fail because it may as well be a ReturnStmt.
|
||||
BTE = dyn_cast<CXXBindTemporaryExpr>(CC->getTriggerStmt());
|
||||
|
||||
// In case of temporary object construction, extract data necessary for
|
||||
// destruction and lifetime extension.
|
||||
if (const auto *TCC =
|
||||
dyn_cast_or_null<TemporaryObjectConstructionContext>(CC)) {
|
||||
assert(CallOpts.IsTemporaryCtorOrDtor);
|
||||
assert(!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion);
|
||||
if (AMgr.getAnalyzerOptions().includeTemporaryDtorsInCFG()) {
|
||||
BTE = TCC->getCXXBindTemporaryExpr();
|
||||
MTE = TCC->getMaterializedTemporaryExpr();
|
||||
if (!BTE) {
|
||||
// FIXME: lifetime extension for temporaries without destructors
|
||||
// is not implemented yet.
|
||||
MTE = nullptr;
|
||||
}
|
||||
if (MTE && MTE->getStorageDuration() != SD_FullExpression) {
|
||||
// If the temporary is lifetime-extended, don't save the BTE,
|
||||
// because we don't need a temporary destructor, but an automatic
|
||||
// destructor.
|
||||
BTE = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "clang/AST/CXXInheritance.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/Analysis/Analyses/LiveVariables.h"
|
||||
#include "clang/Analysis/ConstructionContext.h"
|
||||
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
||||
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
|
@ -635,10 +636,11 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
|
|||
|
||||
const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
|
||||
|
||||
auto CC = getCurrentCFGElement().getAs<CFGConstructor>();
|
||||
const Stmt *ParentExpr = CC ? CC->getTriggerStmt() : nullptr;
|
||||
auto CCE = getCurrentCFGElement().getAs<CFGConstructor>();
|
||||
const ConstructionContext *CC = CCE ? CCE->getConstructionContext()
|
||||
: nullptr;
|
||||
|
||||
if (ParentExpr && isa<CXXNewExpr>(ParentExpr) &&
|
||||
if (CC && isa<NewAllocatedObjectConstructionContext>(CC) &&
|
||||
!Opts.mayInlineCXXAllocator())
|
||||
return CIP_DisallowedOnce;
|
||||
|
||||
|
|
Loading…
Reference in New Issue