[libcxx-commits] [libcxx] [libc++] Diagnose passing null pointers to a bunch of APIs (PR #148585)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jul 14 01:34:10 PDT 2025
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/148585
None
>From 33ad3dd9ebdf92fd974861d47575e14b7d2ec2f9 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 14 Jul 2025 10:30:29 +0200
Subject: [PATCH] [libc++] Diagnose passing null pointers to a bunch of APIs
---
libcxx/.clang-format | 1 +
libcxx/include/__config | 13 +++++
libcxx/include/__memory/construct_at.h | 8 +--
libcxx/include/print | 14 +++--
libcxx/include/string | 45 ++++++++++------
libcxx/include/string_view | 52 +++++++++++--------
.../specialized.algorithms/nonnull.verify.cpp | 22 ++++++++
.../print.fun/nonnull.verify.cpp | 21 ++++++++
.../strings/basic.string/nonnull.verify.cpp | 22 +++++++-
.../strings/string.view/nonnull.verify.cpp | 50 ++++++++++++++++++
10 files changed, 202 insertions(+), 46 deletions(-)
create mode 100644 libcxx/test/libcxx/algorithms/specialized.algorithms/nonnull.verify.cpp
create mode 100644 libcxx/test/libcxx/input.output/iostream.format/print.fun/nonnull.verify.cpp
create mode 100644 libcxx/test/libcxx/strings/string.view/nonnull.verify.cpp
diff --git a/libcxx/.clang-format b/libcxx/.clang-format
index f372ac9619997..9557b955cd72c 100644
--- a/libcxx/.clang-format
+++ b/libcxx/.clang-format
@@ -33,6 +33,7 @@ AttributeMacros: [
'_LIBCPP_DEPRECATED_IN_CXX20',
'_LIBCPP_DEPRECATED_IN_CXX23',
'_LIBCPP_DEPRECATED',
+ '_LIBCPP_DIAGNOSE_NULLPTR_IF',
'_LIBCPP_EXCLUDE_FROM_EXPLICIT_INSTANTIATION',
'_LIBCPP_EXPORTED_FROM_ABI',
'_LIBCPP_EXTERN_TEMPLATE_TYPE_VIS',
diff --git a/libcxx/include/__config b/libcxx/include/__config
index d940461c30234..741b2f475599e 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -1095,6 +1095,19 @@ typedef __char32_t char32_t;
# define _LIBCPP_DIAGNOSE_WARNING(...)
# endif
+# if __has_attribute(__diagnose_if__) && (!defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2010)
+# define _LIBCPP_DIAGNOSE_IF(...) __attribute__((__diagnose_if__(__VA_ARGS__)))
+# else
+# define _LIBCPP_DIAGNOSE_IF(...)
+# endif
+
+# define _LIBCPP_DIAGNOSE_NULLPTR_IF(condition, condition_description) \
+ _LIBCPP_DIAGNOSE_IF( \
+ condition, \
+ "null passed to calle that requires a non-null argument" condition_description, \
+ "warning", \
+ "nonnull")
+
# if __has_cpp_attribute(_Clang::__lifetimebound__)
# define _LIBCPP_LIFETIMEBOUND [[_Clang::__lifetimebound__]]
# else
diff --git a/libcxx/include/__memory/construct_at.h b/libcxx/include/__memory/construct_at.h
index b64e64b5a29b0..a59e2f3bd5ea5 100644
--- a/libcxx/include/__memory/construct_at.h
+++ b/libcxx/include/__memory/construct_at.h
@@ -32,8 +32,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 20
-template <class _Tp, class... _Args, class = decltype(::new(std::declval<void*>()) _Tp(std::declval<_Args>()...))>
-_LIBCPP_HIDE_FROM_ABI constexpr _Tp* construct_at(_Tp* __location, _Args&&... __args) {
+template <class _Tp, class... _Args, class = decltype(::new (std::declval<void*>()) _Tp(std::declval<_Args>()...))>
+_LIBCPP_HIDE_FROM_ABI constexpr _Tp* construct_at(_Tp* _LIBCPP_DIAGNOSE_NULLPTR __location, _Args&&... __args) {
_LIBCPP_ASSERT_NON_NULL(__location != nullptr, "null pointer given to construct_at");
return ::new (static_cast<void*>(__location)) _Tp(std::forward<_Args>(__args)...);
}
@@ -73,13 +73,13 @@ _LIBCPP_HIDE_FROM_ABI constexpr void __destroy_at(_Tp* __loc) {
#if _LIBCPP_STD_VER >= 17
template <class _Tp, enable_if_t<!is_array_v<_Tp>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void destroy_at(_Tp* __loc) {
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void destroy_at(_Tp* _LIBCPP_DIAGNOSE_NULLPTR __loc) {
std::__destroy_at(__loc);
}
# if _LIBCPP_STD_VER >= 20
template <class _Tp, enable_if_t<is_array_v<_Tp>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr void destroy_at(_Tp* __loc) {
+_LIBCPP_HIDE_FROM_ABI constexpr void destroy_at(_Tp* _LIBCPP_DIAGNOSE_NULLPTR __loc) {
std::__destroy_at(__loc);
}
# endif
diff --git a/libcxx/include/print b/libcxx/include/print
index be05d30e0147f..0ff314c22dcd9 100644
--- a/libcxx/include/print
+++ b/libcxx/include/print
@@ -329,7 +329,8 @@ __vprint_unicode([[maybe_unused]] FILE* __stream,
} // namespace __print
template <class... _Args>
-_LIBCPP_HIDE_FROM_ABI void print(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
+_LIBCPP_HIDE_FROM_ABI void
+print(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, format_string<_Args...> __fmt, _Args&&... __args) {
# if _LIBCPP_HAS_UNICODE
if constexpr (__print::__use_unicode_execution_charset)
__print::__vprint_unicode(__stream, __fmt.get(), std::make_format_args(__args...), false);
@@ -346,7 +347,8 @@ _LIBCPP_HIDE_FROM_ABI void print(format_string<_Args...> __fmt, _Args&&... __arg
}
template <class... _Args>
-_LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt, _Args&&... __args) {
+_LIBCPP_HIDE_FROM_ABI void
+println(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, format_string<_Args...> __fmt, _Args&&... __args) {
# if _LIBCPP_HAS_UNICODE
// Note the wording in the Standard is inefficient. The output of
// std::format is a std::string which is then copied. This solution
@@ -361,7 +363,7 @@ _LIBCPP_HIDE_FROM_ABI void println(FILE* __stream, format_string<_Args...> __fmt
}
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void println(FILE* __stream) {
+_LIBCPP_HIDE_FROM_ABI inline void println(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream) {
std::print(__stream, "\n");
}
@@ -377,7 +379,8 @@ _LIBCPP_HIDE_FROM_ABI void println(format_string<_Args...> __fmt, _Args&&... __a
# if _LIBCPP_HAS_UNICODE
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(FILE* __stream, string_view __fmt, format_args __args) {
+_LIBCPP_HIDE_FROM_ABI inline void
+vprint_unicode(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, string_view __fmt, format_args __args) {
__print::__vprint_unicode(__stream, __fmt, __args, false);
}
@@ -389,7 +392,8 @@ _LIBCPP_HIDE_FROM_ABI inline void vprint_unicode(string_view __fmt, format_args
# endif // _LIBCPP_HAS_UNICODE
template <class = void> // TODO PRINT template or availability markup fires too eagerly (http://llvm.org/PR61563).
-_LIBCPP_HIDE_FROM_ABI inline void vprint_nonunicode(FILE* __stream, string_view __fmt, format_args __args) {
+_LIBCPP_HIDE_FROM_ABI inline void
+vprint_nonunicode(FILE* _LIBCPP_DIAGNOSE_NULLPTR __stream, string_view __fmt, format_args __args) {
__print::__vprint_nonunicode(__stream, __fmt, __args, false);
}
diff --git a/libcxx/include/string b/libcxx/include/string
index 514dd91c7c172..88c0ed40e3bf7 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1065,13 +1065,15 @@ public:
basic_string(nullptr_t) = delete;
# endif
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* __s, size_type __n) {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* __s, size_type __n)
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "basic_string(const char*, n) detected nullptr");
__init(__s, __n);
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
basic_string(const _CharT* __s, size_type __n, const _Allocator& __a)
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero")
: __alloc_(__a) {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "basic_string(const char*, n, allocator) detected nullptr");
__init(__s, __n);
@@ -1394,7 +1396,8 @@ public:
return append(__sv.data() + __pos, std::min(__n, __sz - __pos));
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __n);
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __n)
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(size_type __n, value_type __c);
@@ -1521,8 +1524,9 @@ public:
return assign(__sv.data() + __pos, std::min(__n, __sz - __pos));
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* __s, size_type __n);
- _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* __s);
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* __s, size_type __n)
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& assign(size_type __n, value_type __c);
template <class _InputIterator, __enable_if_t<__has_exactly_input_iterator_category<_InputIterator>::value, int> = 0>
@@ -1593,7 +1597,8 @@ public:
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
insert(size_type __pos1, const basic_string& __str, size_type __pos2, size_type __n = npos);
- _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, const value_type* __s, size_type __n);
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, const value_type* __s, size_type __n)
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero");
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, size_type __n, value_type __c);
_LIBCPP_CONSTEXPR_SINCE_CXX20 iterator insert(const_iterator __pos, value_type __c);
@@ -1673,8 +1678,10 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
- replace(size_type __pos, size_type __n1, const value_type* __s, size_type __n2);
- _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& replace(size_type __pos, size_type __n1, const value_type* __s);
+ replace(size_type __pos, size_type __n1, const value_type* __s, size_type __n2)
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n2 != 0 && __s == nullptr, " if n2 is not zero");
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
+ replace(size_type __pos, size_type __n1, const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s);
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& replace(size_type __pos, size_type __n1, size_type __n2, value_type __c);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
@@ -1783,7 +1790,8 @@ public:
return std::__str_find<value_type, size_type, traits_type, npos>(data(), size(), __sv.data(), __pos, __sv.size());
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type find(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type find(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::find(): received nullptr");
return std::__str_find<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
@@ -1814,7 +1822,8 @@ public:
return std::__str_rfind<value_type, size_type, traits_type, npos>(data(), size(), __sv.data(), __pos, __sv.size());
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type rfind(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type rfind(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::rfind(): received nullptr");
return std::__str_rfind<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
@@ -1847,7 +1856,8 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
- find_first_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ find_first_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::find_first_of(): received nullptr");
return std::__str_find_first_of<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
@@ -1881,7 +1891,8 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
- find_last_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ find_last_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::find_last_of(): received nullptr");
return std::__str_find_last_of<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
@@ -1915,7 +1926,8 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
- find_first_not_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ find_first_not_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::find_first_not_of(): received nullptr");
return std::__str_find_first_not_of<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
@@ -1949,7 +1961,8 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
- find_last_not_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ find_last_not_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string::find_last_not_of(): received nullptr");
return std::__str_find_last_not_of<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
@@ -2026,7 +2039,8 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX20 int
- compare(size_type __pos1, size_type __n1, const value_type* __s, size_type __n2) const;
+ compare(size_type __pos1, size_type __n1, const value_type* __s, size_type __n2) const
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n2 != 0 && __s == nullptr, " if n2 is not zero");
// starts_with
@@ -3564,7 +3578,8 @@ operator==(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
template <class _CharT, class _Traits, class _Allocator>
inline _LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI bool
-operator==(const basic_string<_CharT, _Traits, _Allocator>& __lhs, const _CharT* __rhs) _NOEXCEPT {
+operator==(const basic_string<_CharT, _Traits, _Allocator>& __lhs,
+ const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __rhs) _NOEXCEPT {
_LIBCPP_ASSERT_NON_NULL(__rhs != nullptr, "operator==(basic_string, char*): received nullptr");
using _String = basic_string<_CharT, _Traits, _Allocator>;
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index 861187c0640e1..f86b2722aca6c 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -318,8 +318,8 @@ public:
_LIBCPP_HIDE_FROM_ABI basic_string_view& operator=(const basic_string_view&) _NOEXCEPT = default;
_LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI basic_string_view(const _CharT* __s, size_type __len) _NOEXCEPT
- : __data_(__s),
- __size_(__len) {
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__len != 0 && __s == nullptr, " if len is not zero")
+ : __data_(__s), __size_(__len) {
# if _LIBCPP_STD_VER >= 14
// Allocations must fit in `ptrdiff_t` for pointer arithmetic to work. If `__len` exceeds it, the input
// range could not have been valid. Most likely the caller underflowed some arithmetic and inadvertently
@@ -352,7 +352,7 @@ public:
: __data_(ranges::data(__r)), __size_(ranges::size(__r)) {}
# endif // _LIBCPP_STD_VER >= 23
- _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI basic_string_view(const _CharT* __s)
+ _LIBCPP_CONSTEXPR _LIBCPP_HIDE_FROM_ABI basic_string_view(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s)
: __data_(__s), __size_(std::__char_traits_length_checked<_Traits>(__s)) {}
# if _LIBCPP_STD_VER >= 23
@@ -483,17 +483,19 @@ public:
return substr(__pos1, __n1).compare(__sv.substr(__pos2, __n2));
}
- _LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI int compare(const _CharT* __s) const _NOEXCEPT {
+ _LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI int
+ compare(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s) const _NOEXCEPT {
return compare(basic_string_view(__s));
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI int
- compare(size_type __pos1, size_type __n1, const _CharT* __s) const {
+ compare(size_type __pos1, size_type __n1, const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s) const {
return substr(__pos1, __n1).compare(basic_string_view(__s));
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI int
- compare(size_type __pos1, size_type __n1, const _CharT* __s, size_type __n2) const {
+ compare(size_type __pos1, size_type __n1, const _CharT* __s, size_type __n2) const
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n2 != 0 && __s == nullptr, " if n2 is not zero") {
return substr(__pos1, __n1).compare(basic_string_view(__s, __n2));
}
@@ -509,13 +511,14 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ find(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string_view::find(): received nullptr");
return std::__str_find<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find(const _CharT* __s, size_type __pos = 0) const _NOEXCEPT {
+ find(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = 0) const _NOEXCEPT {
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string_view::find(): received nullptr");
return std::__str_find<value_type, size_type, traits_type, npos>(
data(), size(), __s, __pos, traits_type::length(__s));
@@ -534,13 +537,14 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- rfind(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ rfind(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string_view::rfind(): received nullptr");
return std::__str_rfind<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- rfind(const _CharT* __s, size_type __pos = npos) const _NOEXCEPT {
+ rfind(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = npos) const _NOEXCEPT {
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string_view::rfind(): received nullptr");
return std::__str_rfind<value_type, size_type, traits_type, npos>(
data(), size(), __s, __pos, traits_type::length(__s));
@@ -560,13 +564,14 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find_first_of(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ find_first_of(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string_view::find_first_of(): received nullptr");
return std::__str_find_first_of<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find_first_of(const _CharT* __s, size_type __pos = 0) const _NOEXCEPT {
+ find_first_of(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = 0) const _NOEXCEPT {
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string_view::find_first_of(): received nullptr");
return std::__str_find_first_of<value_type, size_type, traits_type, npos>(
data(), size(), __s, __pos, traits_type::length(__s));
@@ -586,13 +591,14 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find_last_of(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ find_last_of(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string_view::find_last_of(): received nullptr");
return std::__str_find_last_of<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find_last_of(const _CharT* __s, size_type __pos = npos) const _NOEXCEPT {
+ find_last_of(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = npos) const _NOEXCEPT {
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string_view::find_last_of(): received nullptr");
return std::__str_find_last_of<value_type, size_type, traits_type, npos>(
data(), size(), __s, __pos, traits_type::length(__s));
@@ -613,13 +619,14 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find_first_not_of(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ find_first_not_of(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string_view::find_first_not_of(): received nullptr");
return std::__str_find_first_not_of<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find_first_not_of(const _CharT* __s, size_type __pos = 0) const _NOEXCEPT {
+ find_first_not_of(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = 0) const _NOEXCEPT {
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string_view::find_first_not_of(): received nullptr");
return std::__str_find_first_not_of<value_type, size_type, traits_type, npos>(
data(), size(), __s, __pos, traits_type::length(__s));
@@ -640,13 +647,14 @@ public:
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find_last_not_of(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT {
+ find_last_not_of(const _CharT* __s, size_type __pos, size_type __n) const _NOEXCEPT
+ _LIBCPP_DIAGNOSE_NULLPTR_IF(__n != 0 && __s == nullptr, " if n is not zero") {
_LIBCPP_ASSERT_NON_NULL(__n == 0 || __s != nullptr, "string_view::find_last_not_of(): received nullptr");
return std::__str_find_last_not_of<value_type, size_type, traits_type, npos>(data(), size(), __s, __pos, __n);
}
_LIBCPP_CONSTEXPR_SINCE_CXX14 _LIBCPP_HIDE_FROM_ABI size_type
- find_last_not_of(const _CharT* __s, size_type __pos = npos) const _NOEXCEPT {
+ find_last_not_of(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = npos) const _NOEXCEPT {
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string_view::find_last_not_of(): received nullptr");
return std::__str_find_last_not_of<value_type, size_type, traits_type, npos>(
data(), size(), __s, __pos, traits_type::length(__s));
@@ -661,7 +669,7 @@ public:
return !empty() && _Traits::eq(front(), __c);
}
- constexpr _LIBCPP_HIDE_FROM_ABI bool starts_with(const value_type* __s) const noexcept {
+ constexpr _LIBCPP_HIDE_FROM_ABI bool starts_with(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s) const noexcept {
return starts_with(basic_string_view(__s));
}
@@ -673,7 +681,7 @@ public:
return !empty() && _Traits::eq(back(), __c);
}
- constexpr _LIBCPP_HIDE_FROM_ABI bool ends_with(const value_type* __s) const noexcept {
+ constexpr _LIBCPP_HIDE_FROM_ABI bool ends_with(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s) const noexcept {
return ends_with(basic_string_view(__s));
}
# endif
@@ -683,7 +691,9 @@ public:
constexpr _LIBCPP_HIDE_FROM_ABI bool contains(value_type __c) const noexcept { return find(__c) != npos; }
- constexpr _LIBCPP_HIDE_FROM_ABI bool contains(const value_type* __s) const { return find(__s) != npos; }
+ constexpr _LIBCPP_HIDE_FROM_ABI bool contains(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s) const {
+ return find(__s) != npos;
+ }
# endif
private:
diff --git a/libcxx/test/libcxx/algorithms/specialized.algorithms/nonnull.verify.cpp b/libcxx/test/libcxx/algorithms/specialized.algorithms/nonnull.verify.cpp
new file mode 100644
index 0000000000000..4bad90eb66257
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/specialized.algorithms/nonnull.verify.cpp
@@ -0,0 +1,22 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// Ensure that APIs which take a pointer are diagnosing passing a nullptr to them
+
+#include <memory>
+
+void func() {
+ using Arr = int[1];
+ int* const np = nullptr;
+ Arr* const np2 = nullptr;
+ std::construct_at(np); // expected-warning {{null passed}}
+ std::destroy_at(np); // expected-warning {{null passed}}
+ std::destroy_at(np2); // expected-warning {{null passed}}
+}
diff --git a/libcxx/test/libcxx/input.output/iostream.format/print.fun/nonnull.verify.cpp b/libcxx/test/libcxx/input.output/iostream.format/print.fun/nonnull.verify.cpp
new file mode 100644
index 0000000000000..b4446750a4455
--- /dev/null
+++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/nonnull.verify.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// Ensure that APIs which take a FILE* are diagnosing passing a nullptr to them
+
+#include <print>
+
+void func() {
+ std::print(nullptr, ""); // expected-warning {{null passed}}
+ std::println(nullptr, ""); // expected-warning {{null passed}}
+ std::println(nullptr); // expected-warning {{null passed}}
+ std::vprint_unicode(nullptr, "", std::make_format_args()); // expected-warning {{null passed}}
+ std::vprint_nonunicode(nullptr, "", std::make_format_args()); // expected-warning {{null passed}}
+}
diff --git a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
index d61896277afd4..c6a22d8d2f984 100644
--- a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp
@@ -8,7 +8,7 @@
// UNSUPPORTED: c++03
-// Ensure that APIs which take a CharT* (and no size for it) are diagnosing passing a nullptr to them
+// Ensure that APIs which take a CharT* are diagnosing passing a nullptr to them
#include <string>
@@ -20,6 +20,7 @@ void func() {
std::string str2(np, std::allocator<char>{}); // expected-warning {{null passed}}
str2 = np; // expected-warning {{null passed}}
str2 += np; // expected-warning {{null passed}}
+ str2.assign(np); // expected-warning {{null passed}}
str2.append(np); // expected-warning {{null passed}}
str2.insert(0, np); // expected-warning {{null passed}}
str2.find(np); // expected-warning {{null passed}}
@@ -30,6 +31,8 @@ void func() {
str2.find_last_not_of(np); // expected-warning {{null passed}}
str2.compare(np); // expected-warning {{null passed}}
str2.compare(0, 0, np); // expected-warning {{null passed}}
+ str2.replace(0, 0, np); // expected-warning {{null passed}}
+ (void)(str2 == np); // expected-warning {{null passed}}
#if TEST_STD_VER >= 20
str2.starts_with(np); // expected-warning {{null passed}}
@@ -38,4 +41,21 @@ void func() {
#if TEST_STD_VER >= 23
str2.contains(np); // expected-warning {{null passed}}
#endif
+
+ // clang-format off
+ // These diagnostics are issued via diagnose_if, so we want to check the full description
+ std::string str3(nullptr, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ std::string str4(nullptr, 1, std::allocator<char>{}); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.find(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.rfind(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.find_first_of(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.find_last_of(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.find_first_not_of(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.find_last_not_of(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.compare(0, 0, nullptr, 1); // expected-warning {{null passed to calle that requires a non-null argument if n2 is not zero}}
+ str4.assign(nullptr, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.append(nullptr, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.insert(0, nullptr, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str4.replace(0, 0, nullptr, 1); // expected-warning {{null passed to calle that requires a non-null argument if n2 is not zero}}
+ // clang-format on
}
diff --git a/libcxx/test/libcxx/strings/string.view/nonnull.verify.cpp b/libcxx/test/libcxx/strings/string.view/nonnull.verify.cpp
new file mode 100644
index 0000000000000..0266bac4da6a9
--- /dev/null
+++ b/libcxx/test/libcxx/strings/string.view/nonnull.verify.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03
+
+// Ensure that APIs which take a CharT* are diagnosing passing a nullptr to them
+
+#include <string_view>
+
+#include "test_macros.h"
+
+void func() {
+ const char* const np = nullptr;
+ std::string_view str1(np); // expected-warning {{null passed}}
+ str1 = np; // expected-warning {{null passed}}
+ str1.find(np); // expected-warning {{null passed}}
+ str1.rfind(np); // expected-warning {{null passed}}
+ str1.find_first_of(np); // expected-warning {{null passed}}
+ str1.find_last_of(np); // expected-warning {{null passed}}
+ str1.find_first_not_of(np); // expected-warning {{null passed}}
+ str1.find_last_not_of(np); // expected-warning {{null passed}}
+ str1.compare(np); // expected-warning {{null passed}}
+ str1.compare(0, 0, np); // expected-warning {{null passed}}
+ (void)(str1 == np); // expected-warning {{null passed}}
+
+#if TEST_STD_VER >= 20
+ str1.starts_with(np); // expected-warning {{null passed}}
+ str1.ends_with(np); // expected-warning {{null passed}}
+#endif
+#if TEST_STD_VER >= 23
+ str1.contains(np); // expected-warning {{null passed}}
+#endif
+
+ // clang-format off
+ // These diagnostics are issued via diagnose_if, so we want to check the full description
+ std::string_view str2(nullptr, 1); // expected-warning {{null passed to calle that requires a non-null argument if len is not zero}}
+ str2.find(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str2.rfind(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str2.find_first_of(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str2.find_last_of(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str2.find_first_not_of(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str2.find_last_not_of(nullptr, 0, 1); // expected-warning {{null passed to calle that requires a non-null argument if n is not zero}}
+ str2.compare(0, 0, nullptr, 1); // expected-warning {{null passed to calle that requires a non-null argument if n2 is not zero}}
+ // clang-format on
+}
More information about the libcxx-commits
mailing list