Skip to content

Commit bd880fe

Browse files
committed
[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
1 parent 959fe03 commit bd880fe

File tree

6 files changed

+173
-12
lines changed

6 files changed

+173
-12
lines changed

clang/lib/Analysis/CFG.cpp

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ class CFGBuilder {
569569
CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S);
570570
CFGBlock *VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S);
571571
CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
572+
CFGBlock *VisitObjCMessageExpr(ObjCMessageExpr *E, AddStmtChoice asc);
572573
CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E);
573574
CFGBlock *VisitReturnStmt(ReturnStmt *R);
574575
CFGBlock *VisitSEHExceptStmt(SEHExceptStmt *S);
@@ -683,6 +684,27 @@ class CFGBuilder {
683684
void findConstructionContexts(const ConstructionContextLayer *Layer,
684685
Stmt *Child);
685686

687+
// Scan all arguments of a call expression for a construction context.
688+
// These sorts of call expressions don't have a common superclass,
689+
// hence strict duck-typing.
690+
template <typename CallLikeExpr,
691+
typename = typename std::enable_if<
692+
std::is_same<CallLikeExpr, CallExpr>::value ||
693+
std::is_same<CallLikeExpr, CXXConstructExpr>::value ||
694+
std::is_same<CallLikeExpr, ObjCMessageExpr>::value>>
695+
void findConstructionContextsForArguments(CallLikeExpr *E) {
696+
// A stub for the code that'll eventually be used for finding construction
697+
// contexts for constructors of C++ object-type arguments passed into
698+
// call-like expression E.
699+
// FIXME: Once actually implemented, this construction context layer should
700+
// include the index of the argument as well.
701+
for (auto Arg : E->arguments())
702+
if (Arg->getType()->getAsCXXRecordDecl() && !Arg->isGLValue())
703+
findConstructionContexts(
704+
ConstructionContextLayer::create(cfg->getBumpVectorContext(), E),
705+
Arg);
706+
}
707+
686708
// Unset the construction context after consuming it. This is done immediately
687709
// after adding the CFGConstructor or CFGCXXRecordTypedCall element, so
688710
// there's no need to do this manually in every Visit... function.
@@ -2101,6 +2123,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
21012123
case Stmt::ObjCForCollectionStmtClass:
21022124
return VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S));
21032125

2126+
case Stmt::ObjCMessageExprClass:
2127+
return VisitObjCMessageExpr(cast<ObjCMessageExpr>(S), asc);
2128+
21042129
case Stmt::OpaqueValueExprClass:
21052130
return Block;
21062131

@@ -2383,12 +2408,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
23832408
if (!boundType.isNull()) calleeType = boundType;
23842409
}
23852410

2386-
// FIXME: Once actually implemented, this construction context layer should
2387-
// include the number of the argument as well.
2388-
for (auto Arg: C->arguments()) {
2389-
findConstructionContexts(
2390-
ConstructionContextLayer::create(cfg->getBumpVectorContext(), C), Arg);
2391-
}
2411+
findConstructionContextsForArguments(C);
23922412

23932413
// If this is a call to a no-return function, this stops the block here.
23942414
bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn();
@@ -3580,6 +3600,16 @@ CFGBlock *CFGBuilder::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) {
35803600
return VisitStmt(S, AddStmtChoice::AlwaysAdd);
35813601
}
35823602

3603+
CFGBlock *CFGBuilder::VisitObjCMessageExpr(ObjCMessageExpr *ME,
3604+
AddStmtChoice asc) {
3605+
findConstructionContextsForArguments(ME);
3606+
3607+
autoCreateBlock();
3608+
appendStmt(Block, ME);
3609+
3610+
return VisitChildren(ME);
3611+
}
3612+
35833613
CFGBlock *CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr *T) {
35843614
// If we were in the middle of a block we stop processing that block.
35853615
if (badCFG)
@@ -4244,6 +4274,11 @@ CFGBlock *CFGBuilder::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E,
42444274

42454275
CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C,
42464276
AddStmtChoice asc) {
4277+
// If the constructor takes objects as arguments by value, we need to properly
4278+
// construct these objects. Construction contexts we find here aren't for the
4279+
// constructor C, they're for its arguments only.
4280+
findConstructionContextsForArguments(C);
4281+
42474282
autoCreateBlock();
42484283
appendConstructor(Block, C);
42494284

clang/lib/Analysis/ConstructionContext.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "clang/Analysis/ConstructionContext.h"
18+
#include "clang/AST/ExprObjC.h"
1819

1920
using namespace clang;
2021

@@ -111,7 +112,9 @@ const ConstructionContext *ConstructionContext::createFromLayers(
111112
assert(ParentLayer->isLast());
112113

113114
// This is a constructor into a function argument. Not implemented yet.
114-
if (isa<CallExpr>(ParentLayer->getTriggerStmt()))
115+
if (isa<CallExpr>(ParentLayer->getTriggerStmt()) ||
116+
isa<CXXConstructExpr>(ParentLayer->getTriggerStmt()) ||
117+
isa<ObjCMessageExpr>(ParentLayer->getTriggerStmt()))
115118
return nullptr;
116119
// This is C++17 copy-elided construction into return statement.
117120
if (auto *RS = dyn_cast<ReturnStmt>(ParentLayer->getTriggerStmt())) {
@@ -173,7 +176,9 @@ const ConstructionContext *ConstructionContext::createFromLayers(
173176
return create<SimpleReturnedValueConstructionContext>(C, RS);
174177
}
175178
// This is a constructor into a function argument. Not implemented yet.
176-
if (isa<CallExpr>(TopLayer->getTriggerStmt()))
179+
if (isa<CallExpr>(TopLayer->getTriggerStmt()) ||
180+
isa<CXXConstructExpr>(TopLayer->getTriggerStmt()) ||
181+
isa<ObjCMessageExpr>(TopLayer->getTriggerStmt()))
177182
return nullptr;
178183
llvm_unreachable("Unexpected construction context with statement!");
179184
} else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) {

clang/test/Analysis/cfg-rich-constructors.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,11 @@ class D {
817817
~D();
818818
};
819819

820+
class E {
821+
public:
822+
E(D d);
823+
};
824+
820825
void useC(C c);
821826
void useCByReference(const C &c);
822827
void useD(D d);
@@ -880,6 +885,32 @@ void passArgumentWithDestructor() {
880885
void passArgumentWithDestructorByReference() {
881886
useDByReference(D());
882887
}
888+
889+
// FIXME: Find construction context for the argument.
890+
// CHECK: void passArgumentIntoAnotherConstructor()
891+
// CXX11: 1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.4], class argument_constructors::D)
892+
// CXX11-NEXT: 2: [B1.1] (BindTemporary)
893+
// CXX11-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const class argument_constructors::D)
894+
// CXX11-NEXT: 4: [B1.3]
895+
// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, class argument_constructors::D)
896+
// CXX11-NEXT: 6: [B1.5] (BindTemporary)
897+
// CXX11-ELIDE-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.9], [B1.10], class argument_constructors::E)
898+
// CXX11-NOELIDE-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.9], class argument_constructors::E)
899+
// CXX11-NEXT: 8: argument_constructors::E([B1.7]) (CXXFunctionalCastExpr, ConstructorConversion, class argument_constructors::E)
900+
// CXX11-NEXT: 9: [B1.8]
901+
// CXX11-NEXT: 10: [B1.9] (CXXConstructExpr, [B1.11], class argument_constructors::E)
902+
// CXX11-NEXT: 11: argument_constructors::E e = argument_constructors::E(argument_constructors::D());
903+
// CXX11-NEXT: 12: ~argument_constructors::D() (Temporary object destructor)
904+
// CXX11-NEXT: 13: ~argument_constructors::D() (Temporary object destructor)
905+
// CXX17: 1: argument_constructors::D() (CXXConstructExpr, class argument_constructors::D)
906+
// CXX17-NEXT: 2: [B1.1] (BindTemporary)
907+
// CXX17-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], class argument_constructors::E)
908+
// CXX17-NEXT: 4: argument_constructors::E([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class argument_constructors::E)
909+
// CXX17-NEXT: 5: argument_constructors::E e = argument_constructors::E(argument_constructors::D());
910+
// CXX17-NEXT: 6: ~argument_constructors::D() (Temporary object destructor)
911+
void passArgumentIntoAnotherConstructor() {
912+
E e = E(D());
913+
}
883914
} // end namespace argument_constructors
884915

885916
namespace copy_elision_with_extra_arguments {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++11 -w %s > %t 2>&1
2+
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,ELIDE,CXX11-ELIDE %s
3+
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++17 -w %s > %t 2>&1
4+
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17,ELIDE,CXX17-ELIDE %s
5+
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++11 -w -analyzer-config elide-constructors=false %s > %t 2>&1
6+
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,NOELIDE,CXX11-NOELIDE %s
7+
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++17 -w -analyzer-config elide-constructors=false %s > %t 2>&1
8+
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17,NOELIDE,CXX17-NOELIDE %s
9+
10+
class D {
11+
public:
12+
D();
13+
~D();
14+
};
15+
16+
@interface E {}
17+
-(void) foo: (D) d;
18+
@end
19+
20+
// FIXME: Find construction context for the argument.
21+
// CHECK: void passArgumentIntoMessage(E *e)
22+
// CHECK: 1: e
23+
// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, E *)
24+
// CXX11-NEXT: 3: D() (CXXConstructExpr, [B1.4], [B1.6], class D)
25+
// CXX11-NEXT: 4: [B1.3] (BindTemporary)
26+
// CXX11-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const class D)
27+
// CXX11-NEXT: 6: [B1.5]
28+
// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, class D)
29+
// CXX11-NEXT: 8: [B1.7] (BindTemporary)
30+
// Double brackets trigger FileCheck variables, escape.
31+
// CXX11-NEXT: 9: {{\[}}[B1.2] foo:[B1.8]]
32+
// CXX11-NEXT: 10: ~D() (Temporary object destructor)
33+
// CXX11-NEXT: 11: ~D() (Temporary object destructor)
34+
// CXX17-NEXT: 3: D() (CXXConstructExpr, class D)
35+
// CXX17-NEXT: 4: [B1.3] (BindTemporary)
36+
// Double brackets trigger FileCheck variables, escape.
37+
// CXX17-NEXT: 5: {{\[}}[B1.2] foo:[B1.4]]
38+
// CXX17-NEXT: 6: ~D() (Temporary object destructor)
39+
void passArgumentIntoMessage(E *e) {
40+
[e foo: D()];
41+
}

clang/test/Analysis/temporaries.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++03 %s
2-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++11 %s
3-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true %s -std=c++11
4-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -w -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true %s -std=c++17
1+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++03 %s
2+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++11 %s
3+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true %s -std=c++11
4+
// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus,debug.ExprInspection -DTEMPORARY_DTORS -w -analyzer-config cfg-temporary-dtors=true,c++-temp-dtor-inlining=true %s -std=c++17
55

66
// Note: The C++17 run-line doesn't -verify yet - it is a no-crash test.
77

@@ -945,3 +945,29 @@ C &&foo2();
945945
const C &bar1() { return foo1(); } // no-crash
946946
C &&bar2() { return foo2(); } // no-crash
947947
} // end namespace pass_references_through
948+
949+
950+
namespace ctor_argument {
951+
// Stripped down unique_ptr<int>
952+
struct IntPtr {
953+
IntPtr(): i(new int) {}
954+
IntPtr(IntPtr &&o): i(o.i) { o.i = 0; }
955+
~IntPtr() { delete i; }
956+
957+
int *i;
958+
};
959+
960+
struct Foo {
961+
Foo(IntPtr);
962+
void bar();
963+
964+
IntPtr i;
965+
};
966+
967+
void bar() {
968+
IntPtr ptr;
969+
int *i = ptr.i;
970+
Foo f(static_cast<IntPtr &&>(ptr));
971+
*i = 99; // no-warning
972+
}
973+
} // namespace ctor_argument

clang/test/Analysis/temporaries.mm

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clang_analyze_cc1 -analyzer-checker core,cplusplus -verify %s
2+
3+
// expected-no-diagnostics
4+
5+
// Stripped down unique_ptr<int>
6+
struct IntPtr {
7+
IntPtr(): i(new int) {}
8+
IntPtr(IntPtr &&o): i(o.i) { o.i = nullptr; }
9+
~IntPtr() { delete i; }
10+
11+
int *i;
12+
};
13+
14+
@interface Foo {}
15+
-(void) foo: (IntPtr)arg;
16+
@end
17+
18+
void bar(Foo *f) {
19+
IntPtr ptr;
20+
int *i = ptr.i;
21+
[f foo: static_cast<IntPtr &&>(ptr)];
22+
*i = 99; // no-warning
23+
}

0 commit comments

Comments
 (0)