diff --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h index 8292750919427..d98595030e6a5 100644 --- a/libcxx/include/__string/char_traits.h +++ b/libcxx/include/__string/char_traits.h @@ -94,16 +94,17 @@ struct char_traits { } // 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 { } } - 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 { 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 @@ -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(~eof()) : __c; } }; @@ -235,18 +245,19 @@ struct __char_traits_base { #if _LIBCPP_HAS_WIDE_CHARACTERS template <> struct char_traits : __char_traits_base(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); } @@ -257,16 +268,16 @@ struct char_traits : __char_traits_base struct char_traits : __char_traits_base(EOF)> { - static _LIBCPP_HIDE_FROM_ABI constexpr int + [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI constexpr int 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 : __char_traits_base struct char_traits : __char_traits_base(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 : __char_traits_base::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::compare(const char_type* __s1, const char_type* __s2, siz return 0; } -inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t char_traits::length(const char_type* __s) _NOEXCEPT { +[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t +char_traits::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::length(const template <> struct char_traits : __char_traits_base(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 : __char_traits_base::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::compare(const char_type* __s1, const char_type* __s2, siz return 0; } -inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t char_traits::length(const char_type* __s) _NOEXCEPT { +[[__nodiscard__]] inline _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t +char_traits::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..190f49202a782 --- /dev/null +++ b/libcxx/test/libcxx/strings/char.traits/nodiscard.verify.cpp @@ -0,0 +1,162 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include "test_macros.h" + +void test_char() { + typedef char char_t; + typedef std::char_traits traits; + typedef typename traits::int_type int_t; + + const char_t buf[1] = {}; + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::eq(char_t(), char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::lt(char_t(), char_t()); + + // expected-warning@+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@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::find(buf, 0, char_t()); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::not_eof(char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_char_type(int_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_int_type(char_t()); + // expected-warning@+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}} +} + +#ifndef TEST_HAS_NO_CHAR8_T +void test_char8_t() { + typedef char8_t char_t; + typedef std::char_traits traits; + typedef typename traits::int_type int_t; + + const char_t buf[1] = {}; + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::eq(char_t(), char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::lt(char_t(), char_t()); + + // expected-warning@+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@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::find(buf, 0, char_t()); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::not_eof(char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_char_type(int_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_int_type(char_t()); + // expected-warning@+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}} +} +#endif + +void test_char16_t() { + typedef char16_t char_t; + typedef std::char_traits traits; + typedef typename traits::int_type int_t; + + const char_t buf[1] = {}; + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::eq(char_t(), char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::lt(char_t(), char_t()); + + // expected-warning@+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@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::find(buf, 0, char_t()); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::not_eof(char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_char_type(int_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_int_type(char_t()); + // expected-warning@+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_char32_t() { + typedef char32_t char_t; + typedef std::char_traits traits; + typedef typename traits::int_type int_t; + + const char_t buf[1] = {}; + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::eq(char_t(), char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::lt(char_t(), char_t()); + + // expected-warning@+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@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::find(buf, 0, char_t()); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::not_eof(char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_char_type(int_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_int_type(char_t()); + // expected-warning@+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}} +} + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS +void test_wchar_t() { + typedef wchar_t char_t; + typedef std::char_traits traits; + typedef typename traits::int_type int_t; + + const char_t buf[1] = {}; + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::eq(char_t(), char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::lt(char_t(), char_t()); + + // expected-warning@+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@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::find(buf, 0, char_t()); + + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::not_eof(char_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_char_type(int_t()); + // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} + traits::to_int_type(char_t()); + // expected-warning@+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}} +} +#endif