Skip to content

Commit b49e718

Browse files
committed
[libc++] Diagnose when nullptrs are passed to string APIs
1 parent 4435b7d commit b49e718

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
@@ -1222,6 +1222,12 @@ typedef __char32_t char32_t;
12221222
# define _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
12231223
# endif
12241224

1225+
# if __has_feature(nullability)
1226+
# define _LIBCPP_DIAGNOSE_NONNULL _Nonnull
1227+
# else
1228+
# define _LIBCPP_DIAGNOSE_NONNULL
1229+
# endif
1230+
12251231
// Clang-18 has support for deducing this, but it does not set the FTM.
12261232
# if defined(__cpp_explicit_this_parameter) || (defined(_LIBCPP_CLANG_VER) && _LIBCPP_CLANG_VER >= 1800)
12271233
# define _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER 1

libcxx/include/string

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

10811081
template <__enable_if_t<__is_allocator<_Allocator>::value, int> = 0>
1082-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* __s) {
1082+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* _LIBCPP_DIAGNOSE_NONNULL __s) {
10831083
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "basic_string(const char*) detected nullptr");
10841084
__init(__s, traits_type::length(__s));
10851085
}
10861086

10871087
template <__enable_if_t<__is_allocator<_Allocator>::value, int> = 0>
1088-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string(const _CharT* __s, const _Allocator& __a)
1088+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
1089+
basic_string(const _CharT* _LIBCPP_DIAGNOSE_NONNULL __s, const _Allocator& __a)
10891090
: __alloc_(__a) {
10901091
_LIBCPP_ASSERT_NON_NULL(__s != nullptr, "basic_string(const char*, allocator) detected nullptr");
10911092
__init(__s, traits_type::length(__s));
@@ -1256,7 +1257,8 @@ public:
12561257
return assign(__il.begin(), __il.size());
12571258
}
12581259
# endif
1259-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator=(const value_type* __s) {
1260+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
1261+
operator=(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) {
12601262
return assign(__s);
12611263
}
12621264
# if _LIBCPP_STD_VER >= 23
@@ -1374,7 +1376,8 @@ public:
13741376
return append(__sv);
13751377
}
13761378

1377-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& operator+=(const value_type* __s) {
1379+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
1380+
operator+=(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) {
13781381
return append(__s);
13791382
}
13801383

@@ -1415,7 +1418,7 @@ public:
14151418
append(const _Tp& __t, size_type __pos, size_type __n = npos);
14161419

14171420
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s, size_type __n);
1418-
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* __s);
1421+
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s);
14191422
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& append(size_type __n, value_type __c);
14201423

14211424
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __append_default_init(size_type __n);
@@ -1573,7 +1576,7 @@ public:
15731576
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
15741577
insert(size_type __pos1, const basic_string& __str, size_type __pos2, size_type __n = npos);
15751578
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, const value_type* __s, size_type __n);
1576-
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, const value_type* __s);
1579+
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, const value_type* _LIBCPP_DIAGNOSE_NONNULL __s);
15771580
_LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string& insert(size_type __pos, size_type __n, value_type __c);
15781581
_LIBCPP_CONSTEXPR_SINCE_CXX20 iterator insert(const_iterator __pos, value_type __c);
15791582

@@ -1739,7 +1742,7 @@ public:
17391742

