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 ee06abfba7a08..d82af6557a075 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -1090,6 +1090,20 @@ typedef __char32_t char32_t; # define _LIBCPP_DIAGNOSE_WARNING(...) # endif +# if __has_attribute(__diagnose_if__) && !defined(_LIBCPP_APPLE_CLANG_VER) && \ + (!defined(_LIBCPP_CLANG_VER) || _LIBCPP_CLANG_VER >= 2001) +# 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 callee 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..658269158d945 100644 --- a/libcxx/include/__memory/construct_at.h +++ b/libcxx/include/__memory/construct_at.h @@ -33,7 +33,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD #if _LIBCPP_STD_VER >= 20 template ()) _Tp(std::declval<_Args>()...))> -_LIBCPP_HIDE_FROM_ABI constexpr _Tp* construct_at(_Tp* __location, _Args&&... __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(__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 , 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 , 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 -_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 -_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 // 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 // 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 // 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 ::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(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(data(), size(), __s, __pos, __n); } @@ -1814,7 +1822,8 @@ public: return std::__str_rfind(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(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(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(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(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(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 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(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( 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(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( 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(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( 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(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( 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(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( 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(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( 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..4d720fb0c8459 --- /dev/null +++ b/libcxx/test/libcxx/algorithms/specialized.algorithms/nonnull.verify.cpp @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Ensure that APIs which take a pointer are diagnosing passing a nullptr to them + +#include + +#include "test_macros.h" + +void func() { + using Arr = int[1]; + int* const np = nullptr; + +#if TEST_STD_VER >= 20 + Arr* const np2 = nullptr; + std::construct_at(np); // expected-warning {{null passed}} + std::destroy_at(np2); // expected-warning {{null passed}} +#endif + + std::destroy_at(np); // 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..afa0bca11ca91 --- /dev/null +++ b/libcxx/test/libcxx/input.output/iostream.format/print.fun/nonnull.verify.cpp @@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// 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: libcpp-has-no-unicode + +// 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 + +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..f428c49fd05f4 100644 --- a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp +++ b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp @@ -8,7 +8,10 @@ // 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 + +// Clang 19 and AppleClang don't have diagnose_if with diagnostic flags +// UNSUPPORTED: clang-19, apple-clang-17 #include @@ -20,6 +23,7 @@ void func() { std::string str2(np, std::allocator{}); // 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 +34,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 +44,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 callee that requires a non-null argument if n is not zero}} + std::string str4(nullptr, 1, std::allocator{}); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.find(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.rfind(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.find_first_of(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.find_last_of(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.find_first_not_of(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.find_last_not_of(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.compare(0, 0, nullptr, 1); // expected-warning {{null passed to callee that requires a non-null argument if n2 is not zero}} + str4.assign(nullptr, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.append(nullptr, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.insert(0, nullptr, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str4.replace(0, 0, nullptr, 1); // expected-warning {{null passed to callee that requires a non-null argument if n2 is not zero}} + // clang-format on } diff --git a/libcxx/test/libcxx/strings/string.view/assert.ctor.pointer.pass.cpp b/libcxx/test/libcxx/strings/string.view/assert.ctor.pointer.pass.cpp index 1810ec1ca8ac9..f358b5efd0df2 100644 --- a/libcxx/test/libcxx/strings/string.view/assert.ctor.pointer.pass.cpp +++ b/libcxx/test/libcxx/strings/string.view/assert.ctor.pointer.pass.cpp @@ -14,6 +14,9 @@ // Construct a string_view from a null pointer // constexpr basic_string_view( const CharT* s ); +// We're testing for assertions here, so let's not diagnose the misuses at compile time +// ADDITIONAL_COMPILE_FLAGS: -Wno-nonnull + #include #include "check_assertion.h" 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..316c9828e0de5 --- /dev/null +++ b/libcxx/test/libcxx/strings/string.view/nonnull.verify.cpp @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Clang 19 and AppleClang don't have diagnose_if with diagnostic flags +// UNSUPPORTED: clang-19, apple-clang-17 + +#include + +#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 callee that requires a non-null argument if len is not zero}} + str2.find(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str2.rfind(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str2.find_first_of(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str2.find_last_of(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str2.find_first_not_of(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str2.find_last_not_of(nullptr, 0, 1); // expected-warning {{null passed to callee that requires a non-null argument if n is not zero}} + str2.compare(0, 0, nullptr, 1); // expected-warning {{null passed to callee that requires a non-null argument if n2 is not zero}} + // clang-format on +}