[libcxx-commits] [libcxx] [libc++][char_traits] Applied `[[nodiscard]]` (PR #172244)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Dec 14 19:28:46 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: A. Jiang (frederick-vs-ja)
<details>
<summary>Changes</summary>
`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.
- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/char.traits
---
Full diff: https://github.com/llvm/llvm-project/pull/172244.diff
2 Files Affected:
- (modified) libcxx/include/__string/char_traits.h (+44-31)
- (added) libcxx/test/libcxx/strings/char.traits/nodiscard.verify.cpp (+54)
``````````diff
diff --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h
index 8292750919427..4bf5b34e75894 100644
--- a/libcxx/include/__string/char_traits.h
+++ b/libcxx/include/__string/char_traits.h
@@ -94,16 +94,17 @@ struct char_traits<char> {
}
// TODO: Make this _LIBCPP_HIDE_FROM_ABI
- static inline _LIBCPP_HIDDEN _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT {
+ [[__nodiscard__]] static inline _LIBCPP_HIDDEN _LIBCPP_CONSTEXPR bool eq(char_type __c1, char_type __c2) _NOEXCEPT {
return __c1 == __c2;
}
- static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT {
+ [[__nodiscard__]] static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+ lt(char_type __c1, char_type __c2) _NOEXCEPT {
return (unsigned char)__c1 < (unsigned char)__c2;
}
// __constexpr_memcmp requires a trivially lexicographically comparable type, but char is not when char is a signed
// type
- static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int
+ [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int
compare(const char_type* __lhs, const char_type* __rhs, size_t __count) _NOEXCEPT {
if (__libcpp_is_constant_evaluated()) {
#ifdef _LIBCPP_COMPILER_CLANG_BASED
@@ -126,11 +127,12 @@ struct char_traits<char> {
}
}
- static inline _LIBCPP_HIDE_FROM_ABI size_t _LIBCPP_CONSTEXPR_SINCE_CXX17 length(const char_type* __s) _NOEXCEPT {
+ [[__nodiscard__]] static inline _LIBCPP_HIDE_FROM_ABI size_t _LIBCPP_CONSTEXPR_SINCE_CXX17
+ length(const char_type* __s) _NOEXCEPT {
return std::__constexpr_strlen(__s);
}
- static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
+ [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
return std::__constexpr_memchr(__s, __a, __n);
}
@@ -154,19 +156,24 @@ struct char_traits<char> {
return __s;
}
- static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT {
+ [[__nodiscard__]] static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT {
return eq_int_type(__c, eof()) ? ~eof() : __c;
}
- static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT {
+ [[__nodiscard__]] static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR char_type
+ to_char_type(int_type __c) _NOEXCEPT {
return char_type(__c);
}
- static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT {
+ [[__nodiscard__]] static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int_type
+ to_int_type(char_type __c) _NOEXCEPT {
return int_type((unsigned char)__c);
}
- static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT {
+ [[__nodiscard__]] static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool
+ eq_int_type(int_type __c1, int_type __c2) _NOEXCEPT {
return __c1 == __c2;
}
- static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT { return int_type(EOF); }
+ [[__nodiscard__]] static inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT {
+ return int_type(EOF);
+ }
};
template <class _CharT, class _IntT, _IntT _EOFVal>
@@ -187,11 +194,11 @@ struct __char_traits_base {
__lhs = __rhs;
}
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR bool eq(char_type __lhs, char_type __rhs) _NOEXCEPT {
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR bool eq(char_type __lhs, char_type __rhs) _NOEXCEPT {
return __lhs == __rhs;
}
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR bool lt(char_type __lhs, char_type __rhs) _NOEXCEPT {
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR bool lt(char_type __lhs, char_type __rhs) _NOEXCEPT {
return __lhs < __rhs;
}
@@ -213,19 +220,22 @@ struct __char_traits_base {
return __str;
}
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT {
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR char_type to_char_type(int_type __c) _NOEXCEPT {
return char_type(__c);
}
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT { return int_type(__c); }
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int_type to_int_type(char_type __c) _NOEXCEPT {
+ return int_type(__c);
+ }
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR bool eq_int_type(int_type __lhs, int_type __rhs) _NOEXCEPT {
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR bool
+ eq_int_type(int_type __lhs, int_type __rhs) _NOEXCEPT {
return __lhs == __rhs;
}
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT { return _EOFVal; }
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT { return _EOFVal; }
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT {
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR int_type not_eof(int_type __c) _NOEXCEPT {
return eq_int_type(__c, eof()) ? static_cast<int_type>(~eof()) : __c;
}
};
@@ -235,18 +245,19 @@ struct __char_traits_base {
#if _LIBCPP_HAS_WIDE_CHARACTERS
template <>
struct char_traits<wchar_t> : __char_traits_base<wchar_t, wint_t, static_cast<wint_t>(WEOF)> {
- static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int
+ [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int
compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
if (__n == 0)
return 0;
return std::__constexpr_wmemcmp(__s1, __s2, __n);
}
- static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t length(const char_type* __s) _NOEXCEPT {
+ [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t
+ length(const char_type* __s) _NOEXCEPT {
return std::__constexpr_wcslen(__s);
}
- static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
+ [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
return std::__constexpr_wmemchr(__s, __a, __n);
}
@@ -258,15 +269,15 @@ struct char_traits<wchar_t> : __char_traits_base<wchar_t, wint_t, static_cast<wi
template <>
struct char_traits<char8_t> : __char_traits_base<char8_t, unsigned int, static_cast<unsigned int>(EOF)> {
static _LIBCPP_HIDE_FROM_ABI constexpr int
- compare(const char_type* __s1, const char_type* __s2, size_t __n) noexcept {
+ [[__nodiscard__]] compare(const char_type* __s1, const char_type* __s2, size_t __n) noexcept {
return std::__constexpr_memcmp(__s1, __s2, __element_count(__n));
}
- static _LIBCPP_HIDE_FROM_ABI constexpr size_t length(const char_type* __str) noexcept {
+ [[__nodiscard__]] static _LIBCPP_HIDE_FROM_ABI constexpr size_t length(const char_type* __str) noexcept {
return std::__constexpr_strlen(__str);
}
- _LIBCPP_HIDE_FROM_ABI static constexpr const char_type*
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static constexpr const char_type*
find(const char_type* __s, size_t __n, const char_type& __a) noexcept {
return std::__constexpr_memchr(__s, __a, __n);
}
@@ -276,11 +287,11 @@ struct char_traits<char8_t> : __char_traits_base<char8_t, unsigned int, static_c
template <>
struct char_traits<char16_t> : __char_traits_base<char16_t, uint_least16_t, static_cast<uint_least16_t>(0xFFFF)> {
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 int
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 int
compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t length(const char_type* __s) _NOEXCEPT;
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
__identity __proj;
const char_type* __match = std::__find(__s, __s + __n, __a, __proj);
@@ -290,7 +301,7 @@ struct char_traits<char16_t> : __char_traits_base<char16_t, uint_least16_t, stat
}
};
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17 int
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX17 int
char_traits<char16_t>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
for (; __n; --__n, ++__s1, ++__s2) {
if (lt(*__s1, *__s2))
@@ -301,7 +312,8 @@ char_traits<char16_t>::compare(const char_type* __s1, const char_type* __s2, siz
return 0;
}
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t char_traits<char16_t>::length(const char_type* __s) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t
+char_traits<char16_t>::length(const char_type* __s) _NOEXCEPT {
size_t __len = 0;
for (; !eq(*__s, char_type(0)); ++__s)
++__len;
@@ -310,11 +322,11 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t char_traits<char16_t>::length(const
template <>
struct char_traits<char32_t> : __char_traits_base<char32_t, uint_least32_t, static_cast<uint_least32_t>(0xFFFFFFFF)> {
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 int
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 int
compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
_LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t length(const char_type* __s) _NOEXCEPT;
- _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX17 const char_type*
find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
__identity __proj;
const char_type* __match = std::__find(__s, __s + __n, __a, __proj);
@@ -324,7 +336,7 @@ struct char_traits<char32_t> : __char_traits_base<char32_t, uint_least32_t, stat
}
};
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17 int
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX17 int
char_traits<char32_t>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
for (; __n; --__n, ++__s1, ++__s2) {
if (lt(*__s1, *__s2))
@@ -335,7 +347,8 @@ char_traits<char32_t>::compare(const char_type* __s1, const char_type* __s2, siz
return 0;
}
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t char_traits<char32_t>::length(const char_type* __s) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t
+char_traits<char32_t>::length(const char_type* __s) _NOEXCEPT {
size_t __len = 0;
for (; !eq(*__s, char_type(0)); ++__s)
++__len;
diff --git a/libcxx/test/libcxx/strings/char.traits/nodiscard.verify.cpp b/libcxx/test/libcxx/strings/char.traits/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..b443b105bb464
--- /dev/null
+++ b/libcxx/test/libcxx/strings/char.traits/nodiscard.verify.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// Check that functions are marked [[nodiscard]]
+
+#include <string>
+
+#include "test_macros.h"
+
+template <class CharT>
+void test() {
+ typedef std::char_traits<CharT> traits;
+ typedef typename traits::int_type int_t;
+
+ const CharT buf[1] = {};
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ traits::eq(CharT(), CharT());
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ traits::lt(CharT(), CharT());
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ traits::compare(buf, buf, 0);
+ traits::length(buf); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ traits::find(buf, 0, CharT());
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ traits::not_eof(CharT());
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ traits::to_char_type(int_t());
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ traits::to_int_type(CharT());
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ traits::eq_int_type(int_t(), int_t());
+ traits::eof(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
+
+void test() {
+ test<char>();
+#ifndef TEST_HAS_NO_CHAR8_T
+ test<char8_t>();
+#endif
+ test<char16_t>();
+ test<char32_t>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test<wchar_t>();
+#endif
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/172244
More information about the libcxx-commits
mailing list