Skip to content

Conversation

localspook
Copy link
Contributor

@localspook localspook commented Jul 11, 2025

C++23 integer literal suffixes:

Suffix Type
z std::make_signed_t<std::size_t>
uz std::size_t

C++23 floating-point literal suffixes:

Suffix Type
bf16 std::bfloat16_t
f16 std::float16_t
f32 std::float32_t
f64 std::float64_t
f128 std::float128_t

C23 integer literal suffixes:

Suffix Type
wb _BitInt(N)
uwb unsigned _BitInt(N)

C23 floating-point literal suffixes:

Suffix Type
df _Decimal32
dd _Decimal64
dl _Decimal128

Clang doesn't actually support any of the new floating point types yet (except for f16). I've decided to add disabled tests for them, so that when the support comes, we can flip the switch and support them with no delay.

@llvmbot
Copy link
Member

llvmbot commented Jul 11, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Victor Chernyakin (localspook)

Changes

C++23 integer literal suffixes:

Suffix Type
z std::make_signed_t&lt;std::size_t&gt;
uz std::size_t

C++23 floating-point literal suffixes:

Suffix Type
bf16 std::bfloat16_t
f16 std::float16_t
f32 std::float32_t
f64 std::float64_t
f128 std::float128_t

C23 integer literal suffixes:

Suffix Type
wb _BitInt(N)
uwb unsigned _BitInt(N)

C23 floating-point literal suffixes:

Suffix Type
df _Decimal32
dd _Decimal64
dl _Decimal128

Clang doesn't actually support any of the new floating point types yet (except for f16). I've decided to add disabled tests for them, so that when the support comes, we can flip the switch and support them with no delay.


Patch is 26.98 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/148275.diff

6 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp (+7-6)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+4)
  • (added) clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c (+162)
  • (added) clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-extended-floating-point.cpp (+248)
  • (removed) clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-float16.cpp (-51)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp (+85-1)
diff --git a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp
index 678aa8dad48a7..dd43ee200f4c0 100644
--- a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp
@@ -25,10 +25,11 @@ struct IntegerLiteralCheck {
   static constexpr llvm::StringLiteral Name = llvm::StringLiteral("integer");
   // What should be skipped before looking for the Suffixes? (Nothing here.)
   static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("");
-  // Suffix can only consist of 'u' and 'l' chars, and can be a complex number
-  // ('i', 'j'). In MS compatibility mode, suffixes like i32 are supported.
+  // Suffix can only consist of 'u', 'l', and 'z' chars, can be a bit-precise
+  // integer (wb), and can be a complex number ('i', 'j'). In MS compatibility
+  // mode, suffixes like i32 are supported.
   static constexpr llvm::StringLiteral Suffixes =
-      llvm::StringLiteral("uUlLiIjJ");
+      llvm::StringLiteral("uUlLzZwWbBiIjJ");
 };
 constexpr llvm::StringLiteral IntegerLiteralCheck::Name;
 constexpr llvm::StringLiteral IntegerLiteralCheck::SkipFirst;
