Author: dergachev Date: Tue Jul 31 12:39:37 2018 New Revision: 338425 URL: http://llvm.org/viewvc/llvm-project?rev=338425&view=rev Log: [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 Added: cfe/trunk/test/Analysis/cfg-rich-constructors.mm cfe/trunk/test/Analysis/temporaries.mm Modified: cfe/trunk/lib/Analysis/CFG.cpp cfe/trunk/lib/Analysis/ConstructionContext.cpp cfe/trunk/test/Analysis/cfg-rich-constructors.cpp cfe/trunk/test/Analysis/temporaries.cpp Modified: cfe/trunk/lib/Analysis/CFG.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CFG.cpp?rev=338425&r1=338424&r2=338425&view=diff ============================================================================== --- cfe/trunk/lib/Analysis/CFG.cpp (original) +++ cfe/trunk/lib/Analysis/CFG.cpp Tue Jul 31 12:39:37 2018 @@ -569,6 +569,7 @@ private: 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 @@ private: 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 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, Ad 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 @@ CFGBlock *CFGBuilder::VisitCallExpr(Call 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 @@ CFGBlock *CFGBuilder::VisitObjCAtThrowSt 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::VisitCXXBindTempor 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); Modified: cfe/trunk/lib/Analysis/ConstructionContext.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/ConstructionContext.cpp?rev=338425&r1=338424&r2=338425&view=diff ============================================================================== --- cfe/trunk/lib/Analysis/ConstructionContext.cpp (original) +++ cfe/trunk/lib/Analysis/ConstructionContext.cpp Tue Jul 31 12:39:37 2018 @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/ConstructionContext.h" +#include "clang/AST/ExprObjC.h" using namespace clang; @@ -111,7 +112,9 @@ const ConstructionContext *ConstructionC 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 @@ const ConstructionContext *ConstructionC 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()) { Modified: cfe/trunk/test/Analysis/cfg-rich-constructors.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cfg-rich-constructors.cpp?rev=338425&r1=338424&r2=338425&view=diff ============================================================================== --- cfe/trunk/test/Analysis/cfg-rich-constructors.cpp (original) +++ cfe/trunk/test/Analysis/cfg-rich-constructors.cpp Tue Jul 31 12:39:37 2018 @@ -817,6 +817,11 @@ public: ~D(); }; +class E { +public: + E(D d); +}; + void useC(C c); void useCByReference(const C &c); void useD(D d); @@ -880,6 +885,32 @@ void passArgumentWithDestructor() { 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 namespace copy_elision_with_extra_arguments { Added: cfe/trunk/test/Analysis/cfg-rich-constructors.mm URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/cfg-rich-constructors.mm?rev=338425&view=auto ============================================================================== --- cfe/trunk/test/Analysis/cfg-rich-constructors.mm (added) +++ cfe/trunk/test/Analysis/cfg-rich-constructors.mm Tue Jul 31 12:39:37 2018 @@ -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()]; +} Modified: cfe/trunk/test/Analysis/temporaries.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temporaries.cpp?rev=338425&r1=338424&r2=338425&view=diff ============================================================================== --- cfe/trunk/test/Analysis/temporaries.cpp (original) +++ cfe/trunk/test/Analysis/temporaries.cpp Tue Jul 31 12:39:37 2018 @@ -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 @@ C &&foo2(); 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<int> +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<IntPtr &&>(ptr)); + *i = 99; // no-warning +} +} // namespace ctor_argument Added: cfe/trunk/test/Analysis/temporaries.mm URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/temporaries.mm?rev=338425&view=auto ============================================================================== --- cfe/trunk/test/Analysis/temporaries.mm (added) +++ cfe/trunk/test/Analysis/temporaries.mm Tue Jul 31 12:39:37 2018 @@ -0,0 +1,23 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker core,cplusplus -verify %s + +// expected-no-diagnostics + +// Stripped down unique_ptr<int> +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<IntPtr &&>(ptr)]; + *i = 99; // no-warning +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits