[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 04:39:53 PDT 2026
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 <shivaansharma16 at gmail.com>
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'
+}
More information about the cfe-commits
mailing list