[analyzer][AnalysisOrderChecker] Display the CallEvent type in preCall/postCall
authorKirstóf Umann <dkszelethus@gmail.com>
Sun, 1 Mar 2020 16:49:44 +0000 (17:49 +0100)
committerKirstóf Umann <dkszelethus@gmail.com>
Thu, 9 Apr 2020 14:41:07 +0000 (16:41 +0200)
Exactly what it says on the tin! The included testfile demonstrates why this is
important -- for C++ dynamic memory operators, we don't always recognize custom,
or even standard-specified new/delete operators as CXXAllocatorCall or
CXXDeallocatorCall.

Differential Revision: https://reviews.llvm.org/D77391

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
clang/test/Analysis/Inputs/system-header-simulator-cxx.h
clang/test/Analysis/analyzer-config.c
clang/test/Analysis/cxx-dynamic-memory-analysis-order.cpp [new file with mode: 0644]
clang/test/Analysis/diagnostics/explicit-suppression.cpp

index 31e7e02..4315b09 100644 (file)
@@ -1276,6 +1276,30 @@ def AnalysisOrderChecker : Checker<"AnalysisOrder">,
                   Released,
                   Hide>,
     CmdLineOption<Boolean,
+                  "PreStmtCXXDeleteExpr",
+                  "",
+                  "false",
+                  Released,
+                  Hide>,
+    CmdLineOption<Boolean,
+                  "PostStmtCXXDeleteExpr",
+                  "",
+                  "false",
+                  Released,
+                  Hide>,
+    CmdLineOption<Boolean,
+                  "PreStmtCXXConstructExpr",
+                  "",
+                  "false",
+                  Released,
+                  Hide>,
+    CmdLineOption<Boolean,
+                  "PostStmtCXXConstructExpr",
+                  "",
+                  "false",
+                  Released,
+                  Hide>,
+    CmdLineOption<Boolean,
                   "PreStmtOffsetOfExpr",
                   "",
                   "false",
@@ -1306,6 +1330,12 @@ def AnalysisOrderChecker : Checker<"AnalysisOrder">,
                   Released,
                   Hide>,
     CmdLineOption<Boolean,
+                  "EndAnalysis",
+                  "",
+                  "false",
+                  Released,
+                  Hide>,
+    CmdLineOption<Boolean,
                   "NewAllocator",
                   "",
                   "false",
index 402acf6..8b4be27 100644 (file)
@@ -199,6 +199,7 @@ public:
 
   /// Returns the kind of call this is.
   virtual Kind getKind() const = 0;
+  virtual StringRef getKindAsString() const = 0;
 
   /// Returns the declaration of the function or method that will be
   /// called. May be null.
@@ -530,6 +531,9 @@ public:
   }
 
   Kind getKind() const override { return CE_Function; }
+  virtual StringRef getKindAsString() const override {
+    return "SimpleFunctionCall";
+  }
 
   static bool classof(const CallEvent *CA) {
     return CA->getKind() == CE_Function;
@@ -634,13 +638,12 @@ public:
   void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
                                     BindingsTy &Bindings) const override;
 
-  ArrayRef<ParmVarDecl*> parameters() const override;
+  ArrayRef<ParmVarDecl *> parameters() const override;
 
   Kind getKind() const override { return CE_Block; }
+  virtual StringRef getKindAsString() const override { return "BlockCall"; }
 
-  static bool classof(const CallEvent *CA) {
-    return CA->getKind() == CE_Block;
-  }
+  static bool classof(const CallEvent *CA) { return CA->getKind() == CE_Block; }
 };
 
 /// Represents a non-static C++ member function call, no matter how
@@ -712,6 +715,7 @@ public:
   RuntimeDefinition getRuntimeDefinition() const override;
 
   Kind getKind() const override { return CE_CXXMember; }
