https://github.com/shivaansharma updated 
https://github.com/llvm/llvm-project/pull/208162

>From 1ad53ffbea88a5dd7a4bcaf34b9975ded40f824a 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   | 26 ++++++++---
 .../bugprone/MisplacedWideningCastCheck.h     |  1 +
 clang-tools-extra/docs/ReleaseNotes.rst       |  8 ++++
 .../bugprone/misplaced-widening-cast.rst      | 43 +++++++++++++++++++
 .../misplaced-widening-cast-explicit-only.cpp | 24 +++++++++++
 5 files changed, 96 insertions(+), 6 deletions(-)

diff --git 
a/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp 
b/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp
index 61bbbf8af2abd..378f368119a5e 100644
--- a/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp
@@ -17,11 +17,13 @@ namespace clang::tidy::bugprone {
 MisplacedWideningCastCheck::MisplacedWideningCastCheck(
     StringRef Name, ClangTidyContext *Context)
     : ClangTidyCheck(Name, Context),
-      CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
+      CheckImplicitCasts(Options.get("CheckImplicitCasts", false)),
+      
IgnoreConstexprOverflowProven(Options.get("IgnoreConstexprOverflowProven", 
false)) {}
 
 void MisplacedWideningCastCheck::storeOptions(
     ClangTidyOptions::OptionMap &Opts) {
   Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
+  Options.store(Opts, "IgnoreConstexprOverflowProven", 
IgnoreConstexprOverflowProven);
 }
 
 void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
@@ -55,12 +57,13 @@ void 
MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
 }
 
 static unsigned getMaxCalculationWidth(const ASTContext &Context,
-                                       const Expr *E) {
+                                       const Expr *E,
+                                       bool IgnoreConstexprOverflowProven) {
   E = E->IgnoreParenImpCasts();
 
   if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
-    const unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
-    const unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
+    const unsigned LHSWidth = getMaxCalculationWidth(Context, 
Bop->getLHS(),IgnoreConstexprOverflowProven);
+    const unsigned RHSWidth = getMaxCalculationWidth(Context, 
Bop->getRHS(),IgnoreConstexprOverflowProven);
     if (Bop->getOpcode() == BO_Mul)
       return LHSWidth + RHSWidth;
     if (Bop->getOpcode() == BO_Add)
@@ -90,8 +93,18 @@ 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 (IgnoreConstexprOverflowProven) {
+    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());
 }
 
@@ -235,7 +248,8 @@ void MisplacedWideningCastCheck::check(const 
MatchFinder::MatchResult &Result) {
 
   // Don't write a warning if we can easily see that the result is not
   // truncated.
-  if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))
+  if (Context.getIntWidth(CalcType) >=
+      getMaxCalculationWidth(Context, Calc, IgnoreConstexprOverflowProven))
     return;
 
   diag(Cast->getBeginLoc(), "either cast from %0 to %1 is ineffective, or "
diff --git a/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.h 
b/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.h
index 151bbcf028783..4c14d242ff7f7 100644
--- a/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.h
@@ -34,6 +34,7 @@ class MisplacedWideningCastCheck : public ClangTidyCheck {
 
 private:
   const bool CheckImplicitCasts;
+  const bool IgnoreConstexprOverflowProven;
 };
 
 } // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index b85ece288881e..e210f43878158 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -417,6 +417,14 @@ 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 adding the
+  :option:`IgnoreConstexprOverflowProven` option to suppress false positives
+  when the calculation operand is a ``constexpr`` variable whose compile-time
+  value provably fits inside the narrower type without overflow. The check
+  still warns when the operand is a non-``constexpr`` ``const``, or when the
+  ``constexpr`` value is negative or overflows.
 
 - 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..de929006ef87f 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
@@ -60,6 +60,49 @@ written for this code:
 Options
 -------
 
+.. option:: IgnoreConstexprOverflowProven
+
+   When set to `true`, the check evaluates the compile-time value of 
``constexpr`` 
+   calculation operands. If the value provably does not overflow the narrower 
+   type and is non-negative, the warning is suppressed because the widening 
cast 
+   is not masking an unintended truncation. Default is `false`.
+
+Constexpr Operands
+~~~~~~~~~~~~~~~~~~
+
+If :option:`IgnoreConstexprOverflowProven` is set to `true` and 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:
+
+.. code-block:: c++
+
+    void bar(std::size_t);
+
+    void f() {
+        constexpr int x = 256;
+        bar(static_cast<std::size_t>(x * 2)); // No warning: x * 2 == 512,
+                                               // which safely fits in int.
+    }
+
+A plain ``const`` (not ``constexpr``) variable does not get this exception,
+since the check does not evaluate non-``constexpr`` values as compile-time 
+constants. 
+
+A ``constexpr`` value that results in a negative value or signed integer 
+overflow will still be diagnosed:
+
+.. code-block:: c++
+
+    void bar(std::size_t);
+
+    void f() {
+        constexpr int x = 2147483647;         // INT_MAX
+        bar(static_cast<std::size_t>(x * 2)); // Still warns: calculation 
overflows int.
+    }
+    
+Options
+-------
+
 .. option:: CheckImplicitCasts
 
    If `true`, enables detection of implicit casts. Default is `false`.
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

Reply via email to