[CFG] [analyzer] Add stubs for constructor and message argument constructors.
CFG now correctly identifies construction context for temporaries constructed
for the purpose of passing into a function as an argument.
Such context is still not fully implemented because the information it provides
is not rich enough: it doens't contain information about argument index.
It will be addresssed later.
This patch is an extension of r330377 to C++ construct-expressions and
Objective-C message expressions which aren't call-expressions but require
similar handling. C++ new-expressions with placement arguments still remain to
be handled.
Differential Revision: https://reviews.llvm.org/D49826
llvm-svn: 338425
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 0e529bb..9e4fbd6 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -569,6 +569,7 @@
CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S);
CFGBlock *VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S);
CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
+ CFGBlock *VisitObjCMessageExpr(ObjCMessageExpr *E, AddStmtChoice asc);
CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E);
CFGBlock *VisitReturnStmt(ReturnStmt *R);
CFGBlock *VisitSEHExceptStmt(SEHExceptStmt *S);
@@ -683,6 +684,27 @@
void findConstructionContexts(const ConstructionContextLayer *Layer,
Stmt *Child);
+ // Scan all arguments of a call expression for a construction context.
+ // These sorts of call expressions don't have a common superclass,
+ // hence strict duck-typing.
+ template <typename CallLikeExpr,
+ typename = typename std::enable_if<
+ std::is_same<CallLikeExpr, CallExpr>::value ||
+ std::is_same<CallLikeExpr, CXXConstructExpr>::value ||
+ std::is_same<CallLikeExpr, ObjCMessageExpr>::value>>
+ void findConstructionContextsForArguments(CallLikeExpr *E) {
+ // A stub for the code that'll eventually be used for finding construction
+ // contexts for constructors of C++ object-type arguments passed into
+ // call-like expression E.
+ // FIXME: Once actually implemented, this construction context layer should
+ // include the index of the argument as well.
+ for (auto Arg : E->arguments())
+ if (Arg->getType()->getAsCXXRecordDecl() && !Arg->isGLValue())
+ findConstructionContexts(
+ ConstructionContextLayer::create(cfg->getBumpVectorContext(), E),
+ Arg);
+ }
+
// Unset the construction context after consuming it. This is done immediately
// after adding the CFGConstructor or CFGCXXRecordTypedCall element, so
// there's no need to do this manually in every Visit... function.
@@ -2101,6 +2123,9 @@
case Stmt::ObjCForCollectionStmtClass:
return VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S));
+ case Stmt::ObjCMessageExprClass:
+ return VisitObjCMessageExpr(cast<ObjCMessageExpr>(S), asc);
+
case Stmt::OpaqueValueExprClass:
return Block;
@@ -2383,12 +2408,7 @@
if (!boundType.isNull()) calleeType = boundType;
}
- // FIXME: Once actually implemented, this construction context layer should
- // include the number of the argument as well.
- for (auto Arg: C->arguments()) {
- findConstructionContexts(
- ConstructionContextLayer::create(cfg->getBumpVectorContext(), C), Arg);
- }
+ findConstructionContextsForArguments(C);
// If this is a call to a no-return function, this stops the block here.
bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn();
@@ -3580,6 +3600,16 @@
return VisitStmt(S, AddStmtChoice::AlwaysAdd);
}
+CFGBlock *CFGBuilder::VisitObjCMessageExpr(ObjCMessageExpr *ME,
+ AddStmtChoice asc) {
+ findConstructionContextsForArguments(ME);
+
+ autoCreateBlock();
+ appendStmt(Block, ME);
+
+ return VisitChildren(ME);
+}
+
CFGBlock *CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr *T) {
// If we were in the middle of a block we stop processing that block.
if (badCFG)
@@ -4244,6 +4274,11 @@
CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C,
AddStmtChoice asc) {
+ // If the constructor takes objects as arguments by value, we need to properly
+ // construct these objects. Construction contexts we find here aren't for the
+ // constructor C, they're for its arguments only.
+ findConstructionContextsForArguments(C);
+
autoCreateBlock();
appendConstructor(Block, C);
diff --git a/clang/lib/Analysis/ConstructionContext.cpp b/clang/lib/Analysis/ConstructionContext.cpp
index ed1e632..3ffb0a6 100644
--- a/clang/lib/Analysis/ConstructionContext.cpp
+++ b/clang/lib/Analysis/ConstructionContext.cpp
@@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//
#include "clang/Analysis/ConstructionContext.h"
+#include "clang/AST/ExprObjC.h"
using namespace clang;
@@ -111,7 +112,9 @@
assert(ParentLayer->isLast());
// This is a constructor into a function argument. Not implemented yet.
- if (isa<CallExpr>(ParentLayer->getTriggerStmt()))
+ if (isa<CallExpr>(ParentLayer->getTriggerStmt()) ||
+ isa<CXXConstructExpr>(ParentLayer->getTriggerStmt()) ||
+ isa<ObjCMessageExpr>(ParentLayer->getTriggerStmt()))
return nullptr;
// This is C++17 copy-elided construction into return statement.
if (auto *RS = dyn_cast<ReturnStmt>(ParentLayer->getTriggerStmt())) {
@@ -173,7 +176,9 @@
return create<SimpleReturnedValueConstructionContext>(C, RS);
}
// This is a constructor into a function argument. Not implemented yet.
- if (isa<CallExpr>(TopLayer->getTriggerStmt()))
+ if (isa<CallExpr>(TopLayer->getTriggerStmt()) ||
+ isa<CXXConstructExpr>(TopLayer->getTriggerStmt()) ||
+ isa<ObjCMessageExpr>(TopLayer->getTriggerStmt()))
return nullptr;
llvm_unreachable("Unexpected construction context with statement!");
} else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {