Skip to content

Commit 2e6e4bb

Browse files
committed
[libc++] Diagnose when nullptrs are passed to string APIs
1 parent 876174f commit 2e6e4bb

File tree

5 files changed

+72
-17
lines changed

5 files changed

+72
-17
lines changed

libcxx/include/__config

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,12 @@ typedef __char32_t char32_t;
12011201
# define _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
12021202
# endif
12031203

1204+
# if __has_feature(nullability)
1205+
# define _LIBCPP_DIAGNOSE_NONNULL _Nonnull
1206+
# else
1207+
# define _LIBCPP_DIAGNOSE_NONNULL
1208+
# endif
1209+
12041210
// TODO(LLVM 22): Remove this macro once LLVM19 support ends. __cpp_explicit_this_parameter has been set in LLVM20.
12051211
// Clang-18 has support for deducing this, but it does not set the FTM.
12061212
# if defined(__cpp_explicit_this_parameter) || (defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 1800)

libcxx/include/string

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,13 +1037,14 @@ public:
10371037
# endif // _LIBCPP_CXX03_LANG
10381038

10391039
template <__enable_if_t<__is_allocator<_Allocator>::value, int> = 0>
1040-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* __s) {
1040+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* _LIBCPP_DIAGNOSE_NONNULL __s) {
10411041
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "basic_string(const char*) detected nullptr");
10421042
__init(__s, traits_type::length(__s));
10431043
}
10441044

10451045
template <__enable_if_t<__is_allocator<_Allocator>::value, int> = 0>
1046-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* __s, const _Allocator& __a)
1046+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
1047+
basic_string(const _CharT* _LIBCPP_DIAGNOSE_NONNULL __s, const _Allocator& __a)
10471048
: __alloc_(__a) {
10481049
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "basic_string(const char*, allocator) detected nullptr");
10491050
__init(__s, traits_type::length(__s));
@@ -1214,7 +1215,8 @@ public:
12141215
return assign(__il.begin(), __il.size());
12151216
}
12161217
# endif
1217-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator=(const value_type* __s) {
1218+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
1219+
operator=(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) {
12181220
return assign(__s);
12191221
}
12201222
# if _LIBCPP_STD_VER >= 23
@@ -1332,7 +1334,8 @@ public:
13321334
return append(__sv);
13331335
}
13341336

1335-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator+=(const value_type* __s) {
1337+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
1338+
operator+=(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) {
13361339
return append(__s);
13371340
}
13381341

@@ -1373,7 +1376,7 @@ public:
13731376
append(const _Tp& __t, size_type __pos, size_type __n = npos);
13741377

13751378
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __n);
1376-
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s);
1379+
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s);
13771380
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(size_type __n, value_type __c);
13781381

13791382
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __append_default_init(size_type __n);
@@ -1531,7 +1534,7 @@ public:
15311534
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
15321535
insert(size_type __pos1, const basic_string& __str, size_type __pos2, size_type __n = npos);
15331536
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, const value_type* __s, size_type __n);
1534-
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, const value_type* __s);
1537+
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, const value_type* _LIBCPP_DIAGNOSE_NONNULL __s);
15351538
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, size_type __n, value_type __c);
15361539
_LIBCPP_CONSTEXPR_SINCE_CXX20 iterator insert(const_iterator __pos, value_type __c);
15371540

@@ -1711,7 +1714,7 @@ public:
17111714
}
17121715

17131716
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1714-
find(const value_type* __s, size_type __pos = 0) const _NOEXCEPT {
1717+
find(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = 0) const _NOEXCEPT {
17151718
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find(): received nullptr");
17161719
return std::__str_find<value_type, size_type, traits_type, npos>(
17171720
data(), size(), __s, __pos, traits_type::length(__s));
@@ -1742,7 +1745,7 @@ public:
17421745
}
17431746

17441747
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1745-
rfind(const value_type* __s, size_type __pos = npos) const _NOEXCEPT {
1748+
rfind(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = npos) const _NOEXCEPT {
17461749
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::rfind(): received nullptr");
17471750
return std::__str_rfind<value_type, size_type, traits_type, npos>(
17481751
data(), size(), __s, __pos, traits_type::length(__s));
@@ -1775,7 +1778,7 @@ public:
17751778
}
17761779

17771780
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1778-
find_first_of(const value_type* __s, size_type __pos = 0) const _NOEXCEPT {
1781+
find_first_of(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = 0) const _NOEXCEPT {
17791782
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find_first_of(): received nullptr");
17801783
return std::__str_find_first_of<value_type, size_type, traits_type, npos>(
17811784
data(), size(), __s, __pos, traits_type::length(__s));
@@ -1809,7 +1812,7 @@ public:
18091812
}
18101813

18111814
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1812-
find_last_of(const value_type* __s, size_type __pos = npos) const _NOEXCEPT {
1815+
find_last_of(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = npos) const _NOEXCEPT {
18131816
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find_last_of(): received nullptr");
18141817
return std::__str_find_last_of<value_type, size_type, traits_type, npos>(
18151818
data(), size(), __s, __pos, traits_type::length(__s));
@@ -1843,7 +1846,7 @@ public:
18431846
}
18441847

18451848
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1846-
find_first_not_of(const value_type* __s, size_type __pos = 0) const _NOEXCEPT {
1849+
find_first_not_of(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = 0) const _NOEXCEPT {
18471850
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find_first_not_of(): received nullptr");
18481851
return std::__str_find_first_not_of<value_type, size_type, traits_type, npos>(
18491852
data(), size(), __s, __pos, traits_type::length(__s));
@@ -1877,7 +1880,7 @@ public:
18771880
}
18781881

18791882
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1880-
find_last_not_of(const value_type* __s, size_type __pos = npos) const _NOEXCEPT {
1883+
find_last_not_of(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = npos) const _NOEXCEPT {
18811884
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::find_last_not_of(): received nullptr");
18821885
return std::__str_find_last_not_of<value_type, size_type, traits_type, npos>(
18831886
data(), size(), __s, __pos, traits_type::length(__s));
@@ -1925,12 +1928,13 @@ public:
19251928
return __self_view(*this).substr(__pos1, __n1).compare(__sv.substr(__pos2, __n2));
19261929
}
19271930

1928-
_LIBCPP_CONSTEXPR_SINCE_CXX20 int compare(const value_type* __s) const _NOEXCEPT {
1931+
_LIBCPP_CONSTEXPR_SINCE_CXX20 int compare(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const _NOEXCEPT {
19291932
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::compare(): received nullptr");
19301933
return compare(0, npos, __s, traits_type::length(__s));
19311934
}
19321935

1933-
_LIBCPP_CONSTEXPR_SINCE_CXX20 int compare(size_type __pos1, size_type __n1, const value_type* __s) const {
1936+
_LIBCPP_CONSTEXPR_SINCE_CXX20 int
1937+
compare(size_type __pos1, size_type __n1, const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const {
19341938
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "string::compare(): received nullptr");
19351939
return compare(__pos1, __n1, __s, traits_type::length(__s));
19361940
}
@@ -1949,7 +1953,7 @@ public:
19491953
return !empty() && _Traits::eq(front(), __c);
19501954
}
19511955

1952-
constexpr _LIBCPP_HIDE_FROM_ABI bool starts_with(const value_type* __s) const noexcept {
1956+
constexpr _LIBCPP_HIDE_FROM_ABI bool starts_with(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const noexcept {
19531957
return starts_with(__self_view(__s));
19541958
}
19551959

@@ -1963,7 +1967,7 @@ public:
19631967
return !empty() && _Traits::eq(back(), __c);
19641968
}
19651969

1966-
constexpr _LIBCPP_HIDE_FROM_ABI bool ends_with(const value_type* __s) const noexcept {
1970+
constexpr _LIBCPP_HIDE_FROM_ABI bool ends_with(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const noexcept {
19671971
return ends_with(__self_view(__s));
19681972
}
19691973
# endif
@@ -1979,7 +1983,7 @@ public:
19791983
return __self_view(typename __self_view::__assume_valid(), data(), size()).contains(__c);
19801984
}
19811985

1982-
constexpr _LIBCPP_HIDE_FROM_ABI bool contains(const value_type* __s) const {
1986+
constexpr _LIBCPP_HIDE_FROM_ABI bool contains(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const {
19831987
return __self_view(typename __self_view::__assume_valid(), data(), size()).contains(__s);
19841988
}
19851989
# endif
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03
10+
11+
// Ensure that APIs which take a CharT* (and no size for it) are diagnosing passing a nullptr to them
12+
13+
#include <string>
14+
15+
#include "test_macros.h"
16+
17+
void func() {
18+
const char* const np = nullptr;
19+
std::string str1(np); // expected-warning {{null passed}}
20+
std::string str2(np, std::allocator<char>{}); // expected-warning {{null passed}}
21+
str2 = np; // expected-warning {{null passed}}
22+
str2 += np; // expected-warning {{null passed}}
23+
str2.append(np); // expected-warning {{null passed}}
24+
str2.insert(0, np); // expected-warning {{null passed}}
25+
str2.find(np); // expected-warning {{null passed}}
26+
str2.rfind(np); // expected-warning {{null passed}}
27+
str2.find_first_of(np); // expected-warning {{null passed}}
28+
str2.find_last_of(np); // expected-warning {{null passed}}
29+
str2.find_first_not_of(np); // expected-warning {{null passed}}
30+
str2.find_last_not_of(np); // expected-warning {{null passed}}
31+
str2.compare(np); // expected-warning {{null passed}}
32+
str2.compare(0, 0, np); // expected-warning {{null passed}}
33+
34+
#if TEST_STD_VER >= 20
35+
str2.starts_with(np); // expected-warning {{null passed}}
36+
str2.ends_with(np); // expected-warning {{null passed}}
37+
#endif
38+
#if TEST_STD_VER >= 23
39+
str2.contains(np); // expected-warning {{null passed}}
40+
#endif
41+
}

libcxx/utils/libcxx/test/params.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@
7272

7373
# This doesn't make sense in real code, but we have to test it because the standard requires us to not break
7474
"-Wno-self-move",
75+
76+
# We're not annotating all the APIs, since that's a lot of annotations compared to how many we actually care about
77+
"-Wno-nullability-completeness",
7578
]
7679

7780
_allStandards = ["c++03", "c++11", "c++14", "c++17", "c++20", "c++23", "c++26"]

runtimes/cmake/Modules/WarningFlags.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ function(cxx_add_warning_flags target enable_werror enable_pedantic)
2525
-Wformat-nonliteral
2626
-Wzero-length-array
2727
-Wdeprecated-redundant-constexpr-static-def
28+
-Wno-nullability-completeness
2829
)
2930

3031
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")

0 commit comments

Comments
 (0)