https://github.com/shivaansharma created https://github.com/llvm/llvm-project/pull/208162
Fixes #208078. The `bugprone-misplaced-widening-cast` check currently warns on casts like `static_cast<std::size_t>(x * 2)` even when `x` is a `constexpr` variable and the multiplication provably does not overflow the narrower type. This is pointless churn, since the compiler can prove the cast is safe as written. This PR teaches `getMaxCalculationWidth` to evaluate `constexpr` operands at compile time and use their actual bit-width instead of the declared type's full width, when the value is non-negative (so overflow cases still warn). Non-`constexpr` `const` values are unaffected and still warn, matching existing check semantics. See the linked issue for the original discussion, including the counter-examples this PR is designed to preserve (non-constexpr const, and constexpr values that do overflow). >From d890f389a887393e78330f948ba4111b613f22d3 Mon Sep 17 00:00:00 2001 From: shivaan sharma <[email protected]> Date: Wed, 8 Jul 2026 13:01:01 +0530 Subject: [PATCH] [clang-tidy] Don't warn on misplaced-widening-cast for provably non-overflowing constexpr values The check now evaluates constexpr operands at compile time and skips the diagnostic when the value provably does not overflow the narrower type. Non-constexpr const values and constexpr values that do overflow still warn as before. Fixes #208078 --- .../bugprone/MisplacedWideningCastCheck.cpp | 9 ++++++ clang-tools-extra/docs/ReleaseNotes.rst | 7 +++++ .../bugprone/misplaced-widening-cast.rst | 28 +++++++++++++++++++ .../misplaced-widening-cast-explicit-only.cpp | 24 ++++++++++++++++ 4 files changed, 68 insertions(+) diff --git a/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp index 61bbbf8af2abd..9b405cf80bdff 100644 --- a/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp @@ -90,6 +90,15 @@ static unsigned getMaxCalculationWidth(const ASTContext &Context, return T->isIntegerType() ? Context.getIntWidth(T) : 1024U; } else if (const auto *I = dyn_cast<IntegerLiteral>(E)) { return I->getValue().getActiveBits(); + } else if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) { + if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + if (VD->isConstexpr()) { + Expr::EvalResult Result; + if (E->EvaluateAsInt(Result, Context) && + !Result.Val.getInt().isNegative()) + return Result.Val.getInt().getActiveBits(); + } + } } return Context.getIntWidth(E->getType()); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index b85ece288881e..e29dfa3c926cd 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -417,6 +417,13 @@ Changes in existing checks positive on bit field assignments when the `CheckImplicitCasts` option is enabled. The check now uses the actual bit field width instead of the declared type to determine if widening occurs. + +- Improved :doc:`bugprone-misplaced-widening-cast + <clang-tidy/checks/bugprone/misplaced-widening-cast>` check by fixing a false + positive when the calculation operand is a ``constexpr`` variable whose + compile-time value provably fits without overflow in the narrower type. The + check still warns when the operand is a non-``constexpr`` ``const`` or when + the ``constexpr`` value does overflow. - Improved :doc:`bugprone-move-forwarding-reference <clang-tidy/checks/bugprone/move-forwarding-reference>` check by fixing some diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/misplaced-widening-cast.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/misplaced-widening-cast.rst index cec49c55309ad..dedb1662ec8a7 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/misplaced-widening-cast.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/misplaced-widening-cast.rst @@ -57,6 +57,34 @@ written for this code: return (double)(x * 10.0f); } +Constexpr operands +------------------ + +If the calculation operand is a ``constexpr`` variable, the check evaluates +its compile-time value. If the value provably does not overflow the narrower +type, no warning is issued, since the cast is then not actually masking any +loss of precision: + +.. code-block:: c++ + + void f(std::size_t (*bar)(std::size_t)) { + constexpr int x = 256; + bar(static_cast<std::size_t>(x * 2)); // No warning: x * 2 == 512, + // which fits in int. + } + +A plain ``const`` (not ``constexpr``) variable does not get this exception, +since the check does not attempt to prove non-``constexpr`` values are +compile-time constants. A ``constexpr`` value that does overflow the narrower +type is still diagnosed as before: + +.. code-block:: c++ + + void f(std::size_t (*bar)(std::size_t)) { + constexpr int x = -1; + bar(static_cast<std::size_t>(x * 2)); // Still warns: overflows int. + } + Options ------- diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/misplaced-widening-cast-explicit-only.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/misplaced-widening-cast-explicit-only.cpp index 56eba15f2cd0e..d5cd43de67bb7 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/misplaced-widening-cast-explicit-only.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/misplaced-widening-cast-explicit-only.cpp @@ -80,3 +80,27 @@ void nextDay(DaysEnum day) { if (day < SUN) day = static_cast<DaysEnum>(day + 1); } + +// Constexpr values that are provably non-overflowing should not warn, +// even though the cast is technically "misplaced" relative to the +// multiplication. See https://github.com/llvm/llvm-project/issues/208078 +void constexprNoOverflow() { + constexpr int x = 256; + long l = static_cast<long>(x * 2); +} + +// A non-constexpr const does NOT get this exception: 'const' alone does not +// prove the compiler evaluated it as a compile-time constant expression from +// the check's point of view, and this mirrors clang's own narrowing checks. +void constNotConstexprStillWarns() { + const int x = 256; + long l = static_cast<long>(x * 2); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long' +} + +// A constexpr value that DOES overflow the narrow type must still warn. +void constexprOverflowStillWarns() { + constexpr int x = -1; + long l = static_cast<long>(x * 2); + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'int' to 'long' +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
