diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1276,6 +1276,30 @@ Released, Hide>, CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, CmdLineOption, + CmdLineOptiongetKind() == CE_Function; @@ -634,13 +638,12 @@ void getInitialStackFrameContents(const StackFrameContext *CalleeCtx, BindingsTy &Bindings) const override; - ArrayRef parameters() const override; + ArrayRef 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 @@ 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 @@ 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 @@ } 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 @@ } 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 @@ } 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 @@ } 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 @@ ArrayRef 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; diff --git a/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp @@ -13,13 +13,14 @@ // //===----------------------------------------------------------------------===// -#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 @@ namespace { class AnalysisOrderChecker - : public Checker, - check::PostStmt, - check::PreStmt, - check::PostStmt, - check::PreStmt, - check::PostStmt, - check::PreStmt, - check::PostStmt, - 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, check::PostStmt, + check::PreStmt, + check::PostStmt, check::PreStmt, + check::PostStmt, check::PreStmt, + check::PostStmt, check::PreStmt, + check::PostStmt, check::PreStmt, + check::PostStmt, 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 @@ llvm::errs() << "PostStmt\n"; } + void checkPreStmt(const CXXDeleteExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PreStmtCXXDeleteExpr")) + llvm::errs() << "PreStmt\n"; + } + + void checkPostStmt(const CXXDeleteExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PostStmtCXXDeleteExpr")) + llvm::errs() << "PostStmt\n"; + } + + void checkPreStmt(const CXXConstructExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PreStmtCXXConstructExpr")) + llvm::errs() << "PreStmt\n"; + } + + void checkPostStmt(const CXXConstructExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PostStmtCXXConstructExpr")) + llvm::errs() << "PostStmt\n"; + } + void checkPreStmt(const OffsetOfExpr *OOE, CheckerContext &C) const { if (isCallbackEnabled(C, "PreStmtOffsetOfExpr")) llvm::errs() << "PreStmt\n"; @@ -110,6 +127,7 @@ llvm::errs() << "PreCall"; if (const NamedDecl *ND = dyn_cast_or_null(Call.getDecl())) llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')'; + llvm::errs() << " [" << Call.getKindAsString() << ']'; llvm::errs() << '\n'; } } @@ -119,6 +137,7 @@ llvm::errs() << "PostCall"; if (const NamedDecl *ND = dyn_cast_or_null(Call.getDecl())) llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')'; + llvm::errs() << " [" << Call.getKindAsString() << ']'; llvm::errs() << '\n'; } } @@ -140,6 +159,12 @@ } } + 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")) diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -450,8 +450,7 @@ 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) { diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h --- a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h +++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h @@ -608,9 +608,10 @@ }; struct nothrow_t {}; - extern const nothrow_t nothrow; + enum class align_val_t : size_t {}; + // libc++'s implementation template class initializer_list @@ -967,10 +968,33 @@ 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 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; }; diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -37,17 +37,22 @@ // 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 @@ -101,4 +106,4 @@ // 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 --- /dev/null +++ b/clang/test/Analysis/cxx-dynamic-memory-analysis-order.cpp @@ -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 should be present. + { + int *p = new int; + delete p; + // CHECK: PreCall (operator new) [CXXAllocatorCall] + // CHECK-NEXT: PostCall (operator new) [CXXAllocatorCall] + // CHECK-NEXT: PreStmt + // CHECK-NEXT: PostStmt + // CHECK-NEXT: PreStmt + + p = new int; + operator delete(p, 23542368); + // CHECK-NEXT: PreCall (operator new) [CXXAllocatorCall] + // CHECK-NEXT: PostCall (operator new) [CXXAllocatorCall] + // CHECK-NEXT: PreStmt + // CHECK-NEXT: PostStmt + // 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 + // CHECK-NEXT: PostStmt + // 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 + // CHECK-NEXT: PostStmt + // CHECK-NEXT: PreStmt + + p = new int[2]; + operator delete[](p, 23542368); + // CHECK-NEXT: PreCall (operator new[]) [CXXAllocatorCall] + // CHECK-NEXT: PostCall (operator new[]) [CXXAllocatorCall] + // CHECK-NEXT: PreStmt + // CHECK-NEXT: PostStmt + // 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 + // CHECK-NEXT: PostStmt + // 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] + } +} diff --git a/clang/test/Analysis/diagnostics/explicit-suppression.cpp b/clang/test/Analysis/diagnostics/explicit-suppression.cpp --- a/clang/test/Analysis/diagnostics/explicit-suppression.cpp +++ b/clang/test/Analysis/diagnostics/explicit-suppression.cpp @@ -19,6 +19,6 @@ 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 }