diff --git a/libcxx/include/__config b/libcxx/include/__config index 53900e40655ef..c8224b07a6b81 100644 --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -1201,6 +1201,12 @@ typedef __char32_t char32_t; # define _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK # endif +# if __has_feature(nullability) +# define _LIBCPP_DIAGNOSE_NULLPTR _Nonnull +# else +# define _LIBCPP_DIAGNOSE_NULLPTR +# endif + // TODO(LLVM 22): Remove this macro once LLVM19 support ends. __cpp_explicit_this_parameter has been set in LLVM20. // Clang-18 has support for deducing this, but it does not set the FTM. # if defined(__cpp_explicit_this_parameter) || (defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 1800) diff --git a/libcxx/include/string b/libcxx/include/string index 7b86bbe5bffa7..08e65caa33b56 100644 --- a/libcxx/include/string +++ b/libcxx/include/string @@ -1037,13 +1037,14 @@ public: # endif // _LIBCPP_CXX03_LANG template <__enable_if_t<__is_allocator<_Allocator>::value, int> = 0> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* __s) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s) { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "basic_string(const char*) detected nullptr"); __init(__s, traits_type::length(__s)); } template <__enable_if_t<__is_allocator<_Allocator>::value, int> = 0> - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* __s, const _Allocator& __a) + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 + basic_string(const _CharT* _LIBCPP_DIAGNOSE_NULLPTR __s, const _Allocator& __a) : __alloc_(__a) { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "basic_string(const char*, allocator) detected nullptr"); __init(__s, traits_type::length(__s)); @@ -1214,7 +1215,8 @@ public: return assign(__il.begin(), __il.size()); } # endif - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator=(const value_type* __s) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& + operator=(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s) { return assign(__s); } # if _LIBCPP_STD_VER >= 23 @@ -1340,7 +1342,8 @@ public: return append(__sv); } - _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator+=(const value_type* __s) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& + operator+=(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s) { return append(__s); } @@ -1381,7 +1384,7 @@ public: append(const _Tp& __t, size_type __pos, size_type __n = npos); _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); + _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); _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __append_default_init(size_type __n); @@ -1539,7 +1542,7 @@ 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); + _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); @@ -1719,7 +1722,7 @@ public: } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type - find(const value_type* __s, size_type __pos = 0) const _NOEXCEPT { + find(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = 0) const _NOEXCEPT { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find(): received nullptr"); return std::__str_find( data(), size(), __s, __pos, traits_type::length(__s)); @@ -1750,7 +1753,7 @@ public: } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type - rfind(const value_type* __s, size_type __pos = npos) const _NOEXCEPT { + rfind(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = npos) const _NOEXCEPT { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::rfind(): received nullptr"); return std::__str_rfind( data(), size(), __s, __pos, traits_type::length(__s)); @@ -1783,7 +1786,7 @@ public: } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type - find_first_of(const value_type* __s, size_type __pos = 0) const _NOEXCEPT { + find_first_of(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = 0) const _NOEXCEPT { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find_first_of(): received nullptr"); return std::__str_find_first_of( data(), size(), __s, __pos, traits_type::length(__s)); @@ -1817,7 +1820,7 @@ public: } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type - find_last_of(const value_type* __s, size_type __pos = npos) const _NOEXCEPT { + find_last_of(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = npos) const _NOEXCEPT { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find_last_of(): received nullptr"); return std::__str_find_last_of( data(), size(), __s, __pos, traits_type::length(__s)); @@ -1851,7 +1854,7 @@ public: } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type - find_first_not_of(const value_type* __s, size_type __pos = 0) const _NOEXCEPT { + find_first_not_of(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = 0) const _NOEXCEPT { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find_first_not_of(): received nullptr"); return std::__str_find_first_not_of( data(), size(), __s, __pos, traits_type::length(__s)); @@ -1885,7 +1888,7 @@ public: } _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type - find_last_not_of(const value_type* __s, size_type __pos = npos) const _NOEXCEPT { + find_last_not_of(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s, size_type __pos = npos) const _NOEXCEPT { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find_last_not_of(): received nullptr"); return std::__str_find_last_not_of( data(), size(), __s, __pos, traits_type::length(__s)); @@ -1933,12 +1936,13 @@ public: return __self_view(*this).substr(__pos1, __n1).compare(__sv.substr(__pos2, __n2)); } - _LIBCPP_CONSTEXPR_SINCE_CXX20 int compare(const value_type* __s) const _NOEXCEPT { + _LIBCPP_CONSTEXPR_SINCE_CXX20 int compare(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s) const _NOEXCEPT { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::compare(): received nullptr"); return compare(0, npos, __s, traits_type::length(__s)); } - _LIBCPP_CONSTEXPR_SINCE_CXX20 int compare(size_type __pos1, size_type __n1, const value_type* __s) const { + _LIBCPP_CONSTEXPR_SINCE_CXX20 int + compare(size_type __pos1, size_type __n1, const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s) const { _LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::compare(): received nullptr"); return compare(__pos1, __n1, __s, traits_type::length(__s)); } @@ -1957,7 +1961,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(__self_view(__s)); } @@ -1971,7 +1975,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(__self_view(__s)); } # endif @@ -1987,7 +1991,7 @@ public: return __self_view(typename __self_view::__assume_valid(), data(), size()).contains(__c); } - constexpr _LIBCPP_HIDE_FROM_ABI bool contains(const value_type* __s) const { + constexpr _LIBCPP_HIDE_FROM_ABI bool contains(const value_type* _LIBCPP_DIAGNOSE_NULLPTR __s) const { return __self_view(typename __self_view::__assume_valid(), data(), size()).contains(__s); } # endif diff --git a/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp new file mode 100644 index 0000000000000..d61896277afd4 --- /dev/null +++ b/libcxx/test/libcxx/strings/basic.string/nonnull.verify.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// 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* (and no size for it) are diagnosing passing a nullptr to them + +#include + +#include "test_macros.h" + +void func() { + const char* const np = nullptr; + std::string str1(np); // expected-warning {{null passed}} + std::string str2(np, std::allocator{}); // expected-warning {{null passed}} + str2 = np; // expected-warning {{null passed}} + str2 += 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}} + str2.rfind(np); // expected-warning {{null passed}} + str2.find_first_of(np); // expected-warning {{null passed}} + str2.find_last_of(np); // expected-warning {{null passed}} + str2.find_first_not_of(np); // expected-warning {{null passed}} + 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}} + +#if TEST_STD_VER >= 20 + str2.starts_with(np); // expected-warning {{null passed}} + str2.ends_with(np); // expected-warning {{null passed}} +#endif +#if TEST_STD_VER >= 23 + str2.contains(np); // expected-warning {{null passed}} +#endif +} diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py index 7dba39bc5db04..93dc3a8be3f08 100644 --- a/libcxx/utils/libcxx/test/params.py +++ b/libcxx/utils/libcxx/test/params.py @@ -72,6 +72,9 @@ # This doesn't make sense in real code, but we have to test it because the standard requires us to not break "-Wno-self-move", + + # We're not annotating all the APIs, since that's a lot of annotations compared to how many we actually care about + "-Wno-nullability-completeness", ] _allStandards = ["c++03", "c++11", "c++14", "c++17", "c++20", "c++23", "c++26"] diff --git a/runtimes/cmake/Modules/WarningFlags.cmake b/runtimes/cmake/Modules/WarningFlags.cmake index d17bf92389d0b..43ef76561cc54 100644 --- a/runtimes/cmake/Modules/WarningFlags.cmake +++ b/runtimes/cmake/Modules/WarningFlags.cmake @@ -25,6 +25,7 @@ function(cxx_add_warning_flags target enable_werror enable_pedantic) -Wformat-nonliteral -Wzero-length-array -Wdeprecated-redundant-constexpr-static-def + -Wno-nullability-completeness ) if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")