@@ -45,10 +46,10 @@ struct FloatingLiteralCheck {
   // Since the exponent ('p'/'P') is mandatory for hexadecimal floating-point
   // literals, we first skip everything before the exponent.
   static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("pP");
-  // Suffix can only consist of 'f', 'l', "f16", 'h', 'q' chars,
-  // and can be a complex number ('i', 'j').
+  // Suffix can only consist of 'f', 'l', "f16", "bf16", "df", "dd", "dl",
+  // 'h', 'q' chars, and can be a complex number ('i', 'j').
   static constexpr llvm::StringLiteral Suffixes =
-      llvm::StringLiteral("fFlLhHqQiIjJ");
+      llvm::StringLiteral("fFlLbBdDhHqQiIjJ");
 };
 constexpr llvm::StringLiteral FloatingLiteralCheck::Name;
 constexpr llvm::StringLiteral FloatingLiteralCheck::SkipFirst;
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index ad869265a2db5..119c030b43852 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -354,6 +354,10 @@ Changes in existing checks
   <clang-tidy/checks/readability/redundant-smartptr-get>` check by fixing
   some false positives involving smart pointers to arrays.
 
+- Improved :doc:`readability-uppercase-literal-suffix
+  <clang-tidy/checks/readability/uppercase-literal-suffix>` check to recognize
+  literal suffixes added in C++23 and C23.
+
 Removed checks
 ^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c
new file mode 100644
index 0000000000000..75727fc69e68c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c
@@ -0,0 +1,162 @@
+// TODO: When Clang adds support for decimal floating point types, enable these tests by:
+//    1. Removing all the #if 0 + #endif guards.
+//    2. Removing all occurrences of the string "DISABLED-" in this file.
+//    3. Deleting this message.
+
+// RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -std=c23
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.c
+// RUN: clang-tidy %t.c -checks='-*,readability-uppercase-literal-suffix' -fix -- -std=c23
+// RUN: clang-tidy %t.c -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -std=c23
+
+void bit_precise_literal_suffix() {
+  // _BitInt()
+
+  static constexpr auto v1 = 1wb;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'wb', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v1 = 1wb;
+  // CHECK-MESSAGES-NEXT: ^~~
+  // CHECK-MESSAGES-NEXT: WB{{$}}
+  // CHECK-FIXES: static constexpr auto v1 = 1WB;
+  static_assert(v1 == 1WB);
+
+  static constexpr auto v2 = 1WB; // OK.
+  static_assert(v2 == 1WB);
+
+  // _BitInt() Unsigned
+
+  static constexpr auto v3 = 1wbu;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'wbu', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 1wbu;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: WBU{{$}}
+  // CHECK-FIXES: static constexpr auto v3 = 1WBU;
+  static_assert(v3 == 1WBU);
+
+  static constexpr auto v4 = 1WBu;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'WBu', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v4 = 1WBu;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: WBU{{$}}
+  // CHECK-FIXES: static constexpr auto v4 = 1WBU;
+  static_assert(v4 == 1WBU);
+
+  static constexpr auto v5 = 1wbU;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'wbU', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1wbU;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: WBU{{$}}
+  // CHECK-FIXES: static constexpr auto v5 = 1WBU;
+  static_assert(v5 == 1WBU);
+
+  static constexpr auto v6 = 1WBU; // OK.
+  static_assert(v6 == 1WBU);
+
+  // Unsigned _BitInt()
+
+  static constexpr auto v7 = 1uwb;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'uwb', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 1uwb;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: UWB{{$}}
+  // CHECK-FIXES: static constexpr auto v7 = 1UWB;
+  static_assert(v7 == 1UWB);
+
+  static constexpr auto v8 = 1uWB;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'uWB', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v8 = 1uWB;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: UWB{{$}}
+  // CHECK-FIXES: static constexpr auto v8 = 1UWB;
+  static_assert(v8 == 1UWB);
+
+  static constexpr auto v9 = 1Uwb;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'Uwb', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1Uwb;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: UWB{{$}}
+  // CHECK-FIXES: static constexpr auto v9 = 1UWB;
+  static_assert(v9 == 1UWB);
+
+  static constexpr auto v10 = 1UWB; // OK.
+  static_assert(v10 == 1UWB);
+}
+
+void decimal_floating_point_suffix() {
+  // _Decimal32
+
+#if 0
+  static constexpr auto v1 = 1.df;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'df', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v1 = 1.df;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DF{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v1 = 1.DF;
+  static_assert(v1 == 1.DF);
+
+  static constexpr auto v2 = 1.e0df;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'df', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v2 = 1.e0df;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DF{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v2 = 1.e0DF;
+  static_assert(v2 == 1.DF);
+
+  static constexpr auto v3 = 1.DF; // OK.
+  static_assert(v3 == 1.DF);
+
+  static constexpr auto v4 = 1.e0DF; // OK.
+  static_assert(v4 == 1.DF);
+#endif
+
+  // _Decimal64
+
+#if 0
+  static constexpr auto v5 = 1.dd;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'dd', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1.dd;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DD{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v5 = 1.DD;
+  static_assert(v5 == 1.DD);
+
+  static constexpr auto v6 = 1.e0dd;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'dd', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v6 = 1.e0dd;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DD{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v6 = 1.e0DD;
+  static_assert(v6 == 1.DD);
+
+  static constexpr auto v7 = 1.DD; // OK.
+  static_assert(v7 == 1.DD);
+
+  static constexpr auto v8 = 1.e0DD; // OK.
+  static_assert(v8 == 1.DD);
+#endif
+
+  // _Decimal128
+
+#if 0
+  static constexpr auto v9 = 1.dl;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'dl', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1.dl;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DL{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v9 = 1.DL;
+  static_assert(v9 == 1.DL);
+
+  static constexpr auto v10 = 1.e0dl;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'dl', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v10 = 1.e0dl;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DL{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v10 = 1.e0DL;
+  static_assert(v10 == 1.DL);
+
+  static constexpr auto v11 = 1.DL; // OK.
+  static_assert(v11 == 1.DL);
+
+  static constexpr auto v12 = 1.e0DL; // OK.
+  static_assert(v12 == 1.DL);
+#endif
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-extended-floating-point.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-extended-floating-point.cpp
new file mode 100644
index 0000000000000..abe23e3363766
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-extended-floating-point.cpp
@@ -0,0 +1,248 @@
+// TODO: When Clang adds support for C++23 floating-point types, enable these tests by:
+//    1. Removing all the #if 0 + #endif guards.
+//    2. Removing all occurrences of the string "DISABLED-" in this file.
+//    3. Deleting this message.
+// These suffixes may be relevant to C too: https://github.com/llvm/llvm-project/issues/97335
+
+// RUN: %check_clang_tidy -std=c++23 %s readability-uppercase-literal-suffix %t -- -- -target aarch64-linux-gnu -I %clang_tidy_headers
+
+#include "integral_constant.h"
+#if 0
+#include <stdfloat>
+#endif
+
+void normal_literals() {
+  // std::bfloat16_t
+
+#if 0
+  static constexpr auto v1 = 1.bf16;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'bf16', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v1 = 1.bf16;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: BF16{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v1 = 1.BF16;
+  static_assert(is_same<decltype(v1), const std::bfloat16_t>::value, "");
+  static_assert(v1 == 1.BF16, "");
+
+  static constexpr auto v2 = 1.e0bf16;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'bf16', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v2 = 1.e0bf16;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: BF16{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v2 = 1.e0BF16;
+  static_assert(is_same<decltype(v2), const std::bfloat16_t>::value, "");
+  static_assert(v2 == 1.BF16, "");
+
+  static constexpr auto v3 = 1.BF16; // OK.
+  static_assert(is_same<decltype(v3), const std::bfloat16_t>::value, "");
+  static_assert(v3 == 1.BF16, "");
+
+  static constexpr auto v4 = 1.e0BF16; // OK.
+  static_assert(is_same<decltype(v4), const std::bfloat16_t>::value, "");
+  static_assert(v4 == 1.BF16, "");
+#endif
+
+  // _Float16/std::float16_t
+
+  static constexpr auto v5 = 1.f16;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f16', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1.f16;
+  // CHECK-MESSAGES-NEXT: ^ ~
+  // CHECK-MESSAGES-NEXT: F16{{$}}
+  // CHECK-FIXES: static constexpr auto v5 = 1.F16;
+  static_assert(is_same<decltype(v5), const _Float16>::value, "");
+  static_assert(v5 == 1.F16, "");
+
+  static constexpr auto v6 = 1.e0f16;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f16', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v6 = 1.e0f16;
+  // CHECK-MESSAGES-NEXT: ^ ~
+  // CHECK-MESSAGES-NEXT: F16{{$}}
+  // CHECK-FIXES: static constexpr auto v6 = 1.e0F16;
+  static_assert(is_same<decltype(v6), const _Float16>::value, "");
+  static_assert(v6 == 1.F16, "");
+
+  static constexpr auto v7 = 1.F16; // OK.
+  static_assert(is_same<decltype(v7), const _Float16>::value, "");
+  static_assert(v7 == 1.F16, "");
+
+  static constexpr auto v8 = 1.e0F16; // OK.
+  static_assert(is_same<decltype(v8), const _Float16>::value, "");
+  static_assert(v8 == 1.F16, "");
+
+  // std::float32_t
+
+#if 0
+  static constexpr auto v9 = 1.f32;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f32', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1.f32;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F32{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v9 = 1.F32;
+  static_assert(is_same<decltype(v9), const std::float32_t>::value, "");
+  static_assert(v9 == 1.F32, "");
+
+  static constexpr auto v10 = 1.e0f32;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f32', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v10 = 1.e0f32;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F32{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v10 = 1.e0F32;
+  static_assert(is_same<decltype(v10), const std::float32_t>::value, "");
+  static_assert(v10 == 1.F32, "");
+
+  static constexpr auto v11 = 1.F32; // OK.
+  static_assert(is_same<decltype(v11), const std::float32_t>::value, "");
+  static_assert(v11 == 1.F32, "");
+
+  static constexpr auto v12 = 1.e0F32; // OK.
+  static_assert(is_same<decltype(v12), const std::float32_t>::value, "");
+  static_assert(v12 == 1.F32, "");
+#endif
+
+  // std::float64_t
+
+#if 0
+  static constexpr auto v13 = 1.f64;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f64', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v13 = 1.f64;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F64{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v13 = 1.F64;
+  static_assert(is_same<decltype(v13), const std::float64_t>::value, "");
+  static_assert(v13 == 1.F64, "");
+
+  static constexpr auto v14 = 1.e0f64;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f64', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v14 = 1.e0f64;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F64{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v14 = 1.e0F64;
+  static_assert(is_same<decltype(v14), const std::float64_t>::value, "");
+  static_assert(v14 == 1.F64, "");
+
+  static constexpr auto v15 = 1.F64; // OK.
+  static_assert(is_same<decltype(v15), const std::float64_t>::value, "");
+  static_assert(v15 == 1.F64, "");
+
+  static constexpr auto v16 = 1.e0F64; // OK.
+  static_assert(is_same<decltype(v16), const std::float64_t>::value, "");
+  static_assert(v16 == 1.F64, "");
+#endif
+
+  // std::float128_t
+
+#if 0
+  static constexpr auto v17 = 1.f128;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f128', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v17 = 1.f128;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F128{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v17 = 1.F128;
+  static_assert(is_same<decltype(v17), const std::float128_t>::value, "");
+  static_assert(v17 == 1.F128, "");
+
+  static constexpr auto v18 = 1.e0f128;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f128', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v18 = 1.e0f128;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F128{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v18 = 1.e0F128;
+  static_assert(is_same<decltype(v18), const std::float128_t>::value, "");
+  static_assert(v18 == 1.F128, "");
+
+  static constexpr auto v19 = 1.F128; // OK.
+  static_assert(is_same<decltype(v19), const std::float128_t>::value, "");
+  static_assert(v19 == 1.F128, "");
+
+  static constexpr auto v20 = 1.e0F128; // OK.
+  static_assert(is_same<decltype(v20), const std::float128_t>::value, "");
+  static_assert(v20 == 1.F128, "");
+#endif
+}
+
+void hexadecimal_literals() {
+  // std::bfloat16_t
+
+#if 0
+  static constexpr auto v1 = 0xfp0bf16;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'bf16', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v1 = 0xfp0bf16;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^    ~
+  // DISABLED-CHECK-MESSAGES-NEXT: BF16{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v1 = 0xfp0BF16;
+  static_assert(is_same<decltype(v1), const std::bfloat16_t>::value, "");
+  static_assert(v1 == 0xfp0BF16, "");
+
+  static constexpr auto v2 = 0xfp0BF16; // OK.
+  static_assert(is_same<decltype(v2), const std::bfloat16_t>::value, "");
+  static_assert(v2 == 0xfp0BF16, "");
+#endif
+
+  // _Float16/std::float16_t
+
+  static constexpr auto v3 = 0xfp0f16;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f16', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 0xfp0f16;
+  // CHECK-MESSAGES-NEXT: ^    ~
+  // CHECK-MESSAGES-NEXT: F16{{$}}
+  // CHECK-FIXES: static constexpr auto v3 = 0xfp0F16;
+  static_assert(is_same<decltype(v3), const _Float16>::value, "");
+  static_assert(v3 == 0xfp0F16, "");
+
+  static constexpr auto v4 = 0xfp0F16; // OK.
+  static_assert(is_same<decltype(v4), const _Float16>::value, "");
+  static_assert(v4 == 0xfp0F16, "");
+
+  // std::float32_t
+
+#if 0
+  static constexpr auto v5 = 0xfp0f32;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f32', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v5 = 0xfp0f32;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^    ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F32{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v5 = 0xfp0F32;
+  static_assert(is_same<decltype(v5), const std::float32_t>::value, "");
+  static_assert(v5 == 0xfp0F32, "");
+
+  static constexpr auto v6 = 0xfp0F32; // OK.
+  static_assert(is_same<decltype(v6), const std::float32_t>::value, "");
+  static_assert(v6 == 0xfp0F32, "");
+#endif
+
+  // std::float64_t
+
+#if 0
+  static constexpr auto v7 = 0xfp0f64;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f64', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v7 = 0xfp0f64;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^    ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F64{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v7 = 0xfp0F64;
+  static_assert(is_same<decltype(v7), const std::float64_t>::value, "");
+  static_assert(v7 == 0xfp0F64, "");
+
+  static constexpr auto v8 = 0xfp0F64; // OK.
+  static_assert(is_same<decltype(v8), const std::float64_t>::value, "");
+  static_assert(v8 == 0xfp0F64, "");
+#endif
+
+  // std::float128_t
+
+#if 0
+  static constexpr auto v9 = 0xfp0f128;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f128', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v9 = 0xfp0f128;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^    ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F128{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v9 = 0xfp0F128;
+  static_assert(is_same<decltype(v9), const std::float128_t>::value, "");
+  static_assert(v9 == 0xfp0F128, "");
+
+  static constexpr auto v10 ...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jul 11, 2025

@llvm/pr-subscribers-clang-tidy

Author: Victor Chernyakin (localspook)

Changes

C++23 integer literal suffixes:

Suffix Type
z std::make_signed_t&lt;std::size_t&gt;
uz std::size_t

C++23 floating-point literal suffixes:

Suffix Type
bf16 std::bfloat16_t
f16 std::float16_t
f32 std::float32_t
f64 std::float64_t
f128 std::float128_t

C23 integer literal suffixes:

Suffix Type
wb _BitInt(N)
uwb unsigned _BitInt(N)

C23 floating-point literal suffixes:

Suffix Type
df _Decimal32
dd _Decimal64
dl _Decimal128

Clang doesn't actually support any of the new floating point types yet (except for f16). I've decided to add disabled tests for them, so that when the support comes, we can flip the switch and support them with no delay.


Patch is 26.98 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/148275.diff

6 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp (+7-6)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+4)
  • (added) clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c (+162)
  • (added) clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-extended-floating-point.cpp (+248)
  • (removed) clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-float16.cpp (-51)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-integer.cpp (+85-1)
diff --git a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp
index 678aa8dad48a7..dd43ee200f4c0 100644
--- a/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UppercaseLiteralSuffixCheck.cpp
@@ -25,10 +25,11 @@ struct IntegerLiteralCheck {
   static constexpr llvm::StringLiteral Name = llvm::StringLiteral("integer");
   // What should be skipped before looking for the Suffixes? (Nothing here.)
   static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("");
-  // Suffix can only consist of 'u' and 'l' chars, and can be a complex number
-  // ('i', 'j'). In MS compatibility mode, suffixes like i32 are supported.
+  // Suffix can only consist of 'u', 'l', and 'z' chars, can be a bit-precise
+  // integer (wb), and can be a complex number ('i', 'j'). In MS compatibility
+  // mode, suffixes like i32 are supported.
   static constexpr llvm::StringLiteral Suffixes =
-      llvm::StringLiteral("uUlLiIjJ");
+      llvm::StringLiteral("uUlLzZwWbBiIjJ");
 };
 constexpr llvm::StringLiteral IntegerLiteralCheck::Name;
 constexpr llvm::StringLiteral IntegerLiteralCheck::SkipFirst;
@@ -45,10 +46,10 @@ struct FloatingLiteralCheck {
   // Since the exponent ('p'/'P') is mandatory for hexadecimal floating-point
   // literals, we first skip everything before the exponent.
   static constexpr llvm::StringLiteral SkipFirst = llvm::StringLiteral("pP");
-  // Suffix can only consist of 'f', 'l', "f16", 'h', 'q' chars,
-  // and can be a complex number ('i', 'j').
+  // Suffix can only consist of 'f', 'l', "f16", "bf16", "df", "dd", "dl",
+  // 'h', 'q' chars, and can be a complex number ('i', 'j').
   static constexpr llvm::StringLiteral Suffixes =
-      llvm::StringLiteral("fFlLhHqQiIjJ");
+      llvm::StringLiteral("fFlLbBdDhHqQiIjJ");
 };
 constexpr llvm::StringLiteral FloatingLiteralCheck::Name;
 constexpr llvm::StringLiteral FloatingLiteralCheck::SkipFirst;
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index ad869265a2db5..119c030b43852 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -354,6 +354,10 @@ Changes in existing checks
   <clang-tidy/checks/readability/redundant-smartptr-get>` check by fixing
   some false positives involving smart pointers to arrays.
 