17401743
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type find(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT;
17411744
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1742-
find(const value_type* __s, size_type __pos = 0) const _NOEXCEPT;
1745+
find(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = 0) const _NOEXCEPT;
17431746
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type find(value_type __c, size_type __pos = 0) const _NOEXCEPT;
17441747

17451748
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
@@ -1751,7 +1754,7 @@ public:
17511754

17521755
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type rfind(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT;
17531756
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1754-
rfind(const value_type* __s, size_type __pos = npos) const _NOEXCEPT;
1757+
rfind(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = npos) const _NOEXCEPT;
17551758
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type rfind(value_type __c, size_type __pos = npos) const _NOEXCEPT;
17561759

17571760
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
@@ -1764,7 +1767,7 @@ public:
17641767
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
17651768
find_first_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT;
17661769
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1767-
find_first_of(const value_type* __s, size_type __pos = 0) const _NOEXCEPT;
1770+
find_first_of(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = 0) const _NOEXCEPT;
17681771
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
17691772
find_first_of(value_type __c, size_type __pos = 0) const _NOEXCEPT;
17701773

@@ -1778,7 +1781,7 @@ public:
17781781
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
17791782
find_last_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT;
17801783
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1781-
find_last_of(const value_type* __s, size_type __pos = npos) const _NOEXCEPT;
1784+
find_last_of(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = npos) const _NOEXCEPT;
17821785
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
17831786
find_last_of(value_type __c, size_type __pos = npos) const _NOEXCEPT;
17841787

@@ -1792,7 +1795,7 @@ public:
17921795
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
17931796
find_first_not_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT;
17941797
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1795-
find_first_not_of(const value_type* __s, size_type __pos = 0) const _NOEXCEPT;
1798+
find_first_not_of(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = 0) const _NOEXCEPT;
17961799
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
17971800
find_first_not_of(value_type __c, size_type __pos = 0) const _NOEXCEPT;
17981801

@@ -1806,7 +1809,7 @@ public:
18061809
_LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
18071810
find_last_not_of(const value_type* __s, size_type __pos, size_type __n) const _NOEXCEPT;
18081811
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
1809-
find_last_not_of(const value_type* __s, size_type __pos = npos) const _NOEXCEPT;
1812+
find_last_not_of(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s, size_type __pos = npos) const _NOEXCEPT;
18101813
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 size_type
18111814
find_last_not_of(value_type __c, size_type __pos = npos) const _NOEXCEPT;
18121815

@@ -1832,8 +1835,9 @@ public:
18321835
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 int
18331836
compare(size_type __pos1, size_type __n1, const _Tp& __t, size_type __pos2, size_type __n2 = npos) const;
18341837

1835-
_LIBCPP_CONSTEXPR_SINCE_CXX20 int compare(const value_type* __s) const _NOEXCEPT;
1836-
_LIBCPP_CONSTEXPR_SINCE_CXX20 int compare(size_type __pos1, size_type __n1, const value_type* __s) const;
1838+
_LIBCPP_CONSTEXPR_SINCE_CXX20 int compare(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const _NOEXCEPT;
1839+
_LIBCPP_CONSTEXPR_SINCE_CXX20 int
1840+
compare(size_type __pos1, size_type __n1, const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const;
18371841
_LIBCPP_CONSTEXPR_SINCE_CXX20 int
18381842
compare(size_type __pos1, size_type __n1, const value_type* __s, size_type __n2) const;
18391843

@@ -1846,7 +1850,7 @@ public:
18461850
return !empty() && _Traits::eq(front(), __c);
18471851
}
18481852

1849-
constexpr _LIBCPP_HIDE_FROM_ABI bool starts_with(const value_type* __s) const noexcept {
1853+
constexpr _LIBCPP_HIDE_FROM_ABI bool starts_with(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const noexcept {
18501854
return starts_with(__self_view(__s));
18511855
}
18521856

@@ -1858,7 +1862,7 @@ public:
18581862
return !empty() && _Traits::eq(back(), __c);
18591863
}
18601864

1861-
constexpr _LIBCPP_HIDE_FROM_ABI bool ends_with(const value_type* __s) const noexcept {
1865+
constexpr _LIBCPP_HIDE_FROM_ABI bool ends_with(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const noexcept {
18621866
return ends_with(__self_view(__s));
18631867
}
18641868
# endif
@@ -1872,7 +1876,7 @@ public:
18721876
return __self_view(typename __self_view::__assume_valid(), data(), size()).contains(__c);
18731877
}
18741878

1875-
constexpr _LIBCPP_HIDE_FROM_ABI bool contains(const value_type* __s) const {
1879+
constexpr _LIBCPP_HIDE_FROM_ABI bool contains(const value_type* _LIBCPP_DIAGNOSE_NONNULL __s) const {
18761880
return __self_view(typename __self_view::__assume_valid(), data(), size()).contains(__s);
18771881
}
18781882
# 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
@@ -71,6 +71,9 @@
7171

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

7679
_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)