Skip to content

[libc++] Diagnose passing null pointers to a bunch of APIs #148585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcxx/.clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
14 changes: 14 additions & 0 deletions libcxx/include/__config
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions libcxx/include/__memory/construct_at.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ _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) {
_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)...);
}
Expand Down Expand Up @@ -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
Expand Down
14 changes: 9 additions & 5 deletions libcxx/include/print
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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");
}

Expand All @@ -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);
}

Expand All @@ -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);
}

Expand Down
45 changes: 30 additions & 15 deletions libcxx/include/string
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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&
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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>;
Expand Down
Loading
Loading