From ca0fed2621af3bcb59b2e243f73c0cf188295213 Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Thu, 28 May 2026 14:35:22 +0200 Subject: [PATCH 01/12] clang-format improvements - Add a clang-format linter check to the PR pipeline - Apply clang-format to files where the linter initially failed - Remove `CommentPragmas` from `.clang-format` - Remove all `// clang-format off` and `// NO-FORMAT` as they are not needed - Remove a commented out `GSL_SUPPRESS` --- .clang-format | 1 - .github/workflows/clang-format.yml | 26 +++++++++++ include/gsl/algorithm | 4 +- include/gsl/assert | 4 +- include/gsl/byte | 2 - include/gsl/narrow | 24 +++++----- include/gsl/span | 71 +++++++++++------------------- include/gsl/util | 20 +++------ tests/byte_tests.cpp | 4 +- tests/constexpr_notnull_tests.cpp | 17 +++---- tests/notnull_tests.cpp | 32 ++++---------- tests/owner_tests.cpp | 5 +-- tests/pointers_tests.cpp | 6 ++- tests/span_compatibility_tests.cpp | 8 ++-- tests/span_tests.cpp | 10 +++-- tests/strict_notnull_tests.cpp | 23 +++------- tests/utils_tests.cpp | 6 +-- 17 files changed, 114 insertions(+), 149 deletions(-) create mode 100644 .github/workflows/clang-format.yml diff --git a/.clang-format b/.clang-format index c12d3bf29..b958ccb02 100644 --- a/.clang-format +++ b/.clang-format @@ -31,4 +31,3 @@ AlignConsecutiveAssignments: false AlignTrailingComments: true SpaceAfterCStyleCast: true -CommentPragmas: '^ NO-FORMAT:' diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 000000000..76daf33e6 --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,26 @@ +name: Code Formatting + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + clang-format: + name: Run clang-format + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Runs clang-format over the repository codebase + - name: Check format + uses: jidicula/clang-format-action@v4.11.0 + with: + clang-format-version: '19' + check-path: 'include tests' diff --git a/include/gsl/algorithm b/include/gsl/algorithm index c67f35abd..209890689 100644 --- a/include/gsl/algorithm +++ b/include/gsl/algorithm @@ -48,9 +48,7 @@ void copy(span src, span "Source range is longer than target range"); Expects(dest.size() >= src.size()); - // clang-format off - GSL_SUPPRESS(stl.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(stl.1) std::copy_n(src.data(), src.size(), dest.data()); } diff --git a/include/gsl/assert b/include/gsl/assert index a08424ca3..5181c131d 100644 --- a/include/gsl/assert +++ b/include/gsl/assert @@ -93,9 +93,7 @@ namespace details typedef void(__cdecl* terminate_handler)(); - // clang-format off - GSL_SUPPRESS(f.6) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(f.6) [[noreturn]] inline void __cdecl default_terminate_handler() { __fastfail(RANGE_CHECKS_FAILURE); diff --git a/include/gsl/byte b/include/gsl/byte index fe9b28662..fc81c0840 100644 --- a/include/gsl/byte +++ b/include/gsl/byte @@ -172,8 +172,6 @@ constexpr IntegerType to_integer(byte b) noexcept template -// NOTE: need suppression since c++14 does not allow "return {t}" -// GSL_SUPPRESS(type.4) // NO-FORMAT: attribute // TODO: suppression does not work constexpr gsl::impl::byte to_byte(T t) noexcept { static_assert(std::is_same::value, diff --git a/include/gsl/narrow b/include/gsl/narrow index bee29a1a8..f08230d39 100644 --- a/include/gsl/narrow +++ b/include/gsl/narrow @@ -28,21 +28,19 @@ struct narrowing_error : public std::exception // narrow() : a checked version of narrow_cast() that throws if the cast changed the value template ::value>::type* = nullptr> -// clang-format off -GSL_SUPPRESS(type.1) // NO-FORMAT: attribute -GSL_SUPPRESS(es.46) // NO-FORMAT: attribute // The warning suggests that a floating->unsigned conversion can occur - // in the static_cast below, and that gsl::narrow should be used instead. - // Suppress this warning, since gsl::narrow is defined in terms of - // static_cast - // clang-format on +GSL_SUPPRESS(type.1) +GSL_SUPPRESS(es.46) // The warning suggests that a floating->unsigned conversion can occur + // in the static_cast below, and that gsl::narrow should be used instead. + // Suppress this warning, since gsl::narrow is defined in terms of + // static_cast constexpr T narrow(U u) { constexpr const bool is_different_signedness = (std::is_signed::value != std::is_signed::value); -GSL_SUPPRESS(es.103) // NO-FORMAT: attribute // don't overflow -GSL_SUPPRESS(es.104) // NO-FORMAT: attribute // don't underflow -GSL_SUPPRESS(p.2) // NO-FORMAT: attribute // don't rely on undefined behavior +GSL_SUPPRESS(es.103) // don't overflow +GSL_SUPPRESS(es.104) // don't underflow +GSL_SUPPRESS(p.2) // don't rely on undefined behavior const T t = narrow_cast(u); // While this is technically undefined behavior in some cases (i.e., if the source value is of floating-point type // and cannot fit into the destination integral type), the resultant behavior is benign on the platforms // that we target (i.e., no hardware trap representations are hit). @@ -64,10 +62,8 @@ GSL_SUPPRESS(p.2) // NO-FORMAT: attribute // don't rely on undefined behavior } template ::value>::type* = nullptr> -// clang-format off -GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on - constexpr T narrow(U u) +GSL_SUPPRESS(type.1) +constexpr T narrow(U u) { const T t = narrow_cast(u); diff --git a/include/gsl/span b/include/gsl/span index af351ca17..a035d195a 100644 --- a/include/gsl/span +++ b/include/gsl/span @@ -163,9 +163,7 @@ namespace details constexpr span_iterator& operator++() noexcept { Expects(current_ != end_); - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(bounds.1) ++current_; return *this; } @@ -196,9 +194,7 @@ namespace details if (n != 0) Expects(begin_ && current_ && end_); if (n > 0) Expects(end_ - current_ >= n); if (n < 0) Expects(current_ - begin_ >= -n); - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(bounds.1) current_ += n; return *this; } @@ -315,9 +311,7 @@ namespace details if (n < 0) Expects(current_ - begin_ >= -n); } - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(bounds.1) constexpr pointer _Unwrapped() const noexcept { // after seeking *this to a high water mark, or using one of the // _Verify_xxx functions above, unwrap this span_iterator to a raw @@ -332,9 +326,7 @@ namespace details #else static constexpr bool _Unwrap_when_unverified = false; #endif - // clang-format off - GSL_SUPPRESS(con.3) // NO-FORMAT: attribute // TODO: false positive - // clang-format on + GSL_SUPPRESS(con.3) // TODO: false positive constexpr void _Seek_to(const pointer p) noexcept { // adjust the position of *this to previously verified location p // after _Unwrapped @@ -349,7 +341,8 @@ namespace details template friend struct std::pointer_traits; }; -}} // namespace gsl::details +} // namespace details +} // namespace gsl namespace std { @@ -364,7 +357,10 @@ struct pointer_traits<::gsl::details::span_iterator> }; } // namespace std -namespace gsl { namespace details { +namespace gsl +{ +namespace details +{ template class extent_type { @@ -589,10 +585,8 @@ public: } template - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on - constexpr span last() const noexcept + GSL_SUPPRESS(bounds.1) + constexpr span last() const noexcept { static_assert(Extent == dynamic_extent || Count <= Extent, "last() cannot extract more elements from a span than it contains."); @@ -601,10 +595,8 @@ public: } template - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on - constexpr auto subspan() const noexcept -> + GSL_SUPPRESS(bounds.1) + constexpr auto subspan() const noexcept -> typename details::calculate_subspan_type::type { static_assert(Extent == dynamic_extent || (Extent >= Offset && (Count == dynamic_extent || @@ -642,9 +634,7 @@ public: constexpr bool empty() const noexcept { return size() == 0; } // [span.elem], span element access - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(bounds.1) constexpr reference operator[](size_type idx) const noexcept { Expects(idx < size()); @@ -669,18 +659,14 @@ public: constexpr iterator begin() const noexcept { const auto data = storage_.data(); - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(bounds.1) return {data, data + size(), data}; } constexpr iterator end() const noexcept { const auto data = storage_.data(); - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(bounds.1) const auto endData = data + storage_.size(); return {data, endData, endData}; } @@ -693,9 +679,7 @@ public: constexpr pointer _Unchecked_begin() const noexcept { return data(); } constexpr pointer _Unchecked_end() const noexcept { - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(bounds.1) return data() + size(); } #endif // _MSC_VER @@ -752,9 +736,7 @@ private: return tmp.subspan(offset, count); } - // clang-format off - GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(bounds.1) constexpr span make_subspan(size_type offset, size_type count, subspan_selector) const noexcept { @@ -792,7 +774,9 @@ span(const Container&) -> span; #if defined(GSL_USE_STATIC_CONSTEXPR_WORKAROUND) #if defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L) #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" // Bug in clang-cl.exe which raises a C++17 -Wdeprecated warning about this static constexpr workaround in C++14 mode. +#pragma clang diagnostic ignored \ + "-Wdeprecated" // Bug in clang-cl.exe which raises a C++17 -Wdeprecated warning about this + // static constexpr workaround in C++14 mode. #endif // defined(__clang__) && defined(_MSC_VER) && defined(__cplusplus) && (__cplusplus < 201703L) template constexpr const typename span::size_type span::extent; @@ -827,11 +811,10 @@ template span::value> as_bytes(span s) noexcept { - using type = span::value>; + using type = + span::value>; - // clang-format off - GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(type.1) return type{reinterpret_cast(s.data()), s.size_bytes()}; } @@ -842,9 +825,7 @@ as_writable_bytes(span s) noexcept { using type = span::value>; - // clang-format off - GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(type.1) return type{reinterpret_cast(s.data()), s.size_bytes()}; } diff --git a/include/gsl/util b/include/gsl/util index 7a3caed48..3bafc4e68 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -127,9 +127,7 @@ GSL_NODISCARD auto finally(F&& f) noexcept // narrow_cast(): a searchable way to do narrowing casts of values template -// clang-format off -GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on +GSL_SUPPRESS(type.1) constexpr T narrow_cast(U&& u) noexcept { return static_cast(std::forward(u)); @@ -139,10 +137,8 @@ GSL_SUPPRESS(type.1) // NO-FORMAT: attribute // at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector // template -// clang-format off -GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute -GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute - // clang-format on +GSL_SUPPRESS(bounds.4) +GSL_SUPPRESS(bounds.2) constexpr T& at(T (&arr)[N], const index i) { static_assert(N <= static_cast((std::numeric_limits::max)()), "We only support arrays up to PTRDIFF_MAX bytes."); @@ -151,10 +147,8 @@ GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute } template -// clang-format off -GSL_SUPPRESS(bounds.4) // NO-FORMAT: attribute -GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute - // clang-format on +GSL_SUPPRESS(bounds.4) +GSL_SUPPRESS(bounds.2) constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]) { Expects(i >= 0 && i < narrow_cast(cont.size())); @@ -163,9 +157,7 @@ GSL_SUPPRESS(bounds.2) // NO-FORMAT: attribute } template -// clang-format off -GSL_SUPPRESS(bounds.1) // NO-FORMAT: attribute - // clang-format on +GSL_SUPPRESS(bounds.1) constexpr T at(const std::initializer_list cont, const index i) { Expects(i >= 0 && i < narrow_cast(cont.size())); diff --git a/tests/byte_tests.cpp b/tests/byte_tests.cpp index 77a947a6e..e361e8ae2 100644 --- a/tests/byte_tests.cpp +++ b/tests/byte_tests.cpp @@ -171,8 +171,8 @@ static_assert(!RShiftAssignCompilesFor, "!RShiftAssignCompilesFor" template static constexpr bool ToIntegerCompilesFor = false; template -static constexpr bool - ToIntegerCompilesFor(gsl::byte{}))>> = true; +static constexpr bool ToIntegerCompilesFor(gsl::byte{}))>> = + true; static_assert(!ToIntegerCompilesFor, "!ToIntegerCompilesFor"); } // namespace diff --git a/tests/constexpr_notnull_tests.cpp b/tests/constexpr_notnull_tests.cpp index 9192f85fe..f735b146f 100644 --- a/tests/constexpr_notnull_tests.cpp +++ b/tests/constexpr_notnull_tests.cpp @@ -28,15 +28,15 @@ constexpr bool comparison_test(const int* ptr1, const int* ptr2) const not_null p1(ptr1); const not_null p1_same(ptr1); const not_null p2(ptr2); - + // Testing operator== const bool eq_result = (p1 == p1_same); // Should be true const bool neq_result = (p1 != p2); // Should be true - + // Testing operator<= and operator>= const bool le_result = (p1 <= p1_same); // Should be true const bool ge_result = (p1 >= p1_same); // Should be true - + // The exact comparison results will depend on pointer ordering, // but we can verify that the basic equality checks work as expected return eq_result && neq_result && le_result && ge_result; @@ -47,11 +47,11 @@ constexpr bool workaround_test(const int* ptr1, const int* ptr2) const not_null p1(ptr1); const not_null p1_same(ptr1); const not_null p2(ptr2); - + // Using .get() to compare const bool eq_result = (p1.get() == p1_same.get()); // Should be true const bool neq_result = (p1.get() != p2.get()); // Should be true - + return eq_result && neq_result; } } // namespace @@ -59,8 +59,10 @@ constexpr bool workaround_test(const int* ptr1, const int* ptr2) constexpr int test_value1 = 1; constexpr int test_value2 = 2; -static_assert(comparison_test(&test_value1, &test_value2), "not_null comparison operators should be constexpr"); -static_assert(workaround_test(&test_value1, &test_value2), "not_null .get() comparison workaround should work"); +static_assert(comparison_test(&test_value1, &test_value2), + "not_null comparison operators should be constexpr"); +static_assert(workaround_test(&test_value1, &test_value2), + "not_null .get() comparison workaround should work"); TEST(notnull_constexpr_tests, TestNotNullConstexprComparison) { @@ -71,4 +73,3 @@ TEST(notnull_constexpr_tests, TestNotNullConstexprComparison) EXPECT_TRUE(comparison_test(&value1, &value2)); EXPECT_TRUE(workaround_test(&value1, &value2)); } - diff --git a/tests/notnull_tests.cpp b/tests/notnull_tests.cpp index 8cb9f8620..178433fda 100644 --- a/tests/notnull_tests.cpp +++ b/tests/notnull_tests.cpp @@ -69,9 +69,7 @@ struct CustomPtr template std::string operator==(CustomPtr const& lhs, CustomPtr const& rhs) { - // clang-format off - GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(type.1) return reinterpret_cast(lhs.p_) == reinterpret_cast(rhs.p_) ? "true" : "false"; } @@ -79,9 +77,7 @@ std::string operator==(CustomPtr const& lhs, CustomPtr const& rhs) template std::string operator!=(CustomPtr const& lhs, CustomPtr const& rhs) { - // clang-format off - GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(type.1) return reinterpret_cast(lhs.p_) != reinterpret_cast(rhs.p_) ? "true" : "false"; } @@ -89,9 +85,7 @@ std::string operator!=(CustomPtr const& lhs, CustomPtr const& rhs) template std::string operator<(CustomPtr const& lhs, CustomPtr const& rhs) { - // clang-format off - GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(type.1) return reinterpret_cast(lhs.p_) < reinterpret_cast(rhs.p_) ? "true" : "false"; } @@ -99,9 +93,7 @@ std::string operator<(CustomPtr const& lhs, CustomPtr const& rhs) template std::string operator>(CustomPtr const& lhs, CustomPtr const& rhs) { - // clang-format off - GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(type.1) return reinterpret_cast(lhs.p_) > reinterpret_cast(rhs.p_) ? "true" : "false"; } @@ -109,9 +101,7 @@ std::string operator>(CustomPtr const& lhs, CustomPtr const& rhs) template std::string operator<=(CustomPtr const& lhs, CustomPtr const& rhs) { - // clang-format off - GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(type.1) return reinterpret_cast(lhs.p_) <= reinterpret_cast(rhs.p_) ? "true" : "false"; } @@ -119,9 +109,7 @@ std::string operator<=(CustomPtr const& lhs, CustomPtr const& rhs) template std::string operator>=(CustomPtr const& lhs, CustomPtr const& rhs) { - // clang-format off - GSL_SUPPRESS(type.1) // NO-FORMAT: attribute - // clang-format on + GSL_SUPPRESS(type.1) return reinterpret_cast(lhs.p_) >= reinterpret_cast(rhs.p_) ? "true" : "false"; } @@ -137,13 +125,9 @@ struct NonCopyableNonMovable namespace { -// clang-format off -GSL_SUPPRESS(f.4) // NO-FORMAT: attribute -// clang-format on +GSL_SUPPRESS(f.4) bool helper(not_null p) { return *p == 12; } -// clang-format off -GSL_SUPPRESS(f.4) // NO-FORMAT: attribute -// clang-format on +GSL_SUPPRESS(f.4) bool helper_const(not_null p) { return *p == 12; } int* return_pointer() { return nullptr; } diff --git a/tests/owner_tests.cpp b/tests/owner_tests.cpp index 0cb47e851..c6373d296 100644 --- a/tests/owner_tests.cpp +++ b/tests/owner_tests.cpp @@ -21,7 +21,7 @@ using namespace gsl; -GSL_SUPPRESS(f.23) // NO-FORMAT: attribute +GSL_SUPPRESS(f.23) void f(int* i) { *i += 1; } TEST(owner_tests, basic_test) @@ -43,8 +43,7 @@ using void_t = void; template static constexpr bool OwnerCompilesFor = false; template -static constexpr bool OwnerCompilesFor{})>> = - true; +static constexpr bool OwnerCompilesFor{})>> = true; static_assert(OwnerCompilesFor, "OwnerCompilesFor"); static_assert(!OwnerCompilesFor, "!OwnerCompilesFor"); static_assert(!OwnerCompilesFor>, "!OwnerCompilesFor>"); diff --git a/tests/pointers_tests.cpp b/tests/pointers_tests.cpp index fcee2edc1..72f761fa1 100644 --- a/tests/pointers_tests.cpp +++ b/tests/pointers_tests.cpp @@ -44,7 +44,8 @@ TEST(pointers_test, swap) gsl::not_null> a(std::make_unique(0)); gsl::not_null> b(std::make_unique(1)); - static_assert(noexcept(gsl::swap(a, b)), "not null unique_ptr should be noexcept-swappable"); + static_assert(noexcept(gsl::swap(a, b)), + "not null unique_ptr should be noexcept-swappable"); EXPECT_TRUE(*a == 0); EXPECT_TRUE(*b == 1); @@ -65,7 +66,8 @@ TEST(pointers_test, swap) gsl::strict_not_null> a{std::make_unique(0)}; gsl::strict_not_null> b{std::make_unique(1)}; - static_assert(noexcept(gsl::swap(a, b)), "strict not null unique_ptr should be noexcept-swappable"); + static_assert(noexcept(gsl::swap(a, b)), + "strict not null unique_ptr should be noexcept-swappable"); EXPECT_TRUE(*a == 0); EXPECT_TRUE(*b == 1); diff --git a/tests/span_compatibility_tests.cpp b/tests/span_compatibility_tests.cpp index 3aad96137..e0ee7fd98 100644 --- a/tests/span_compatibility_tests.cpp +++ b/tests/span_compatibility_tests.cpp @@ -61,10 +61,10 @@ void ArrayConvertibilityCheck() static_assert(std::is_same>::value, "std::is_same< decltype(span{stl_nullptr}), span>::value"); - static_assert( - std::is_same>::value, - "std::is_same< decltype(span{std::as_const(stl_nullptr)}), span>::value"); + static_assert(std::is_same>::value, + "std::is_same< decltype(span{std::as_const(stl_nullptr)}), span>::value"); } #endif } diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 9b74ad447..54f66280b 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -257,10 +257,12 @@ TEST(span_test, from_pointer_pointer_construction) EXPECT_TRUE(s.data() == &arr[0]); } - //{ // this test succeeds on all platforms, gsl::span is more relaxed than std::span where this would be UB - // auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; - // EXPECT_DEATH(workaround_macro(), expected); - //} +#if 0 + { // this test succeeds on all platforms, gsl::span is more relaxed than std::span where this would be UB + auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; + EXPECT_DEATH(workaround_macro(), expected); + } +#endif { int* p = nullptr; diff --git a/tests/strict_notnull_tests.cpp b/tests/strict_notnull_tests.cpp index 0626d8f2f..2879e01d0 100644 --- a/tests/strict_notnull_tests.cpp +++ b/tests/strict_notnull_tests.cpp @@ -41,24 +41,16 @@ struct RefCounted namespace { -// clang-format off -GSL_SUPPRESS(f.4) // NO-FORMAT: attribute -// clang-format on +GSL_SUPPRESS(f.4) bool helper(not_null p) { return *p == 12; } -// clang-format off -GSL_SUPPRESS(f.4) // NO-FORMAT: attribute -// clang-format on +GSL_SUPPRESS(f.4) bool helper_const(not_null p) { return *p == 12; } -// clang-format off -GSL_SUPPRESS(f.4) // NO-FORMAT: attribute -// clang-format on +GSL_SUPPRESS(f.4) bool strict_helper(strict_not_null p) { return *p == 12; } -// clang-format off -GSL_SUPPRESS(f.4) // NO-FORMAT: attribute -// clang-format on +GSL_SUPPRESS(f.4) bool strict_helper_const(strict_not_null p) { return *p == 12; } int* return_pointer() { return nullptr; } @@ -195,7 +187,6 @@ template static constexpr bool StrictHelperCompilesFor()))>> = true; - template static constexpr bool StrictHelperConstCompilesFor = false; template @@ -203,7 +194,6 @@ static constexpr bool StrictHelperConstCompilesFor()))>> = true; - template static constexpr bool HelperCompilesFor = false; template @@ -219,13 +209,12 @@ TEST(strict_notnull_tests, TestStrictNotNull) strict_not_null snn = &x; #endif static_assert(!StrictHelperCompilesFor, "!StrictHelperCompilesFor"); - static_assert(!StrictHelperConstCompilesFor, - "!StrictHelperCompilesFor"); + static_assert(!StrictHelperConstCompilesFor, "!StrictHelperCompilesFor"); const strict_not_null snn1{&x}; static_assert(StrictHelperCompilesFor>, - "StrictHelperCompilesFor>"); + "StrictHelperCompilesFor>"); helper(snn1); helper_const(snn1); diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp index 5b64e207e..cb82ec2e7 100644 --- a/tests/utils_tests.cpp +++ b/tests/utils_tests.cpp @@ -16,7 +16,7 @@ #include -#include // for move +#include // for move #include #include // for std::ptrdiff_t #include // for uint32_t, int32_t @@ -156,8 +156,8 @@ TEST(utils_tests, narrow) n = -42; EXPECT_THROW(narrow(n), narrowing_error); - EXPECT_TRUE( - narrow>(std::complex(4, 2)) == std::complex(4, 2)); + EXPECT_TRUE(narrow>(std::complex(4, 2)) == + std::complex(4, 2)); EXPECT_THROW(narrow>(std::complex(4.2)), narrowing_error); EXPECT_TRUE(narrow(float(1)) == 1); From 26a2b6a8e8efae7df8aec1648c8ec0afab8dff0d Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Thu, 28 May 2026 16:22:03 +0200 Subject: [PATCH 02/12] clang-format 20 --- .github/workflows/clang-format.yml | 2 +- tests/span_ext_tests.cpp | 2 +- tests/span_tests.cpp | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 76daf33e6..6a41d00bd 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -22,5 +22,5 @@ jobs: - name: Check format uses: jidicula/clang-format-action@v4.11.0 with: - clang-format-version: '19' + clang-format-version: '20' check-path: 'include tests' diff --git a/tests/span_ext_tests.cpp b/tests/span_ext_tests.cpp index dfd2725db..25aa46f35 100644 --- a/tests/span_ext_tests.cpp +++ b/tests/span_ext_tests.cpp @@ -114,7 +114,7 @@ TEST(span_ext_test, make_span_from_array_constructor) TEST(span_ext_test, make_span_from_dynamic_array_constructor) { - double(*arr)[3][4] = new double[100][3][4]; + double (*arr)[3][4] = new double[100][3][4]; { auto s = make_span(&arr[0][0][0], 10); diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 54f66280b..a0486c1cf 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -70,9 +70,7 @@ struct AddressOverloaded [[maybe_unused]] #endif AddressOverloaded operator&() const - { - return {}; - } + { return {}; } }; } // namespace @@ -344,7 +342,7 @@ TEST(span_test, from_array_constructor) TEST(span_test, from_dynamic_array_constructor) { - double(*arr)[3][4] = new double[100][3][4]; + double (*arr)[3][4] = new double[100][3][4]; { span s(&arr[0][0][0], 10); From e8dcfbecb5a4d74fe1ec87520a2f1d44a96f76b7 Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Thu, 28 May 2026 16:24:55 +0200 Subject: [PATCH 03/12] pipeline fail --- tests/at_tests.cpp | 4 ++-- tests/span_tests.cpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/at_tests.cpp b/tests/at_tests.cpp index 42b2241bf..a2809077e 100644 --- a/tests/at_tests.cpp +++ b/tests/at_tests.cpp @@ -32,7 +32,7 @@ TEST(at_tests, static_array) { int a[4] = {1, 2, 3, 4}; - const int(&c_a)[4] = a; + const int (&c_a)[4] = a; for (int i = 0; i < 4; ++i) { @@ -152,7 +152,7 @@ TEST(at_tests, std_span) static constexpr bool test_constexpr() { int a1[4] = {1, 2, 3, 4}; - const int(&c_a1)[4] = a1; + const int (&c_a1)[4] = a1; std::array a2 = {1, 2, 3, 4}; const std::array& c_a2 = a2; diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index a0486c1cf..494ded594 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -70,7 +70,9 @@ struct AddressOverloaded [[maybe_unused]] #endif AddressOverloaded operator&() const - { return {}; } + { + return {}; + } }; } // namespace From 1b433f0fb8a952eb7cd4019b0bd64660fc3f7eb3 Mon Sep 17 00:00:00 2001 From: Werner Henze <34543625+beinhaerter@users.noreply.github.com> Date: Fri, 29 May 2026 11:49:43 +0200 Subject: [PATCH 04/12] Update .github/workflows/clang-format.yml Co-authored-by: Carson Radtke --- .github/workflows/clang-format.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 6a41d00bd..34ba26cfc 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -20,7 +20,8 @@ jobs: # Runs clang-format over the repository codebase - name: Check format - uses: jidicula/clang-format-action@v4.11.0 - with: - clang-format-version: '20' - check-path: 'include tests' + run: | + { + find include -type f + find tests -type f \( -name '*.cpp' -o -name '*.h' \) + } | xargs clang-format --dry-run --Werror From cef28cf14c28f39d8dbec444e25cb51c6c7c8803 Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Fri, 29 May 2026 12:13:11 +0200 Subject: [PATCH 05/12] output used clang-format version --- .github/workflows/clang-format.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 34ba26cfc..8a0f123d5 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -18,6 +18,10 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + # Prints the version of clang-format being used + - name: Log clang-format version + run: clang-format --version + # Runs clang-format over the repository codebase - name: Check format run: | From 27ec152148764618a35850e953af40b5b87188d2 Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Fri, 29 May 2026 12:22:34 +0200 Subject: [PATCH 06/12] installed version is 18 which replaces "GSL_SUPPRESS(bounds.1)" with "GSL_SUPPRESS(bounds .1)" --- .github/workflows/clang-format.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 8a0f123d5..4e9f9c9a1 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -9,6 +9,9 @@ on: permissions: contents: read +env: + CLANG_VERSION: "20" + jobs: clang-format: name: Run clang-format @@ -18,9 +21,14 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + # Install the exact clang-format binary + - name: Install clang-format + run: | + sudo apt install clang-format-${{ env.CLANG_VERSION }} + # Prints the version of clang-format being used - name: Log clang-format version - run: clang-format --version + run: clang-format-${{ env.CLANG_VERSION }} --version # Runs clang-format over the repository codebase - name: Check format @@ -28,4 +36,4 @@ jobs: { find include -type f find tests -type f \( -name '*.cpp' -o -name '*.h' \) - } | xargs clang-format --dry-run --Werror + } | xargs clang-format-${{ env.CLANG_VERSION }} --dry-run --Werror From 8e6f2a084ed4e3ec9672c68d0f72ceb802b8ab27 Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Fri, 29 May 2026 14:29:11 +0200 Subject: [PATCH 07/12] only include/gsl, not include to prevent formatting of include/CMakeLists.txt --- .github/workflows/clang-format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 4e9f9c9a1..ddb373d2c 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -34,6 +34,6 @@ jobs: - name: Check format run: | { - find include -type f + find include/gsl -type f find tests -type f \( -name '*.cpp' -o -name '*.h' \) } | xargs clang-format-${{ env.CLANG_VERSION }} --dry-run --Werror From dc788cff5e7ab0ce184299cf4ba25cb7118ac4ec Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Fri, 29 May 2026 14:39:58 +0200 Subject: [PATCH 08/12] apply clang-format[-20] In a VS2026 developer command prompt I ran `clang-format -i include\gsl\* --assume-filename x.cpp`. This was necessary because VS GUI does not format files without an extension :(. Please note that `--assume-filename` is necessary here, otherwise the files will not be formatted. Surprisingly the behaviour for the formatter differs from the behaviour of `lang-format(-20) --dry-run --Werror include/gsl/*` where clang-format recognizes that the files is C++. --- include/gsl/byte | 32 ++++++++------ include/gsl/gsl | 14 +++--- include/gsl/narrow | 29 ++++++------- include/gsl/pointers | 101 +++++++++++++++++++++++++------------------ include/gsl/util | 38 +++++++++------- include/gsl/zstring | 2 +- 6 files changed, 122 insertions(+), 94 deletions(-) diff --git a/include/gsl/byte b/include/gsl/byte index fc81c0840..b532b5f4e 100644 --- a/include/gsl/byte +++ b/include/gsl/byte @@ -82,11 +82,13 @@ namespace gsl { #if GSL_USE_STD_BYTE -namespace impl { -// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as would be the case when we used gsl::byte. -// Users of GSL should only use gsl::byte, not gsl::impl::byte. -using byte = std::byte; -} +namespace impl +{ + // impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as + // would be the case when we used gsl::byte. Users of GSL should only use gsl::byte, not + // gsl::impl::byte. + using byte = std::byte; +} // namespace impl using byte GSL_DEPRECATED("Use std::byte instead.") = std::byte; @@ -100,11 +102,13 @@ enum class byte_may_alias byte : unsigned char { }; -namespace impl { -// impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as would be the case when we used gsl::byte. -// Users of GSL should only use gsl::byte, not gsl::impl::byte. -using byte = gsl::byte; -} +namespace impl +{ + // impl::byte is used by gsl::as_bytes so our own code does not trigger a deprecation warning as + // would be the case when we used gsl::byte. Users of GSL should only use gsl::byte, not + // gsl::impl::byte. + using byte = gsl::byte; +} // namespace impl template ::value, bool> = true> constexpr byte& operator<<=(byte& b, IntegerType shift) noexcept @@ -170,13 +174,13 @@ constexpr IntegerType to_integer(byte b) noexcept #endif // GSL_USE_STD_BYTE - template constexpr gsl::impl::byte to_byte(T t) noexcept { - static_assert(std::is_same::value, - "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. " - "If you are calling to_byte with an integer constant use: gsl::to_byte() version."); + static_assert( + std::is_same::value, + "gsl::to_byte(t) must be provided an unsigned char, otherwise data loss may occur. " + "If you are calling to_byte with an integer constant use: gsl::to_byte() version."); return gsl::impl::byte(t); } diff --git a/include/gsl/gsl b/include/gsl/gsl index ffaa1eb3b..157ef6c1c 100644 --- a/include/gsl/gsl +++ b/include/gsl/gsl @@ -18,13 +18,13 @@ #define GSL_GSL_H // IWYU pragma: begin_exports -#include "./algorithm" // copy -#include "./assert" // Ensures/Expects -#include "./byte" // byte -#include "./pointers" // owner, not_null -#include "./span" // span -#include "./zstring" // zstring -#include "./util" // finally()/narrow_cast()... +#include "./algorithm" // copy +#include "./assert" // Ensures/Expects +#include "./byte" // byte +#include "./pointers" // owner, not_null +#include "./span" // span +#include "./util" // finally()/narrow_cast()... +#include "./zstring" // zstring #ifdef __cpp_exceptions #include "./narrow" // narrow() diff --git a/include/gsl/narrow b/include/gsl/narrow index f08230d39..07d6ba22f 100644 --- a/include/gsl/narrow +++ b/include/gsl/narrow @@ -16,8 +16,8 @@ #ifndef GSL_NARROW_H #define GSL_NARROW_H -#include "./assert" // for GSL_SUPPRESS -#include "./util" // for narrow_cast +#include "./assert" // for GSL_SUPPRESS +#include "./util" // for narrow_cast #include // for std::exception namespace gsl { @@ -38,16 +38,18 @@ GSL_SUPPRESS(es.46) // The warning suggests that a floating->unsigned conversion constexpr const bool is_different_signedness = (std::is_signed::value != std::is_signed::value); -GSL_SUPPRESS(es.103) // don't overflow -GSL_SUPPRESS(es.104) // don't underflow -GSL_SUPPRESS(p.2) // don't rely on undefined behavior - const T t = narrow_cast(u); // While this is technically undefined behavior in some cases (i.e., if the source value is of floating-point type - // and cannot fit into the destination integral type), the resultant behavior is benign on the platforms - // that we target (i.e., no hardware trap representations are hit). + GSL_SUPPRESS(es.103) // don't overflow + GSL_SUPPRESS(es.104) // don't underflow + GSL_SUPPRESS(p.2) // don't rely on undefined behavior + const T t = narrow_cast( + u); // While this is technically undefined behavior in some cases (i.e., if the source value + // is of floating-point type and cannot fit into the destination integral type), the + // resultant behavior is benign on the platforms that we target (i.e., no hardware trap + // representations are hit). #if defined(__clang__) || defined(__GNUC__) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wfloat-equal" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" #endif // Note: NaN will always throw, since NaN != NaN if (static_cast(t) != u || (is_different_signedness && ((t < T{}) != (u < U{})))) @@ -55,7 +57,7 @@ GSL_SUPPRESS(p.2) // don't rely on undefined behavior throw narrowing_error{}; } #if defined(__clang__) || defined(__GNUC__) - #pragma GCC diagnostic pop +#pragma GCC diagnostic pop #endif return t; @@ -67,10 +69,7 @@ constexpr T narrow(U u) { const T t = narrow_cast(u); - if (static_cast(t) != u) - { - throw narrowing_error{}; - } + if (static_cast(t) != u) { throw narrowing_error{}; } return t; } diff --git a/include/gsl/pointers b/include/gsl/pointers index 436f09a46..09c40f71d 100644 --- a/include/gsl/pointers +++ b/include/gsl/pointers @@ -18,13 +18,13 @@ #define GSL_POINTERS_H #include "./assert" // for Ensures, Expects -#include "./util" // for GSL_DEPRECATED +#include "./util" // for GSL_DEPRECATED -#include // for ptrdiff_t, nullptr_t, size_t -#include // for less, greater -#include // for shared_ptr, unique_ptr, hash -#include // for enable_if_t, is_convertible, is_assignable -#include // for declval, forward +#include // for ptrdiff_t, nullptr_t, size_t +#include // for less, greater +#include // for shared_ptr, unique_ptr, hash +#include // for enable_if_t, is_convertible, is_assignable +#include // for declval, forward #if !defined(GSL_NO_IOSTREAMS) #include // for ostream @@ -48,15 +48,16 @@ namespace details { }; - // Resolves to the more efficient of `const T` or `const T&`, in the context of returning a const-qualified value - // of type T. + // Resolves to the more efficient of `const T` or `const T&`, in the context of returning a + // const-qualified value of type T. // - // Copied from cppfront's implementation of the CppCoreGuidelines F.16 (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rf-in) - template - using value_or_reference_return_t = std::conditional_t< - sizeof(T) <= 2*sizeof(void*) && std::is_trivially_copy_constructible::value, - const T, - const T&>; + // Copied from cppfront's implementation of the CppCoreGuidelines F.16 + // (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rf-in) + template + using value_or_reference_return_t = + std::conditional_t::value, + const T, const T&>; } // namespace details @@ -72,9 +73,10 @@ using unique_ptr GSL_DEPRECATED("Use std::unique_ptr instead") = std::unique_ptr // // owner // -// `gsl::owner` is designed as a safety mechanism for code that must deal directly with raw pointers that own memory. -// Ideally such code should be restricted to the implementation of low-level abstractions. `gsl::owner` can also be used -// as a stepping point in converting legacy code to use more modern RAII constructs, such as smart pointers. +// `gsl::owner` is designed as a safety mechanism for code that must deal directly with raw +// pointers that own memory. Ideally such code should be restricted to the implementation of +// low-level abstractions. `gsl::owner` can also be used as a stepping point in converting legacy +// code to use more modern RAII constructs, such as smart pointers. // // T must be a pointer type // - disallow construction from any type other than pointer type @@ -105,19 +107,23 @@ public: using element_type = T; template ::value>> - constexpr not_null(U&& u) noexcept(std::is_nothrow_move_constructible::value) : ptr_(std::forward(u)) + constexpr not_null(U&& u) noexcept(std::is_nothrow_move_constructible::value) + : ptr_(std::forward(u)) { Expects(ptr_ != nullptr); } template ::value>> - constexpr not_null(T u) noexcept(std::is_nothrow_move_constructible::value) : ptr_(std::move(u)) + constexpr not_null(T u) noexcept(std::is_nothrow_move_constructible::value) + : ptr_(std::move(u)) { Expects(ptr_ != nullptr); } template ::value>> - constexpr not_null(const not_null& other) noexcept(std::is_nothrow_move_constructible::value) : not_null(other.get()) + constexpr not_null(const not_null& other) noexcept( + std::is_nothrow_move_constructible::value) + : not_null(other.get()) {} not_null(const not_null& other) = default; @@ -151,7 +157,9 @@ private: T ptr_; }; -template ::value && std::is_move_constructible::value, bool> = true> +template ::value && + std::is_move_constructible::value, + bool> = true> void swap(not_null& a, not_null& b) noexcept { a.swap(b); @@ -174,7 +182,7 @@ std::ostream& operator<<(std::ostream& os, const not_null& val) template constexpr auto operator==(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(lhs.get() == rhs.get())) + const not_null& rhs) noexcept(noexcept(lhs.get() == rhs.get())) -> decltype(lhs.get() == rhs.get()) { return lhs.get() == rhs.get(); @@ -182,39 +190,41 @@ constexpr auto operator==(const not_null& lhs, template constexpr auto operator!=(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(lhs.get() != rhs.get())) + const not_null& rhs) noexcept(noexcept(lhs.get() != rhs.get())) -> decltype(lhs.get() != rhs.get()) { return lhs.get() != rhs.get(); } template -constexpr auto operator<(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(std::less<>{}(lhs.get(), rhs.get()))) - -> decltype(std::less<>{}(lhs.get(), rhs.get())) +constexpr auto operator<(const not_null& lhs, const not_null& rhs) noexcept( + noexcept(std::less<>{}(lhs.get(), rhs.get()))) -> decltype(std::less<>{}(lhs.get(), rhs.get())) { return std::less<>{}(lhs.get(), rhs.get()); } template -constexpr auto operator<=(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(std::less_equal<>{}(lhs.get(), rhs.get()))) +constexpr auto +operator<=(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(std::less_equal<>{}(lhs.get(), rhs.get()))) -> decltype(std::less_equal<>{}(lhs.get(), rhs.get())) { return std::less_equal<>{}(lhs.get(), rhs.get()); } template -constexpr auto operator>(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(std::greater<>{}(lhs.get(), rhs.get()))) +constexpr auto +operator>(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(std::greater<>{}(lhs.get(), rhs.get()))) -> decltype(std::greater<>{}(lhs.get(), rhs.get())) { return std::greater<>{}(lhs.get(), rhs.get()); } template -constexpr auto operator>=(const not_null& lhs, - const not_null& rhs) noexcept(noexcept(std::greater_equal<>{}(lhs.get(), rhs.get()))) +constexpr auto +operator>=(const not_null& lhs, + const not_null& rhs) noexcept(noexcept(std::greater_equal<>{}(lhs.get(), rhs.get()))) -> decltype(std::greater_equal<>{}(lhs.get(), rhs.get())) { return std::greater_equal<>{}(lhs.get(), rhs.get()); @@ -230,9 +240,10 @@ not_null operator+(const not_null&, std::ptrdiff_t) = delete; template not_null operator+(std::ptrdiff_t, const not_null&) = delete; - -// T is conceptually a pointer so we don't have to worry about it being a reference and violating std::hash requirements -template >::value> +// T is conceptually a pointer so we don't have to worry about it being a reference and violating +// std::hash requirements +template >::value> struct not_null_hash { std::size_t operator()(const T& value) const noexcept { return std::hash{}(value.get()); } @@ -282,24 +293,32 @@ class strict_not_null : public not_null { public: template ::value>> - constexpr explicit strict_not_null(U&& u) noexcept(std::is_nothrow_move_constructible::value) : not_null(std::forward(u)) + constexpr explicit strict_not_null(U&& u) noexcept(std::is_nothrow_move_constructible::value) + : not_null(std::forward(u)) {} template ::value>> - constexpr explicit strict_not_null(T u) noexcept(std::is_nothrow_move_constructible::value) : not_null(std::move(u)) + constexpr explicit strict_not_null(T u) noexcept(std::is_nothrow_move_constructible::value) + : not_null(std::move(u)) {} template ::value>> - constexpr strict_not_null(const not_null& other) noexcept(std::is_nothrow_move_constructible::value) : not_null(other) + constexpr strict_not_null(const not_null& other) noexcept( + std::is_nothrow_move_constructible::value) + : not_null(other) {} template ::value>> - constexpr strict_not_null(const strict_not_null& other) noexcept(std::is_nothrow_move_constructible::value) : not_null(other) + constexpr strict_not_null(const strict_not_null& other) noexcept( + std::is_nothrow_move_constructible::value) + : not_null(other) {} // To avoid invalidating the "not null" invariant, the contained pointer is actually copied - // instead of moved. If it is a custom pointer, its constructor could in theory throw exceptions. - strict_not_null(strict_not_null&& other) noexcept(std::is_nothrow_copy_constructible::value) = default; + // instead of moved. If it is a custom pointer, its constructor could in theory throw + // exceptions. + strict_not_null(strict_not_null&& other) noexcept( + std::is_nothrow_copy_constructible::value) = default; strict_not_null(const strict_not_null& other) = default; strict_not_null& operator=(const strict_not_null& other) = default; strict_not_null& operator=(const not_null& other) diff --git a/include/gsl/util b/include/gsl/util index 3bafc4e68..8b2f02c4c 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -21,8 +21,8 @@ #include #include // for ptrdiff_t, size_t -#include // for numeric_limits #include // for initializer_list +#include // for numeric_limits #include // for is_signed, integral_constant #include // for exchange, forward @@ -100,18 +100,21 @@ template class final_action { public: - explicit final_action(const F& ff) noexcept : f{ff} { } - explicit final_action(F&& ff) noexcept : f{std::move(ff)} { } + explicit final_action(const F& ff) noexcept : f{ff} {} + explicit final_action(F&& ff) noexcept : f{std::move(ff)} {} - ~final_action() noexcept { if (invoke) f(); } + ~final_action() noexcept + { + if (invoke) f(); + } final_action(final_action&& other) noexcept : f(std::move(other.f)), invoke(std::exchange(other.invoke, false)) - { } + {} - final_action(const final_action&) = delete; + final_action(const final_action&) = delete; void operator=(const final_action&) = delete; - void operator=(final_action&&) = delete; + void operator=(final_action&&) = delete; private: F f; @@ -128,7 +131,7 @@ GSL_NODISCARD auto finally(F&& f) noexcept // narrow_cast(): a searchable way to do narrowing casts of values template GSL_SUPPRESS(type.1) - constexpr T narrow_cast(U&& u) noexcept +constexpr T narrow_cast(U&& u) noexcept { return static_cast(std::forward(u)); } @@ -138,18 +141,17 @@ GSL_SUPPRESS(type.1) // template GSL_SUPPRESS(bounds.4) -GSL_SUPPRESS(bounds.2) - constexpr T& at(T (&arr)[N], const index i) +GSL_SUPPRESS(bounds.2) constexpr T& at(T (&arr)[N], const index i) { - static_assert(N <= static_cast((std::numeric_limits::max)()), "We only support arrays up to PTRDIFF_MAX bytes."); + static_assert(N <= static_cast((std::numeric_limits::max)()), + "We only support arrays up to PTRDIFF_MAX bytes."); Expects(i >= 0 && i < narrow_cast(N)); return arr[narrow_cast(i)]; } template GSL_SUPPRESS(bounds.4) -GSL_SUPPRESS(bounds.2) - constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]) +GSL_SUPPRESS(bounds.2) constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]) { Expects(i >= 0 && i < narrow_cast(cont.size())); using size_type = decltype(cont.size()); @@ -158,14 +160,18 @@ GSL_SUPPRESS(bounds.2) template GSL_SUPPRESS(bounds.1) - constexpr T at(const std::initializer_list cont, const index i) +constexpr T at(const std::initializer_list cont, const index i) { Expects(i >= 0 && i < narrow_cast(cont.size())); return *(cont.begin() + i); } -template ::value && std::is_move_constructible::value>> -void swap(T& a, T& b) { std::swap(a, b); } +template ::value && + std::is_move_constructible::value>> +void swap(T& a, T& b) +{ + std::swap(a, b); +} #if defined(__cpp_lib_span) && __cpp_lib_span >= 202002L template diff --git a/include/gsl/zstring b/include/gsl/zstring index f00aa055a..a71825137 100644 --- a/include/gsl/zstring +++ b/include/gsl/zstring @@ -19,7 +19,7 @@ #include "./span_ext" // for dynamic_extent -#include // for size_t, nullptr_t +#include // for size_t, nullptr_t namespace gsl { From f49ce4783cd759926996bc3f7634fdad2fa731dc Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Fri, 29 May 2026 21:43:09 +0200 Subject: [PATCH 09/12] change "#if 0" back to original version with comments only --- tests/span_tests.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp index 494ded594..3edf1798a 100644 --- a/tests/span_tests.cpp +++ b/tests/span_tests.cpp @@ -257,12 +257,12 @@ TEST(span_test, from_pointer_pointer_construction) EXPECT_TRUE(s.data() == &arr[0]); } -#if 0 - { // this test succeeds on all platforms, gsl::span is more relaxed than std::span where this would be UB - auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; - EXPECT_DEATH(workaround_macro(), expected); - } -#endif + // this test succeeds on all platforms, gsl::span is more relaxed than std::span where this + // would be UB + //{ + // auto workaround_macro = [&]() { span s{&arr[1], &arr[0]}; }; + // EXPECT_DEATH(workaround_macro(), expected); + //} { int* p = nullptr; From 32581bf9788a7fc421be8e3447ed2999763f29d4 Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Mon, 1 Jun 2026 22:06:59 +0200 Subject: [PATCH 10/12] formatting scripts for windows and linux for linux with linter (shfmt and shellcheck) --- .github/workflows/shell-script-linter.yml | 32 +++++++++++++++++++++++ scripts/apply-formatting.bat | 13 +++++++++ scripts/apply-formatting.sh | 18 +++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 .github/workflows/shell-script-linter.yml create mode 100644 scripts/apply-formatting.bat create mode 100644 scripts/apply-formatting.sh diff --git a/.github/workflows/shell-script-linter.yml b/.github/workflows/shell-script-linter.yml new file mode 100644 index 000000000..c31c51102 --- /dev/null +++ b/.github/workflows/shell-script-linter.yml @@ -0,0 +1,32 @@ +name: Shell script linter + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + clang-format: + name: Run shfmt and shellcheck + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Install the needed binaries + - name: Install shfmt and shellcheck + run: | + sudo apt install shfmt shellcheck + + - name: Check format + run: | + find scripts -type f -name '*.sh' -exec shfmt -l {} \; + + - name: Run shellcheck + run: | + find scripts -type f -name '*.sh' -exec shellcheck {} \; diff --git a/scripts/apply-formatting.bat b/scripts/apply-formatting.bat new file mode 100644 index 000000000..968349725 --- /dev/null +++ b/scripts/apply-formatting.bat @@ -0,0 +1,13 @@ +@echo off +setlocal enabledelayedexpansion + +clang-format -version +if %errorlevel% neq 0 ( + echo [ERROR] clang-format not found, script should be called from a visual studio developer command prompt. + exit /b %errorlevel% +) + +for %%f in (include\gsl\* tests\*.h tests\*.cpp) do ( + echo formatting %%f + clang-format -i --assume-filename x.cpp "%%f" +) diff --git a/scripts/apply-formatting.sh b/scripts/apply-formatting.sh new file mode 100644 index 000000000..560d9b5ac --- /dev/null +++ b/scripts/apply-formatting.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -euo pipefail + +cf=clang-format-$(grep -i 'CLANG_VERSION:' .github/workflows/clang-format.yml | sed -E 's/.*"([^"]+)".*/\1/') +readonly cf + +if ! "${cf}" -version; then + echo "[ERROR] clang-format not found. Please install it using: sudo apt install ${cf}" + exit 1 +fi + +{ + find include/gsl -type f + find tests -type f \( -name '*.cpp' -o -name '*.h' \) +} | xargs "${cf}" -i --assume-filename=x.cpp --verbose + +find scripts -type f -name '*.sh' -print -exec shfmt -w {} \; From a0ccc997d22b7e75780f64c0017e37587f1b6913 Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Mon, 1 Jun 2026 22:43:43 +0200 Subject: [PATCH 11/12] add WhitespaceSensitiveMacros: [GSL_SUPPRESS] --- .clang-format | 1 + include/gsl/narrow | 11 +++++------ include/gsl/span | 6 ++---- include/gsl/util | 13 +++++-------- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/.clang-format b/.clang-format index b958ccb02..08ad16ae4 100644 --- a/.clang-format +++ b/.clang-format @@ -31,3 +31,4 @@ AlignConsecutiveAssignments: false AlignTrailingComments: true SpaceAfterCStyleCast: true +WhitespaceSensitiveMacros: [GSL_SUPPRESS] diff --git a/include/gsl/narrow b/include/gsl/narrow index 07d6ba22f..5c657179f 100644 --- a/include/gsl/narrow +++ b/include/gsl/narrow @@ -29,10 +29,10 @@ struct narrowing_error : public std::exception // narrow() : a checked version of narrow_cast() that throws if the cast changed the value template ::value>::type* = nullptr> GSL_SUPPRESS(type.1) -GSL_SUPPRESS(es.46) // The warning suggests that a floating->unsigned conversion can occur - // in the static_cast below, and that gsl::narrow should be used instead. - // Suppress this warning, since gsl::narrow is defined in terms of - // static_cast + GSL_SUPPRESS(es.46) // The warning suggests that a floating->unsigned conversion can occur + // in the static_cast below, and that gsl::narrow should be used instead. + // Suppress this warning, since gsl::narrow is defined in terms of + // static_cast constexpr T narrow(U u) { constexpr const bool is_different_signedness = @@ -64,8 +64,7 @@ GSL_SUPPRESS(es.46) // The warning suggests that a floating->unsigned conversion } template ::value>::type* = nullptr> -GSL_SUPPRESS(type.1) -constexpr T narrow(U u) +GSL_SUPPRESS(type.1) constexpr T narrow(U u) { const T t = narrow_cast(u); diff --git a/include/gsl/span b/include/gsl/span index a035d195a..abbb6b50b 100644 --- a/include/gsl/span +++ b/include/gsl/span @@ -585,8 +585,7 @@ public: } template - GSL_SUPPRESS(bounds.1) - constexpr span last() const noexcept + GSL_SUPPRESS(bounds.1) constexpr span last() const noexcept { static_assert(Extent == dynamic_extent || Count <= Extent, "last() cannot extract more elements from a span than it contains."); @@ -595,8 +594,7 @@ public: } template - GSL_SUPPRESS(bounds.1) - constexpr auto subspan() const noexcept -> + GSL_SUPPRESS(bounds.1) constexpr auto subspan() const noexcept -> typename details::calculate_subspan_type::type { static_assert(Extent == dynamic_extent || (Extent >= Offset && (Count == dynamic_extent || diff --git a/include/gsl/util b/include/gsl/util index 8b2f02c4c..e6dbb8633 100644 --- a/include/gsl/util +++ b/include/gsl/util @@ -130,8 +130,7 @@ GSL_NODISCARD auto finally(F&& f) noexcept // narrow_cast(): a searchable way to do narrowing casts of values template -GSL_SUPPRESS(type.1) -constexpr T narrow_cast(U&& u) noexcept +GSL_SUPPRESS(type.1) constexpr T narrow_cast(U&& u) noexcept { return static_cast(std::forward(u)); } @@ -140,8 +139,7 @@ constexpr T narrow_cast(U&& u) noexcept // at() - Bounds-checked way of accessing builtin arrays, std::array, std::vector // template -GSL_SUPPRESS(bounds.4) -GSL_SUPPRESS(bounds.2) constexpr T& at(T (&arr)[N], const index i) +GSL_SUPPRESS(bounds.4) GSL_SUPPRESS(bounds.2) constexpr T& at(T (&arr)[N], const index i) { static_assert(N <= static_cast((std::numeric_limits::max)()), "We only support arrays up to PTRDIFF_MAX bytes."); @@ -150,8 +148,8 @@ GSL_SUPPRESS(bounds.2) constexpr T& at(T (&arr)[N], const index i) } template -GSL_SUPPRESS(bounds.4) -GSL_SUPPRESS(bounds.2) constexpr auto at(Cont& cont, const index i) -> decltype(cont[cont.size()]) +GSL_SUPPRESS(bounds.4) GSL_SUPPRESS(bounds.2) constexpr auto at(Cont& cont, const index i) + -> decltype(cont[cont.size()]) { Expects(i >= 0 && i < narrow_cast(cont.size())); using size_type = decltype(cont.size()); @@ -159,8 +157,7 @@ GSL_SUPPRESS(bounds.2) constexpr auto at(Cont& cont, const index i) -> decltype( } template -GSL_SUPPRESS(bounds.1) -constexpr T at(const std::initializer_list cont, const index i) +GSL_SUPPRESS(bounds.1) constexpr T at(const std::initializer_list cont, const index i) { Expects(i >= 0 && i < narrow_cast(cont.size())); return *(cont.begin() + i); From 2acf9de7745f618503c083fdcb397736acf9be13 Mon Sep 17 00:00:00 2001 From: Werner Henze Date: Thu, 4 Jun 2026 20:50:08 +0200 Subject: [PATCH 12/12] provide path for clang-format (Windows) Currently not clear to me: On my personal computer at home, when I start "Developer Command Prompt for VS18", I can run `clang-format` without providing the path. On my managed (domain) company computer, when I start "Developer Command Prompt for VS18", I can NOT run `clang-format` without providing the path. I need to call `"%VCINSTALLDIR%Tools\Llvm\bin\clang-format"`. I have no idea what the difference is. At least the version `"%VCINSTALLDIR%Tools\Llvm\bin\clang-format"` works on both computers, so I add the path. --- scripts/apply-formatting.bat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/apply-formatting.bat b/scripts/apply-formatting.bat index 968349725..397dccf62 100644 --- a/scripts/apply-formatting.bat +++ b/scripts/apply-formatting.bat @@ -1,7 +1,7 @@ @echo off setlocal enabledelayedexpansion -clang-format -version +"%VCINSTALLDIR%Tools\Llvm\bin\clang-format" -version if %errorlevel% neq 0 ( echo [ERROR] clang-format not found, script should be called from a visual studio developer command prompt. exit /b %errorlevel% @@ -9,5 +9,5 @@ if %errorlevel% neq 0 ( for %%f in (include\gsl\* tests\*.h tests\*.cpp) do ( echo formatting %%f - clang-format -i --assume-filename x.cpp "%%f" + "%VCINSTALLDIR%Tools\Llvm\bin\clang-format" -i --assume-filename x.cpp "%%f" )