r338425 - [CFG] [analyzer] Add stubs for constructor and message argument constructors.

Artem Dergachev via cfe-commits cfe-commits at lists.llvm.org
Tue Jul 31 12:39:37 PDT 2018


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();
+};
+
+ at interface E {}
+-(void) foo: (D) d;
+ at 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;
+};
+
+ at interface Foo {}
+  -(void) foo: (IntPtr)arg;
+ at end
+
+void bar(Foo *f) {
+  IntPtr ptr;
+  int *i = ptr.i;
+  [f foo: static_cast<IntPtr &&>(ptr)];
+  *i = 99; // no-warning
+}




More information about the cfe-commits mailing list