Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ 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); @@ -2102,6 +2103,9 @@ case Stmt::ObjCForCollectionStmtClass: return VisitObjCForCollectionStmt(cast(S)); + case Stmt::ObjCMessageExprClass: + return VisitObjCMessageExpr(cast(S), asc); + case Stmt::OpaqueValueExprClass: return Block; @@ -2384,12 +2388,16 @@ if (!boundType.isNull()) calleeType = boundType; } + // A stub for the code that'll eventually be used for finding construction + // contexts for constructors of C++ object-type arguments passed into + // constructor C. // 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); - } + // include the index of the argument as well. + for (auto Arg: C->arguments()) + if (Arg->getType()->getAsCXXRecordDecl() && !Arg->isGLValue()) + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), C), + Arg); // If this is a call to a no-return function, this stops the block here. bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn(); @@ -3581,6 +3589,25 @@ return VisitStmt(S, AddStmtChoice::AlwaysAdd); } +CFGBlock *CFGBuilder::VisitObjCMessageExpr(ObjCMessageExpr *ME, + AddStmtChoice asc) { + // A stub for the code that'll eventually be used for finding construction + // contexts for constructors of C++ object-type arguments passed into + // constructor C. + // FIXME: Once actually implemented, this construction context layer should + // include the index of the argument as well. + for (auto Arg: ME->arguments()) + if (Arg->getType()->getAsCXXRecordDecl() && !Arg->isGLValue()) + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), ME), + Arg); + + 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) @@ -4245,6 +4272,17 @@ CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc) { + // A stub for the code that'll eventually be used for finding construction + // contexts for constructors of C++ object-type arguments passed into + // constructor C. + // FIXME: Once actually implemented, this construction context layer should + // include the index of the argument as well. + for (auto Arg: C->arguments()) + if (Arg->getType()->getAsCXXRecordDecl() && !Arg->isGLValue()) + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), C), + Arg); + autoCreateBlock(); appendConstructor(Block, C); Index: lib/Analysis/ConstructionContext.cpp =================================================================== --- lib/Analysis/ConstructionContext.cpp +++ 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(ParentLayer->getTriggerStmt())) + if (isa(ParentLayer->getTriggerStmt()) || + isa(ParentLayer->getTriggerStmt()) || + isa(ParentLayer->getTriggerStmt())) return nullptr; // This is C++17 copy-elided construction into return statement. if (auto *RS = dyn_cast(ParentLayer->getTriggerStmt())) { @@ -173,7 +176,9 @@ return create(C, RS); } // This is a constructor into a function argument. Not implemented yet. - if (isa(TopLayer->getTriggerStmt())) + if (isa(TopLayer->getTriggerStmt()) || + isa(TopLayer->getTriggerStmt()) || + isa(TopLayer->getTriggerStmt())) return nullptr; llvm_unreachable("Unexpected construction context with statement!"); } else if (const CXXCtorInitializer *I = TopLayer->getTriggerInit()) { Index: test/Analysis/cfg-rich-constructors.cpp =================================================================== --- test/Analysis/cfg-rich-constructors.cpp +++ test/Analysis/cfg-rich-constructors.cpp @@ -814,6 +814,11 @@ ~D(); }; +class E { +public: + E(D d); +}; + void useC(C c); void useCByReference(const C &c); void useD(D d); @@ -877,4 +882,30 @@ void passArgumentWithDestructorByReference() { useDByReference(D()); } + +// FIXME: Find construction context for the argument. +// CHECK: void passArgumentIntoAnotherConstructor() +// CXX11: 1: argument_constructors::D() (CXXConstructExpr, [B1.2], [B1.4], class argument_constructors::D) +// CXX11-NEXT: 2: [B1.1] (BindTemporary) +// CXX11-NEXT: 3: [B1.2] (ImplicitCastExpr, NoOp, const class argument_constructors::D) +// CXX11-NEXT: 4: [B1.3] +// CXX11-NEXT: 5: [B1.4] (CXXConstructExpr, class argument_constructors::D) +// CXX11-NEXT: 6: [B1.5] (BindTemporary) +// CXX11-ELIDE-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.9], [B1.10], class argument_constructors::E) +// CXX11-NOELIDE-NEXT: 7: [B1.6] (CXXConstructExpr, [B1.9], class argument_constructors::E) +// CXX11-NEXT: 8: argument_constructors::E([B1.7]) (CXXFunctionalCastExpr, ConstructorConversion, class argument_constructors::E) +// CXX11-NEXT: 9: [B1.8] +// CXX11-NEXT: 10: [B1.9] (CXXConstructExpr, [B1.11], class argument_constructors::E) +// CXX11-NEXT: 11: argument_constructors::E e = argument_constructors::E(argument_constructors::D()); +// CXX11-NEXT: 12: ~argument_constructors::D() (Temporary object destructor) +// CXX11-NEXT: 13: ~argument_constructors::D() (Temporary object destructor) +// CXX17: 1: argument_constructors::D() (CXXConstructExpr, class argument_constructors::D) +// CXX17-NEXT: 2: [B1.1] (BindTemporary) +// CXX17-NEXT: 3: [B1.2] (CXXConstructExpr, [B1.5], class argument_constructors::E) +// CXX17-NEXT: 4: argument_constructors::E([B1.3]) (CXXFunctionalCastExpr, ConstructorConversion, class argument_constructors::E) +// CXX17-NEXT: 5: argument_constructors::E e = argument_constructors::E(argument_constructors::D()); +// CXX17-NEXT: 6: ~argument_constructors::D() (Temporary object destructor) +void passArgumentIntoAnotherConstructor() { + E e = E(D()); +} } // end namespace argument_constructors Index: test/Analysis/cfg-rich-constructors.mm =================================================================== --- /dev/null +++ test/Analysis/cfg-rich-constructors.mm @@ -0,0 +1,41 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++11 -w %s > %t 2>&1 +// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,ELIDE,CXX11-ELIDE %s +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -std=c++17 -w %s > %t 2>&1 +// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17,ELIDE,CXX17-ELIDE %s +// 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 +// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX11,NOELIDE,CXX11-NOELIDE %s +// 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 +// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,CXX17,NOELIDE,CXX17-NOELIDE %s + +class D { +public: + D(); + ~D(); +}; + +@interface E {} +-(void) foo: (D) d; +@end + +// FIXME: Find construction context for the argument. +// CHECK: void passArgumentIntoMessage(E *e) +// CHECK: 1: e +// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, E *) +// CXX11-NEXT: 3: D() (CXXConstructExpr, [B1.4], [B1.6], class D) +// CXX11-NEXT: 4: [B1.3] (BindTemporary) +// CXX11-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const class D) +// CXX11-NEXT: 6: [B1.5] +// CXX11-NEXT: 7: [B1.6] (CXXConstructExpr, class D) +// CXX11-NEXT: 8: [B1.7] (BindTemporary) +// Double brackets trigger FileCheck variables, escape. +// CXX11-NEXT: 9: {{\[}}[B1.2] foo:[B1.8]] +// CXX11-NEXT: 10: ~D() (Temporary object destructor) +// CXX11-NEXT: 11: ~D() (Temporary object destructor) +// CXX17-NEXT: 3: D() (CXXConstructExpr, class D) +// CXX17-NEXT: 4: [B1.3] (BindTemporary) +// Double brackets trigger FileCheck variables, escape. +// CXX17-NEXT: 5: {{\[}}[B1.2] foo:[B1.4]] +// CXX17-NEXT: 6: ~D() (Temporary object destructor) +void passArgumentIntoMessage(E *e) { + [e foo: D()]; +} Index: test/Analysis/temporaries.cpp =================================================================== --- test/Analysis/temporaries.cpp +++ test/Analysis/temporaries.cpp @@ -1,7 +1,7 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++03 %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++11 %s -// 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 -// 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 +// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++03 %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus,debug.ExprInspection -analyzer-config cfg-temporary-dtors=false -verify -w -std=c++11 %s +// 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 +// 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 // Note: The C++17 run-line doesn't -verify yet - it is a no-crash test. @@ -945,3 +945,29 @@ const C &bar1() { return foo1(); } // no-crash C &&bar2() { return foo2(); } // no-crash } // end namespace pass_references_through + + +namespace ctor_argument { +// Stripped down unique_ptr +struct IntPtr { + IntPtr(): i(new int) {} + IntPtr(IntPtr &&o): i(o.i) { o.i = 0; } + ~IntPtr() { delete i; } + + int *i; +}; + +struct Foo { + Foo(IntPtr); + void bar(); + + IntPtr i; +}; + +void bar() { + IntPtr ptr; + int *i = ptr.i; + Foo f(static_cast(ptr)); + *i = 99; // no-warning +} +} // namespace ctor_argument Index: test/Analysis/temporaries.mm =================================================================== --- /dev/null +++ test/Analysis/temporaries.mm @@ -0,0 +1,23 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker core,cplusplus -verify %s + +// expected-no-diagnostics + +// Stripped down unique_ptr +struct IntPtr { + IntPtr(): i(new int) {} + IntPtr(IntPtr &&o): i(o.i) { o.i = nullptr; } + ~IntPtr() { delete i; } + + int *i; +}; + +@interface Foo {} + -(void) foo: (IntPtr)arg; +@end + +void bar(Foo *f) { + IntPtr ptr; + int *i = ptr.i; + [f foo: static_cast(ptr)]; + *i = 99; // no-warning +}