[clang-tools-extra] [clang-tidy] Don't warn on misplaced-widening-cast for provably non-o… (PR #208162)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 8 00:48:19 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tools-extra
@llvm/pr-subscribers-clang-tidy
Author: shivaansharma
<details>
<summary>Changes</summary>
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).
---
Full diff: https://github.com/llvm/llvm-project/pull/208162.diff
4 Files Affected:
- (modified) clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp (+9)
- (modified) clang-tools-extra/docs/ReleaseNotes.rst (+7)
- (modified) clang-tools-extra/docs/clang-tidy/checks/bugprone/misplaced-widening-cast.rst (+28)
- (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/misplaced-widening-cast-explicit-only.cpp (+24)
``````````diff
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'
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/208162
More information about the cfe-commits
mailing list