+  virtual StringRef getKindAsString() const override { return "CXXMemberCall"; }
 
   static bool classof(const CallEvent *CA) {
     return CA->getKind() == CE_CXXMember;
@@ -751,6 +755,9 @@ public:
   const Expr *getCXXThisExpr() const override;
 
   Kind getKind() const override { return CE_CXXMemberOperator; }
+  virtual StringRef getKindAsString() const override {
+    return "CXXMemberOperatorCall";
+  }
 
   static bool classof(const CallEvent *CA) {
     return CA->getKind() == CE_CXXMemberOperator;
@@ -815,6 +822,9 @@ public:
   }
 
   Kind getKind() const override { return CE_CXXDestructor; }
+  virtual StringRef getKindAsString() const override {
+    return "CXXDestructorCall";
+  }
 
   static bool classof(const CallEvent *CA) {
     return CA->getKind() == CE_CXXDestructor;
@@ -887,6 +897,9 @@ public:
   }
 
   Kind getKind() const override { return CE_CXXConstructor; }
+  virtual StringRef getKindAsString() const override {
+    return "CXXConstructorCall";
+  }
 
   static bool classof(const CallEvent *CA) {
     return CA->getKind() == CE_CXXConstructor;
@@ -964,6 +977,9 @@ public:
   }
 
   Kind getKind() const override { return CE_CXXInheritedConstructor; }
+  virtual StringRef getKindAsString() const override {
+    return "CXXInheritedConstructorCall";
+  }
 
   static bool classof(const CallEvent *CA) {
     return CA->getKind() == CE_CXXInheritedConstructor;
@@ -1020,6 +1036,9 @@ public:
   }
 
   Kind getKind() const override { return CE_CXXAllocator; }
+  virtual StringRef getKindAsString() const override {
+    return "CXXAllocatorCall";
+  }
 
   static bool classof(const CallEvent *CE) {
     return CE->getKind() == CE_CXXAllocator;
@@ -1143,6 +1162,9 @@ public:
   ArrayRef<ParmVarDecl*> parameters() const override;
 
   Kind getKind() const override { return CE_ObjCMessage; }
+  virtual StringRef getKindAsString() const override {
+    return "ObjCMethodCall";
+  }
 
   static bool classof(const CallEvent *CA) {
     return CA->getKind() == CE_ObjCMessage;
index fb1a407..f44a40d 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/Analysis/CFGStmtMap.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/ErrorHandling.h"
 
 using namespace clang;
 using namespace ento;
@@ -27,24 +28,20 @@ using namespace ento;
 namespace {
 
 class AnalysisOrderChecker
-    : public Checker<check::PreStmt<CastExpr>,
-                     check::PostStmt<CastExpr>,
-                     check::PreStmt<ArraySubscriptExpr>,
-                     check::PostStmt<ArraySubscriptExpr>,
-                     check::PreStmt<CXXNewExpr>,
-                     check::PostStmt<CXXNewExpr>,
-                     check::PreStmt<OffsetOfExpr>,
-                     check::PostStmt<OffsetOfExpr>,
-                     check::PreCall,
-                     check::PostCall,
-                     check::EndFunction,
-                     check::NewAllocator,
-                     check::Bind,
-                     check::PointerEscape,
-                     check::RegionChanges,
-                     check::LiveSymbols> {
-
-  bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const {
+    : public Checker<
+          check::PreStmt<CastExpr>, check::PostStmt<CastExpr>,
+          check::PreStmt<ArraySubscriptExpr>,
+          check::PostStmt<ArraySubscriptExpr>, check::PreStmt<CXXNewExpr>,
+          check::PostStmt<CXXNewExpr>, check::PreStmt<CXXDeleteExpr>,
+          check::PostStmt<CXXDeleteExpr>, check::PreStmt<CXXConstructExpr>,
+          check::PostStmt<CXXConstructExpr>, check::PreStmt<OffsetOfExpr>,
+          check::PostStmt<OffsetOfExpr>, check::PreCall, check::PostCall,
+          check::EndFunction, check::EndAnalysis, check::NewAllocator,
+          check::Bind, check::PointerEscape, check::RegionChanges,
+          check::LiveSymbols> {
+
+  bool isCallbackEnabled(const AnalyzerOptions &Opts,
+                         StringRef CallbackName) const {
     return Opts.getCheckerBooleanOption(this, "*") ||
            Opts.getCheckerBooleanOption(this, CallbackName);
   }
@@ -95,6 +92,26 @@ public:
       llvm::errs() << "PostStmt<CXXNewExpr>\n";
   }
 
+  void checkPreStmt(const CXXDeleteExpr *NE, CheckerContext &C) const {
+    if (isCallbackEnabled(C, "PreStmtCXXDeleteExpr"))
+      llvm::errs() << "PreStmt<CXXDeleteExpr>\n";
+  }
+
+  void checkPostStmt(const CXXDeleteExpr *NE, CheckerContext &C) const {
+    if (isCallbackEnabled(C, "PostStmtCXXDeleteExpr"))
+      llvm::errs() << "PostStmt<CXXDeleteExpr>\n";
+  }
+
+  void checkPreStmt(const CXXConstructExpr *NE, CheckerContext &C) const {
+    if (isCallbackEnabled(C, "PreStmtCXXConstructExpr"))
+      llvm::errs() << "PreStmt<CXXConstructExpr>\n";
+  }
+
+  void checkPostStmt(const CXXConstructExpr *NE, CheckerContext &C) const {
+    if (isCallbackEnabled(C, "PostStmtCXXConstructExpr"))
+      llvm::errs() << "PostStmt<CXXConstructExpr>\n";
+  }
+
   void checkPreStmt(const OffsetOfExpr *OOE, CheckerContext &C) const {
     if (isCallbackEnabled(C, "PreStmtOffsetOfExpr"))
       llvm::errs() << "PreStmt<OffsetOfExpr>\n";
@@ -110,6 +127,7 @@ public:
       llvm::errs() << "PreCall";
       if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
         llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
+      llvm::errs() << " [" << Call.getKindAsString() << ']';
       llvm::errs() << '\n';
     }
   }
@@ -119,6 +137,7 @@ public:
       llvm::errs() << "PostCall";
       if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
         llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
+      llvm::errs() << " [" << Call.getKindAsString() << ']';
       llvm::errs() << '\n';
     }
   }
@@ -140,6 +159,12 @@ public:
     }
   }
 
+  void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
+                        ExprEngine &Eng) const {
+    if (isCallbackEnabled(BR.getAnalyzerOptions(), "EndAnalysis"))
+      llvm::errs() << "EndAnalysis\n";
+  }
+
   void checkNewAllocator(const CXXNewExpr *CNE, SVal Target,
                          CheckerContext &C) const {
     if (isCallbackEnabled(C, "NewAllocator"))
index 4fc23d8..ea366d0 100644 (file)
@@ -450,8 +450,7 @@ void CallEvent::dump(raw_ostream &Out) const {
     return;
   }
 
-  // FIXME: a string representation of the kind would be nice.
-  Out << "Unknown call (type " << getKind() << ")";
+  Out << "Unknown call (type " << getKindAsString() << ")";
 }
 
 bool CallEvent::isCallStmt(const Stmt *S) {
index 25a705b..4729b04 100644 (file)
@@ -608,9 +608,10 @@ namespace std {
   };
 
   struct nothrow_t {};
-
   extern const nothrow_t nothrow;
 
+  enum class align_val_t : size_t {};
+
   // libc++'s implementation
   template <class _E>
   class initializer_list
@@ -967,10 +968,33 @@ void* operator new[](std::size_t size, const std::nothrow_t&) throw() { return s
 void operator delete(void* ptr, const std::nothrow_t&) throw() { std::free(ptr); }
 void operator delete[](void* ptr, const std::nothrow_t&) throw() { std::free(ptr); }
 #else
-void* operator new(std::size_t, const std::nothrow_t&) throw();
-void* operator new[](std::size_t, const std::nothrow_t&) throw();
-void operator delete(void*, const std::nothrow_t&) throw();
-void operator delete[](void*, const std::nothrow_t&) throw();
+// C++20 standard draft 17.6.1, from "Header <new> synopsis", but with throw()
+// instead of noexcept:
+
+void *operator new(std::size_t size);
+void *operator new(std::size_t size, std::align_val_t alignment);
+void *operator new(std::size_t size, const std::nothrow_t &) throw();
+void *operator new(std::size_t size, std::align_val_t alignment,
+                   const std::nothrow_t &) throw();
+void operator delete(void *ptr) throw();
+void operator delete(void *ptr, std::size_t size) throw();
+void operator delete(void *ptr, std::align_val_t alignment) throw();
+void operator delete(void *ptr, std::size_t size, std::align_val_t alignment) throw();
+void operator delete(void *ptr, const std::nothrow_t &)throw();
+void operator delete(void *ptr, std::align_val_t alignment,
+                     const std::nothrow_t &)throw();
+void *operator new[](std::size_t size);
+void *operator new[](std::size_t size, std::align_val_t alignment);
+void *operator new[](std::size_t size, const std::nothrow_t &) throw();
+void *operator new[](std::size_t size, std::align_val_t alignment,
+                     const std::nothrow_t &) throw();
+void operator delete[](void *ptr) throw();
+void operator delete[](void *ptr, std::size_t size) throw();
+void operator delete[](void *ptr, std::align_val_t alignment) throw();
+void operator delete[](void *ptr, std::size_t size, std::align_val_t alignment) throw();
+void operator delete[](void *ptr, const std::nothrow_t &) throw();
+void operator delete[](void *ptr, std::align_val_t alignment,
+                       const std::nothrow_t &) throw();
 #endif
 
 void* operator new (std::size_t size, void* ptr) throw() { return ptr; };
index 7069ee4..226c020 100644 (file)
 // CHECK-NEXT: deadcode.DeadStores:WarnForDeadNestedAssignments = true
 // CHECK-NEXT: debug.AnalysisOrder:* = false
 // CHECK-NEXT: debug.AnalysisOrder:Bind = false
+// CHECK-NEXT: debug.AnalysisOrder:EndAnalysis = false
 // CHECK-NEXT: debug.AnalysisOrder:EndFunction = false
 // CHECK-NEXT: debug.AnalysisOrder:LiveSymbols = false
 // CHECK-NEXT: debug.AnalysisOrder:NewAllocator = false
 // CHECK-NEXT: debug.AnalysisOrder:PointerEscape = false
 // CHECK-NEXT: debug.AnalysisOrder:PostCall = false
 // CHECK-NEXT: debug.AnalysisOrder:PostStmtArraySubscriptExpr = false
+// CHECK-NEXT: debug.AnalysisOrder:PostStmtCXXConstructExpr = false
+// CHECK-NEXT: debug.AnalysisOrder:PostStmtCXXDeleteExpr = false
 // CHECK-NEXT: debug.AnalysisOrder:PostStmtCXXNewExpr = false
 // CHECK-NEXT: debug.AnalysisOrder:PostStmtCastExpr = false
 // CHECK-NEXT: debug.AnalysisOrder:PostStmtOffsetOfExpr = false
 // CHECK-NEXT: debug.AnalysisOrder:PreCall = false
 // CHECK-NEXT: debug.AnalysisOrder:PreStmtArraySubscriptExpr = false
+// CHECK-NEXT: debug.AnalysisOrder:PreStmtCXXConstructExpr = false
+// CHECK-NEXT: debug.AnalysisOrder:PreStmtCXXDeleteExpr = false
 // CHECK-NEXT: debug.AnalysisOrder:PreStmtCXXNewExpr = false
 // CHECK-NEXT: debug.AnalysisOrder:PreStmtCastExpr = false
 // CHECK-NEXT: debug.AnalysisOrder:PreStmtOffsetOfExpr = false
 // CHECK-NEXT: unroll-loops = false
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 98
+// CHECK-NEXT: num-entries = 103
diff --git a/clang/test/Analysis/cxx-dynamic-memory-analysis-order.cpp b/clang/test/Analysis/cxx-dynamic-memory-analysis-order.cpp
new file mode 100644 (file)
index 0000000..dfb3bbf
--- /dev/null
@@ -0,0 +1,130 @@
+// RUN: %clang_analyze_cc1 -std=c++20 -fblocks -verify %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=debug.AnalysisOrder \
+// RUN:   -analyzer-config debug.AnalysisOrder:PreStmtCXXNewExpr=true \
+// RUN:   -analyzer-config debug.AnalysisOrder:PostStmtCXXNewExpr=true \
+// RUN:   -analyzer-config debug.AnalysisOrder:PreStmtCXXDeleteExpr=true \
+// RUN:   -analyzer-config debug.AnalysisOrder:PostStmtCXXDeleteExpr=true \
+// RUN:   -analyzer-config debug.AnalysisOrder:PreCall=true \
+// RUN:   -analyzer-config debug.AnalysisOrder:PostCall=true \
+// RUN:   2>&1 | FileCheck %s
+
+// expected-no-diagnostics
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void f() {
+  // C++20 standard draft 17.6.1.15:
+  // Required behavior: A call to an operator delete with a size parameter may
+  // be changed to a call to the corresponding operator delete without a size
+  // parameter, without affecting memory allocation. [ Note: A conforming
+  // implementation is for operator delete(void* ptr, size_t size) to simply
+  // call operator delete(ptr). — end note ]
+  //
+  // C++20 standard draft 17.6.1.24, about nothrow operator delete:
+  //   void operator delete(void* ptr, const std::nothrow_t&) noexcept;
+  //   void operator delete(void* ptr, std::align_val_t alignment,
+  //                        const std::nothrow_t&) noexcept;
+  // Default behavior: Calls operator delete(ptr), or operator delete(ptr,
+  // alignment), respectively.
+
+  // FIXME: All calls to operator new should be CXXAllocatorCall.
+  // FIXME: PostStmt<CXXDeleteExpr> should be present.
+  {
+    int *p = new int;
+    delete p;
+    // CHECK:      PreCall (operator new) [CXXAllocatorCall]
+    // CHECK-NEXT: PostCall (operator new) [CXXAllocatorCall]
+    // CHECK-NEXT: PreStmt<CXXNewExpr>
+    // CHECK-NEXT: PostStmt<CXXNewExpr>
+    // CHECK-NEXT: PreStmt<CXXDeleteExpr>
+
+    p = new int;
+    operator delete(p, 23542368);
+    // CHECK-NEXT: PreCall (operator new) [CXXAllocatorCall]
+    // CHECK-NEXT: PostCall (operator new) [CXXAllocatorCall]
+    // CHECK-NEXT: PreStmt<CXXNewExpr>
+    // CHECK-NEXT: PostStmt<CXXNewExpr>
+    // CHECK-NEXT: PreCall (operator delete) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete) [SimpleFunctionCall]
+
+    void *v = operator new(sizeof(int[2]), std::align_val_t(2));
+    operator delete(v, std::align_val_t(2));
+    // CHECK-NEXT: PreCall (operator new) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator new) [SimpleFunctionCall]
+    // CHECK-NEXT: PreCall (operator delete) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete) [SimpleFunctionCall]
+
+    v = operator new(sizeof(int[2]), std::align_val_t(2));
+    operator delete(v, 345345, std::align_val_t(2));
+    // CHECK-NEXT: PreCall (operator new) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator new) [SimpleFunctionCall]
+    // CHECK-NEXT: PreCall (operator delete) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete) [SimpleFunctionCall]
+
+    p = new (std::nothrow) int;
+    operator delete(p, std::nothrow);
+    // CHECK-NEXT: PreCall (operator new) [CXXAllocatorCall]
+    // CHECK-NEXT: PostCall (operator new) [CXXAllocatorCall]
+    // CHECK-NEXT: PreStmt<CXXNewExpr>
+    // CHECK-NEXT: PostStmt<CXXNewExpr>
+    // CHECK-NEXT: PreCall (operator delete) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete) [SimpleFunctionCall]
+
+    v = operator new(sizeof(int[2]), std::align_val_t(2), std::nothrow);
+    operator delete(v, std::align_val_t(2), std::nothrow);
+    // CHECK-NEXT: PreCall (operator new) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator new) [SimpleFunctionCall]
+    // CHECK-NEXT: PreCall (operator delete) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete) [SimpleFunctionCall]
+  }
+
+  {
+    int *p = new int[2];
+    delete[] p;
+    // CHECK-NEXT: PreCall (operator new[]) [CXXAllocatorCall]
+    // CHECK-NEXT: PostCall (operator new[]) [CXXAllocatorCall]
+    // CHECK-NEXT: PreStmt<CXXNewExpr>
+    // CHECK-NEXT: PostStmt<CXXNewExpr>
+    // CHECK-NEXT: PreStmt<CXXDeleteExpr>
+
+    p = new int[2];
+    operator delete[](p, 23542368);
+    // CHECK-NEXT: PreCall (operator new[]) [CXXAllocatorCall]
+    // CHECK-NEXT: PostCall (operator new[]) [CXXAllocatorCall]
+    // CHECK-NEXT: PreStmt<CXXNewExpr>
+    // CHECK-NEXT: PostStmt<CXXNewExpr>
+    // CHECK-NEXT: PreCall (operator delete[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete[]) [SimpleFunctionCall]
+
+    void *v = operator new[](sizeof(int[2]), std::align_val_t(2));
+    operator delete[](v, std::align_val_t(2));
+    // CHECK-NEXT: PreCall (operator new[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator new[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PreCall (operator delete[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete[]) [SimpleFunctionCall]
+
+    v = operator new[](sizeof(int[2]), std::align_val_t(2));
+    operator delete[](v, 345345, std::align_val_t(2));
+    // CHECK-NEXT: PreCall (operator new[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator new[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PreCall (operator delete[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete[]) [SimpleFunctionCall]
+
+    p = new (std::nothrow) int[2];
+    operator delete[](p, std::nothrow);
+    // CHECK-NEXT: PreCall (operator new[]) [CXXAllocatorCall]
+    // CHECK-NEXT: PostCall (operator new[]) [CXXAllocatorCall]
+    // CHECK-NEXT: PreStmt<CXXNewExpr>
+    // CHECK-NEXT: PostStmt<CXXNewExpr>
+    // CHECK-NEXT: PreCall (operator delete[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete[]) [SimpleFunctionCall]
+
+    v = operator new[](sizeof(int[2]), std::align_val_t(2), std::nothrow);
+    operator delete[](v, std::align_val_t(2), std::nothrow);
+    // CHECK-NEXT: PreCall (operator new[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator new[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PreCall (operator delete[]) [SimpleFunctionCall]
+    // CHECK-NEXT: PostCall (operator delete[]) [SimpleFunctionCall]
+  }
+}
index f7db7a2..2b586ad 100644 (file)
@@ -19,6 +19,6 @@ class C {
 void testCopyNull(C *I, C *E) {
   std::copy(I, E, (C *)0);
 #ifndef SUPPRESSED
-  // expected-warning@../Inputs/system-header-simulator-cxx.h:698 {{Called C++ object pointer is null}}
+  // expected-warning@../Inputs/system-header-simulator-cxx.h:699 {{Called C++ object pointer is null}}
 #endif
 }