+- Improved :doc:`readability-uppercase-literal-suffix
+  <clang-tidy/checks/readability/uppercase-literal-suffix>` check to recognize
+  literal suffixes added in C++23 and C23.
+
 Removed checks
 ^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c
new file mode 100644
index 0000000000000..75727fc69e68c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-c23.c
@@ -0,0 +1,162 @@
+// TODO: When Clang adds support for decimal floating point types, enable these tests by:
+//    1. Removing all the #if 0 + #endif guards.
+//    2. Removing all occurrences of the string "DISABLED-" in this file.
+//    3. Deleting this message.
+
+// RUN: %check_clang_tidy %s readability-uppercase-literal-suffix %t -- -- -std=c23
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.c
+// RUN: clang-tidy %t.c -checks='-*,readability-uppercase-literal-suffix' -fix -- -std=c23
+// RUN: clang-tidy %t.c -checks='-*,readability-uppercase-literal-suffix' -warnings-as-errors='-*,readability-uppercase-literal-suffix' -- -std=c23
+
+void bit_precise_literal_suffix() {
+  // _BitInt()
+
+  static constexpr auto v1 = 1wb;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'wb', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v1 = 1wb;
+  // CHECK-MESSAGES-NEXT: ^~~
+  // CHECK-MESSAGES-NEXT: WB{{$}}
+  // CHECK-FIXES: static constexpr auto v1 = 1WB;
+  static_assert(v1 == 1WB);
+
+  static constexpr auto v2 = 1WB; // OK.
+  static_assert(v2 == 1WB);
+
+  // _BitInt() Unsigned
+
+  static constexpr auto v3 = 1wbu;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'wbu', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 1wbu;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: WBU{{$}}
+  // CHECK-FIXES: static constexpr auto v3 = 1WBU;
+  static_assert(v3 == 1WBU);
+
+  static constexpr auto v4 = 1WBu;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'WBu', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v4 = 1WBu;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: WBU{{$}}
+  // CHECK-FIXES: static constexpr auto v4 = 1WBU;
+  static_assert(v4 == 1WBU);
+
+  static constexpr auto v5 = 1wbU;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'wbU', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1wbU;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: WBU{{$}}
+  // CHECK-FIXES: static constexpr auto v5 = 1WBU;
+  static_assert(v5 == 1WBU);
+
+  static constexpr auto v6 = 1WBU; // OK.
+  static_assert(v6 == 1WBU);
+
+  // Unsigned _BitInt()
+
+  static constexpr auto v7 = 1uwb;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'uwb', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v7 = 1uwb;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: UWB{{$}}
+  // CHECK-FIXES: static constexpr auto v7 = 1UWB;
+  static_assert(v7 == 1UWB);
+
+  static constexpr auto v8 = 1uWB;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'uWB', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v8 = 1uWB;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: UWB{{$}}
+  // CHECK-FIXES: static constexpr auto v8 = 1UWB;
+  static_assert(v8 == 1UWB);
+
+  static constexpr auto v9 = 1Uwb;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: integer literal has suffix 'Uwb', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1Uwb;
+  // CHECK-MESSAGES-NEXT: ^~~~
+  // CHECK-MESSAGES-NEXT: UWB{{$}}
+  // CHECK-FIXES: static constexpr auto v9 = 1UWB;
+  static_assert(v9 == 1UWB);
+
+  static constexpr auto v10 = 1UWB; // OK.
+  static_assert(v10 == 1UWB);
+}
+
+void decimal_floating_point_suffix() {
+  // _Decimal32
+
+#if 0
+  static constexpr auto v1 = 1.df;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'df', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v1 = 1.df;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DF{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v1 = 1.DF;
+  static_assert(v1 == 1.DF);
+
+  static constexpr auto v2 = 1.e0df;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'df', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v2 = 1.e0df;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DF{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v2 = 1.e0DF;
+  static_assert(v2 == 1.DF);
+
+  static constexpr auto v3 = 1.DF; // OK.
+  static_assert(v3 == 1.DF);
+
+  static constexpr auto v4 = 1.e0DF; // OK.
+  static_assert(v4 == 1.DF);
+#endif
+
+  // _Decimal64
+
+#if 0
+  static constexpr auto v5 = 1.dd;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'dd', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1.dd;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DD{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v5 = 1.DD;
+  static_assert(v5 == 1.DD);
+
+  static constexpr auto v6 = 1.e0dd;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'dd', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v6 = 1.e0dd;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DD{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v6 = 1.e0DD;
+  static_assert(v6 == 1.DD);
+
+  static constexpr auto v7 = 1.DD; // OK.
+  static_assert(v7 == 1.DD);
+
+  static constexpr auto v8 = 1.e0DD; // OK.
+  static_assert(v8 == 1.DD);
+#endif
+
+  // _Decimal128
+
+#if 0
+  static constexpr auto v9 = 1.dl;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'dl', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1.dl;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DL{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v9 = 1.DL;
+  static_assert(v9 == 1.DL);
+
+  static constexpr auto v10 = 1.e0dl;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'dl', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v10 = 1.e0dl;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: DL{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v10 = 1.e0DL;
+  static_assert(v10 == 1.DL);
+
+  static constexpr auto v11 = 1.DL; // OK.
+  static_assert(v11 == 1.DL);
+
+  static constexpr auto v12 = 1.e0DL; // OK.
+  static_assert(v12 == 1.DL);
+#endif
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-extended-floating-point.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-extended-floating-point.cpp
new file mode 100644
index 0000000000000..abe23e3363766
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/uppercase-literal-suffix-extended-floating-point.cpp
@@ -0,0 +1,248 @@
+// TODO: When Clang adds support for C++23 floating-point types, enable these tests by:
+//    1. Removing all the #if 0 + #endif guards.
+//    2. Removing all occurrences of the string "DISABLED-" in this file.
+//    3. Deleting this message.
+// These suffixes may be relevant to C too: https://github.com/llvm/llvm-project/issues/97335
+
+// RUN: %check_clang_tidy -std=c++23 %s readability-uppercase-literal-suffix %t -- -- -target aarch64-linux-gnu -I %clang_tidy_headers
+
+#include "integral_constant.h"
+#if 0
+#include <stdfloat>
+#endif
+
+void normal_literals() {
+  // std::bfloat16_t
+
+#if 0
+  static constexpr auto v1 = 1.bf16;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'bf16', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v1 = 1.bf16;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: BF16{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v1 = 1.BF16;
+  static_assert(is_same<decltype(v1), const std::bfloat16_t>::value, "");
+  static_assert(v1 == 1.BF16, "");
+
+  static constexpr auto v2 = 1.e0bf16;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'bf16', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v2 = 1.e0bf16;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: BF16{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v2 = 1.e0BF16;
+  static_assert(is_same<decltype(v2), const std::bfloat16_t>::value, "");
+  static_assert(v2 == 1.BF16, "");
+
+  static constexpr auto v3 = 1.BF16; // OK.
+  static_assert(is_same<decltype(v3), const std::bfloat16_t>::value, "");
+  static_assert(v3 == 1.BF16, "");
+
+  static constexpr auto v4 = 1.e0BF16; // OK.
+  static_assert(is_same<decltype(v4), const std::bfloat16_t>::value, "");
+  static_assert(v4 == 1.BF16, "");
+#endif
+
+  // _Float16/std::float16_t
+
+  static constexpr auto v5 = 1.f16;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f16', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v5 = 1.f16;
+  // CHECK-MESSAGES-NEXT: ^ ~
+  // CHECK-MESSAGES-NEXT: F16{{$}}
+  // CHECK-FIXES: static constexpr auto v5 = 1.F16;
+  static_assert(is_same<decltype(v5), const _Float16>::value, "");
+  static_assert(v5 == 1.F16, "");
+
+  static constexpr auto v6 = 1.e0f16;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f16', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v6 = 1.e0f16;
+  // CHECK-MESSAGES-NEXT: ^ ~
+  // CHECK-MESSAGES-NEXT: F16{{$}}
+  // CHECK-FIXES: static constexpr auto v6 = 1.e0F16;
+  static_assert(is_same<decltype(v6), const _Float16>::value, "");
+  static_assert(v6 == 1.F16, "");
+
+  static constexpr auto v7 = 1.F16; // OK.
+  static_assert(is_same<decltype(v7), const _Float16>::value, "");
+  static_assert(v7 == 1.F16, "");
+
+  static constexpr auto v8 = 1.e0F16; // OK.
+  static_assert(is_same<decltype(v8), const _Float16>::value, "");
+  static_assert(v8 == 1.F16, "");
+
+  // std::float32_t
+
+#if 0
+  static constexpr auto v9 = 1.f32;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f32', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v9 = 1.f32;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F32{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v9 = 1.F32;
+  static_assert(is_same<decltype(v9), const std::float32_t>::value, "");
+  static_assert(v9 == 1.F32, "");
+
+  static constexpr auto v10 = 1.e0f32;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f32', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v10 = 1.e0f32;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F32{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v10 = 1.e0F32;
+  static_assert(is_same<decltype(v10), const std::float32_t>::value, "");
+  static_assert(v10 == 1.F32, "");
+
+  static constexpr auto v11 = 1.F32; // OK.
+  static_assert(is_same<decltype(v11), const std::float32_t>::value, "");
+  static_assert(v11 == 1.F32, "");
+
+  static constexpr auto v12 = 1.e0F32; // OK.
+  static_assert(is_same<decltype(v12), const std::float32_t>::value, "");
+  static_assert(v12 == 1.F32, "");
+#endif
+
+  // std::float64_t
+
+#if 0
+  static constexpr auto v13 = 1.f64;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f64', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v13 = 1.f64;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F64{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v13 = 1.F64;
+  static_assert(is_same<decltype(v13), const std::float64_t>::value, "");
+  static_assert(v13 == 1.F64, "");
+
+  static constexpr auto v14 = 1.e0f64;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f64', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v14 = 1.e0f64;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F64{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v14 = 1.e0F64;
+  static_assert(is_same<decltype(v14), const std::float64_t>::value, "");
+  static_assert(v14 == 1.F64, "");
+
+  static constexpr auto v15 = 1.F64; // OK.
+  static_assert(is_same<decltype(v15), const std::float64_t>::value, "");
+  static_assert(v15 == 1.F64, "");
+
+  static constexpr auto v16 = 1.e0F64; // OK.
+  static_assert(is_same<decltype(v16), const std::float64_t>::value, "");
+  static_assert(v16 == 1.F64, "");
+#endif
+
+  // std::float128_t
+
+#if 0
+  static constexpr auto v17 = 1.f128;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f128', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v17 = 1.f128;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F128{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v17 = 1.F128;
+  static_assert(is_same<decltype(v17), const std::float128_t>::value, "");
+  static_assert(v17 == 1.F128, "");
+
+  static constexpr auto v18 = 1.e0f128;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:31: warning: floating point literal has suffix 'f128', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v18 = 1.e0f128;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^ ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F128{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v18 = 1.e0F128;
+  static_assert(is_same<decltype(v18), const std::float128_t>::value, "");
+  static_assert(v18 == 1.F128, "");
+
+  static constexpr auto v19 = 1.F128; // OK.
+  static_assert(is_same<decltype(v19), const std::float128_t>::value, "");
+  static_assert(v19 == 1.F128, "");
+
+  static constexpr auto v20 = 1.e0F128; // OK.
+  static_assert(is_same<decltype(v20), const std::float128_t>::value, "");
+  static_assert(v20 == 1.F128, "");
+#endif
+}
+
+void hexadecimal_literals() {
+  // std::bfloat16_t
+
+#if 0
+  static constexpr auto v1 = 0xfp0bf16;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'bf16', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v1 = 0xfp0bf16;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^    ~
+  // DISABLED-CHECK-MESSAGES-NEXT: BF16{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v1 = 0xfp0BF16;
+  static_assert(is_same<decltype(v1), const std::bfloat16_t>::value, "");
+  static_assert(v1 == 0xfp0BF16, "");
+
+  static constexpr auto v2 = 0xfp0BF16; // OK.
+  static_assert(is_same<decltype(v2), const std::bfloat16_t>::value, "");
+  static_assert(v2 == 0xfp0BF16, "");
+#endif
+
+  // _Float16/std::float16_t
+
+  static constexpr auto v3 = 0xfp0f16;
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f16', which is not uppercase
+  // CHECK-MESSAGES-NEXT: static constexpr auto v3 = 0xfp0f16;
+  // CHECK-MESSAGES-NEXT: ^    ~
+  // CHECK-MESSAGES-NEXT: F16{{$}}
+  // CHECK-FIXES: static constexpr auto v3 = 0xfp0F16;
+  static_assert(is_same<decltype(v3), const _Float16>::value, "");
+  static_assert(v3 == 0xfp0F16, "");
+
+  static constexpr auto v4 = 0xfp0F16; // OK.
+  static_assert(is_same<decltype(v4), const _Float16>::value, "");
+  static_assert(v4 == 0xfp0F16, "");
+
+  // std::float32_t
+
+#if 0
+  static constexpr auto v5 = 0xfp0f32;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f32', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v5 = 0xfp0f32;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^    ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F32{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v5 = 0xfp0F32;
+  static_assert(is_same<decltype(v5), const std::float32_t>::value, "");
+  static_assert(v5 == 0xfp0F32, "");
+
+  static constexpr auto v6 = 0xfp0F32; // OK.
+  static_assert(is_same<decltype(v6), const std::float32_t>::value, "");
+  static_assert(v6 == 0xfp0F32, "");
+#endif
+
+  // std::float64_t
+
+#if 0
+  static constexpr auto v7 = 0xfp0f64;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f64', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v7 = 0xfp0f64;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^    ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F64{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v7 = 0xfp0F64;
+  static_assert(is_same<decltype(v7), const std::float64_t>::value, "");
+  static_assert(v7 == 0xfp0F64, "");
+
+  static constexpr auto v8 = 0xfp0F64; // OK.
+  static_assert(is_same<decltype(v8), const std::float64_t>::value, "");
+  static_assert(v8 == 0xfp0F64, "");
+#endif
+
+  // std::float128_t
+
+#if 0
+  static constexpr auto v9 = 0xfp0f128;
+  // DISABLED-CHECK-MESSAGES: :[[@LINE-1]]:30: warning: floating point literal has suffix 'f128', which is not uppercase
+  // DISABLED-CHECK-MESSAGES-NEXT: static constexpr auto v9 = 0xfp0f128;
+  // DISABLED-CHECK-MESSAGES-NEXT: ^    ~
+  // DISABLED-CHECK-MESSAGES-NEXT: F128{{$}}
+  // DISABLED-CHECK-FIXES: static constexpr auto v9 = 0xfp0F128;
+  static_assert(is_same<decltype(v9), const std::float128_t>::value, "");
+  static_assert(v9 == 0xfp0F128, "");
+
+  static constexpr auto v10 ...
[truncated]

// 3. Deleting this message.
// These suffixes may be relevant to C too: https://github.com/llvm/llvm-project/issues/97335

// RUN: %check_clang_tidy -std=c++23 %s readability-uppercase-literal-suffix %t -- -- -target aarch64-linux-gnu -I %clang_tidy_headers
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need this target aarch64-linux-gnu? Seems odd to me, maybe we can delete it?

Copy link
Contributor

Choose a reason for hiding this comment

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

+1, we use the default target (x86_64) in tests

Copy link
Contributor Author

@localspook localspook Aug 25, 2025

Choose a reason for hiding this comment

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

I copied this from the existing uppercase-literal-suffix-float16.cpp test; apparently clang doesn't support _Float16 on x86_64. Should I add a TODO to remove the -target once compiler support improves?


#include "integral_constant.h"
#if 0
#include <stdfloat>
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a real header or a fake one from %clang_tidy_headers? We should not depend on real-world headers in tests. If it is expected to be real, we should create a fake one instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added stub stdfloat. I can't add the actual type aliases yet though, we need to wait for compiler support.

Comment on lines 16 to 18
// CHECK-MESSAGES-NEXT: static constexpr auto v1 = 1wb;
// CHECK-MESSAGES-NEXT: ^~~
// CHECK-MESSAGES-NEXT: WB{{$}}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need these messages? I haven't seen them in other tests and they look obsolete (considering we have CHECK-FIXES).

Copy link
Contributor Author

@localspook localspook Aug 26, 2025

Choose a reason for hiding this comment

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

I'm parroting the existing uppercase-literal-suffix-* tests here. I agree that they seem unnecessary; should I go and remove them from the existing tests too? (ditto for grep, it looks like it could just be a cp)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think yes, remove unnecessary CHECK-FIXES-NEXT.
But i still don't understand what we need cp for. Could we just make two separate test files instead of creating one on the fly?

Copy link
Contributor Author

@localspook localspook Aug 26, 2025

Choose a reason for hiding this comment

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

The idea is that we copy the test, run the check and apply fixes, then run the check again to make sure there are no more warnings. (I’m not sure how much value this adds on top of the usual CHECK-FIXES. I wouldn’t be against removing it too.) But making a separate file would cause duplication: we would need to check in both the original and the fixed file, right?

Copy link
Contributor

@vbvictor vbvictor Aug 26, 2025

Choose a reason for hiding this comment

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

Thank you for description! A separate file won't do much, and I don't see any value in running check multiple times over corrected file, so I vote to remove it. If this thing is ever needed, I think it should go to check_clang_tidy.py as an opt-in for tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

The only benefit I see is to don't write GOOD cases. So the workflow would be like:

  1. Write only "bad" cases in test file
  2. First run corrects that they are all become "good"
  3. Second run checks that none of "good" are not flagged as FP.

So since we write all good cases explicitly (I hope so) then we don't need double-run.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If this thing is ever needed, I think it should go to check_clang_tidy.py as an opt-in for tests.

+1

I’ll go and remove it from this and existing tests

// 3. Deleting this message.

// RUN: %check_clang_tidy -std=c23-or-later %s readability-uppercase-literal-suffix %t
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.c
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need grep? What if build env doesn't have it, e.g. Windows

Copy link
Contributor

Choose a reason for hiding this comment

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

Our win tests may have grep but downstream users may not


#include "integral_constant.h"

void integer_suffix() {
static constexpr auto v0 = __LINE__; // synthetic
static_assert(v0 == 9 || v0 == 5, "");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm removing this assert (here and in 2 other places) because it depends on absolute line numbers. I considered changing it to relative numbers (static_assert(v0 == (__LINE__ - 1), "");), but I think it's better to remove it, because all we care about is that the check doesn't fire here, we don't care about line numbers at all.

Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

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

LGTM

@localspook localspook merged commit a49030e into llvm:main Aug 31, 2025
10 checks passed
@localspook localspook deleted the more-literal-suffixes branch August 31, 2025 23:38
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 2, 2025

LLVM Buildbot has detected a new failure on builder clang-armv7-2stage running on linaro-clang-armv7-2stage while building clang-tools-extra at step 18 "install lnt dependencies".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/79/builds/2119

Here is the relevant piece of the build log for the reference
Step 18 (install lnt dependencies) failure: '/home/tcwg-buildbot/worker/clang-armv7-2stage/test/sandbox/Scripts/python -m ...' (failure)
Upon execvpe b'/home/tcwg-buildbot/worker/clang-armv7-2stage/test/sandbox/Scripts/python' [b'/home/tcwg-buildbot/worker/clang-armv7-2stage/test/sandbox/Scripts/python', b'-m', b'pip', b'install', b'setuptools'] in environment id 4107452240
:Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/twisted/internet/process.py", line 397, in _fork
    self._execChild(path, uid, gid, executable, args, environment)
  File "/usr/lib/python3/dist-packages/twisted/internet/process.py", line 468, in _execChild
    os.execvpe(executable, args, environment)
  File "/usr/lib/python3.10/os.py", line 584, in execvpe
    _execvpe(file, args, env)
  File "/usr/lib/python3.10/os.py", line 598, in _execvpe
    exec_func(file, *argrest)
FileNotFoundError: [Errno 2] No such file or directory: b'/home/tcwg-buildbot/worker/clang-armv7-2stage/test/sandbox/Scripts/python'

@localspook
Copy link
Contributor Author

localspook commented Sep 2, 2025

That CI failure looks unrelated. (I see the same error on clang-armv8-lld-2stage, but that's it.)

@nettle
Copy link

nettle commented Sep 2, 2025

I'm sorry, but what about 0x1b test case?

@localspook
Copy link
Contributor Author

What do you mean by that? 🤔

@nettle
Copy link

nettle commented Sep 2, 2025

Sorry for not being clear enough. My point is the checker will report literal 0x1b as an issue since b is in the list of suffixes, however 0x1b is a valid hex integer literal. Actually we are missing LIT tests for hex literals.

@localspook
Copy link
Contributor Author

Good catch, thank you! Opened #156584 with a fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants