Skip to content
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
75 changes: 44 additions & 31 deletions libcxx/include/__string/char_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
Expand All @@ -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>
Expand All @@ -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;
}

Expand All @@ -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;
}
};
Expand All @@ -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);
}
Expand All @@ -257,16 +268,16 @@ 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
[[nodiscard]] static _LIBCPP_HIDE_FROM_ABI constexpr int
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for review: Non-uglified nodiscard occurrences can only appear since C++20 currently, while other uglified __nodiscard__ occurrences can appear in C++03.

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);
}
Expand All @@ -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);
Expand All @@ -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))
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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))
Expand All @@ -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;
Expand Down
162 changes: 162 additions & 0 deletions libcxx/test/libcxx/strings/char.traits/nodiscard.verify.cpp
Original file line number Diff line number Diff line change
@@ -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 <string>

#include "test_macros.h"

void test_char() {
typedef char char_t;
typedef std::char_traits<char_t> 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() {
Comment on lines +44 to +45
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for review: I attempted to use a function template to handle five character types. However, the count of expected warnings varies in different modes, and it's infeasible to write the different counts in comments. As a result, I chose to repeat the function body 5 times.

typedef char8_t char_t;
typedef std::char_traits<char_t> 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<char_t> 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<char_t> 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<char_t> 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
Loading