[clang] [clang-tools-extra] [clang-tidy] bugprone-implicit-widening ignores unsigned consts (PR #101073)

Chris Warner via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 12 10:29:55 PDT 2024


https://github.com/cwarner-8702 updated https://github.com/llvm/llvm-project/pull/101073

>From 24f52fbfb9117a6498769cebdc7b09ecbd7e019e Mon Sep 17 00:00:00 2001
From: Chris Warner <cwarner at esri.com>
Date: Wed, 17 Jul 2024 11:22:39 -0700
Subject: [PATCH 1/5] [clang-tidy] bugprone-implicit-widening ignores unsigned
 consts

---
 ...citWideningOfMultiplicationResultCheck.cpp | 12 +++---
 clang-tools-extra/docs/ReleaseNotes.rst       |  5 +++
 ...icit-widening-of-multiplication-result.rst |  3 +-
 ...ing-of-multiplication-result-constants.cpp | 15 +++++--
 clang/include/clang/AST/Expr.h                | 11 +++--
 clang/lib/AST/ExprConstant.cpp                | 40 +++++++++++++------
 6 files changed, 60 insertions(+), 26 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
index 46bf20e34ce041..05c37acda11912 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
@@ -88,13 +88,15 @@ void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
 
   // Is the expression a compile-time constexpr that we know can fit in the
   // source type?
-  if (IgnoreConstantIntExpr && ETy->isIntegerType() &&
-      !ETy->isUnsignedIntegerType()) {
-    if (const auto ConstExprResult = E->getIntegerConstantExpr(*Context)) {
+  if (IgnoreConstantIntExpr && ETy->isIntegerType()) {
+    if (const auto ConstExprResult =
+            E->getIntegerConstantExpr(*Context, nullptr, true)) {
       const auto TypeSize = Context->getTypeSize(ETy);
+      const auto Unsigned = ETy->isUnsignedIntegerType();
+
       llvm::APSInt WidenedResult = ConstExprResult->extOrTrunc(TypeSize);
-      if (WidenedResult <= llvm::APSInt::getMaxValue(TypeSize, false) &&
-          WidenedResult >= llvm::APSInt::getMinValue(TypeSize, false))
+      if (WidenedResult <= llvm::APSInt::getMaxValue(TypeSize, Unsigned) &&
+          WidenedResult >= llvm::APSInt::getMinValue(TypeSize, Unsigned))
         return;
     }
   }
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 1b025e8f90f7ba..b2d2a545b1ab5f 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -104,6 +104,11 @@ New check aliases
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+- Improved :doc:`bugprone-implicit-widening-of-multiplication-result
+  <clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result>` check
+  by allowing the `IgnoreConstantIntExpr` option to apply to both signed and
+  unsigned integer types.
+
 - Improved :doc:`modernize-use-std-format
   <clang-tidy/checks/modernize/use-std-format>` check to support replacing
   member function calls too.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst
index 117310d404f6f4..6d72e970d53fb6 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst
@@ -49,8 +49,7 @@ Options
 
    If the multiplication operands are compile-time constants (like literals or
    are ``constexpr``) and fit within the source expression type, do not emit a
-   diagnostic or suggested fix.  Only considers expressions where the source
-   expression is a signed integer type.  Defaults to ``false``.
+   diagnostic or suggested fix.  Defaults to ``false``.
 
 Examples:
 
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/implicit-widening-of-multiplication-result-constants.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/implicit-widening-of-multiplication-result-constants.cpp
index d7ab8a7a44fe68..b337b226001359 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/implicit-widening-of-multiplication-result-constants.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/implicit-widening-of-multiplication-result-constants.cpp
@@ -40,6 +40,11 @@ long t5() {
   return 1 * min_int;
 }
 
+long t6() {
+  const unsigned int a = 4294967295u; // unsigned int max
+  return a * 1u;
+}
+
 unsigned long n0() {
   const int a = 1073741824; // 1/2 of int32_t max
   const int b = 3;
@@ -50,7 +55,11 @@ unsigned long n0() {
   // CHECK-MESSAGES: :[[@LINE-4]]:10: note: perform multiplication in a wider type
 }
 
-double n1() {
-  const long a = 100000000;
-  return a * 400;
+long n1() {
+  const unsigned int a = 4294967295u; // unsigned int max
+  return a * 3u;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'
+  // CHECK-MESSAGES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-MESSAGES:                  static_cast<long>( )
+  // CHECK-MESSAGES: :[[@LINE-4]]:10: note: perform multiplication in a wider type
 }
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 7bacf028192c65..d92df1da3134cc 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -551,11 +551,15 @@ class Expr : public ValueStmt {
   /// and fill in Loc (if specified) with the location of the invalid
   /// expression.
   ///
+  /// If CheckUnsignedOverflow is true, the i-c-e is of an unsigned type, and
+  /// the i-c-e result cannot fit into the expression type without overflowing,
+  /// std::nullopt is returned.
+  ///
   /// Note: This does not perform the implicit conversions required by C++11
   /// [expr.const]p5.
   std::optional<llvm::APSInt>
-  getIntegerConstantExpr(const ASTContext &Ctx,
-                         SourceLocation *Loc = nullptr) const;
+  getIntegerConstantExpr(const ASTContext &Ctx, SourceLocation *Loc = nullptr,
+                         bool CheckUnsignedOverflow = false) const;
   bool isIntegerConstantExpr(const ASTContext &Ctx,
                              SourceLocation *Loc = nullptr) const;
 
@@ -569,7 +573,8 @@ class Expr : public ValueStmt {
   /// Note: This does not perform the implicit conversions required by C++11
   /// [expr.const]p5.
   bool isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result = nullptr,
-                           SourceLocation *Loc = nullptr) const;
+                           SourceLocation *Loc = nullptr,
+                           bool CheckUnsignedOverflow = false) const;
 
   /// isPotentialConstantExpr - Return true if this function's definition
   /// might be usable in a constant expression in C++11, if it were marked
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index d46f57521a97d3..6a704b9e91aeda 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -980,6 +980,12 @@ namespace {
     /// is set; this is used when evaluating ICEs in C.
     bool CheckingForUndefinedBehavior = false;
 
+    /// Whether we're checking for an expression that causing the unsigned
+    /// integer expression type to overflow and wrap-around back to 0 when
+    /// evaluating an ICE.  If set to true, the ICE evaluation will fail as
+    /// though the expression could not be calculated at compile time
+    bool CheckingForUnsignedIntegerOverflow = false;
+
     enum EvaluationMode {
       /// Evaluate as a constant expression. Stop if we find that the expression
       /// is not a constant expression.
@@ -2791,24 +2797,28 @@ static bool truncateBitfieldValue(EvalInfo &Info, const Expr *E,
 
 /// Perform the given integer operation, which is known to need at most BitWidth
 /// bits, and check for overflow in the original type (if that type was not an
-/// unsigned type).
+/// unsigned type or EvalInfo.CheckingForUnsignedIntegerOverflow is true).
 template<typename Operation>
 static bool CheckedIntArithmetic(EvalInfo &Info, const Expr *E,
                                  const APSInt &LHS, const APSInt &RHS,
                                  unsigned BitWidth, Operation Op,
                                  APSInt &Result) {
-  if (LHS.isUnsigned()) {
+  if (LHS.isUnsigned() && !Info.CheckingForUnsignedIntegerOverflow) {
     Result = Op(LHS, RHS);
     return true;
   }
 
-  APSInt Value(Op(LHS.extend(BitWidth), RHS.extend(BitWidth)), false);
+  APSInt Value(Op(LHS.extend(BitWidth), RHS.extend(BitWidth)),
+               LHS.isUnsigned());
   Result = Value.trunc(LHS.getBitWidth());
   if (Result.extend(BitWidth) != Value) {
+    if (Result.isUnsigned())
+      return false;
     if (Info.checkingForUndefinedBehavior())
       Info.Ctx.getDiagnostics().Report(E->getExprLoc(),
                                        diag::warn_integer_constant_overflow)
-          << toString(Result, 10, Result.isSigned(), /*formatAsCLiteral=*/false,
+          << toString(Result, 10, Result.isSigned(),
+                      /*formatAsCLiteral=*/false,
                       /*UpperCase=*/true, /*InsertSeparators=*/true)
           << E->getType() << E->getSourceRange();
     return HandleOverflow(Info, E, Value, E->getType());
@@ -16943,17 +16953,16 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
 }
 
 /// Evaluate an expression as a C++11 integral constant expression.
-static bool EvaluateCPlusPlus11IntegralConstantExpr(const ASTContext &Ctx,
-                                                    const Expr *E,
-                                                    llvm::APSInt *Value,
-                                                    SourceLocation *Loc) {
+static bool EvaluateCPlusPlus11IntegralConstantExpr(
+    const ASTContext &Ctx, const Expr *E, llvm::APSInt *Value,
+    SourceLocation *Loc, bool CheckUnsignedOverflow) {
   if (!E->getType()->isIntegralOrUnscopedEnumerationType()) {
     if (Loc) *Loc = E->getExprLoc();
     return false;
   }
 
   APValue Result;
-  if (!E->isCXX11ConstantExpr(Ctx, &Result, Loc))
+  if (!E->isCXX11ConstantExpr(Ctx, &Result, Loc, CheckUnsignedOverflow))
     return false;
 
   if (!Result.isInt()) {
@@ -16973,7 +16982,8 @@ bool Expr::isIntegerConstantExpr(const ASTContext &Ctx,
   ExprTimeTraceScope TimeScope(this, Ctx, "isIntegerConstantExpr");
 
   if (Ctx.getLangOpts().CPlusPlus11)
-    return EvaluateCPlusPlus11IntegralConstantExpr(Ctx, this, nullptr, Loc);
+    return EvaluateCPlusPlus11IntegralConstantExpr(Ctx, this, nullptr, Loc,
+                                                   false);
 
   ICEDiag D = CheckICE(this, Ctx);
   if (D.Kind != IK_ICE) {
@@ -16984,7 +16994,8 @@ bool Expr::isIntegerConstantExpr(const ASTContext &Ctx,
 }
 
 std::optional<llvm::APSInt>
-Expr::getIntegerConstantExpr(const ASTContext &Ctx, SourceLocation *Loc) const {
+Expr::getIntegerConstantExpr(const ASTContext &Ctx, SourceLocation *Loc,
+                             bool CheckUnsignedOverflow) const {
   if (isValueDependent()) {
     // Expression evaluator can't succeed on a dependent expression.
     return std::nullopt;
@@ -16993,7 +17004,8 @@ Expr::getIntegerConstantExpr(const ASTContext &Ctx, SourceLocation *Loc) const {
   APSInt Value;
 
   if (Ctx.getLangOpts().CPlusPlus11) {
-    if (EvaluateCPlusPlus11IntegralConstantExpr(Ctx, this, &Value, Loc))
+    if (EvaluateCPlusPlus11IntegralConstantExpr(Ctx, this, &Value, Loc,
+                                                CheckUnsignedOverflow))
       return Value;
     return std::nullopt;
   }
@@ -17024,7 +17036,8 @@ bool Expr::isCXX98IntegralConstantExpr(const ASTContext &Ctx) const {
 }
 
 bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result,
-                               SourceLocation *Loc) const {
+                               SourceLocation *Loc,
+                               bool CheckUnsignedOverflow) const {
   assert(!isValueDependent() &&
          "Expression evaluator can't be called on a dependent expression.");
 
@@ -17037,6 +17050,7 @@ bool Expr::isCXX11ConstantExpr(const ASTContext &Ctx, APValue *Result,
   SmallVector<PartialDiagnosticAt, 8> Diags;
   Status.Diag = &Diags;
   EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantExpression);
+  Info.CheckingForUnsignedIntegerOverflow = CheckUnsignedOverflow;
 
   APValue Scratch;
   bool IsConstExpr =

>From 3f59abbd16a87ec9296cda453baee5a260ec1635 Mon Sep 17 00:00:00 2001
From: Chris Warner <cwarner at esri.com>
Date: Tue, 30 Jul 2024 08:55:28 -0700
Subject: [PATCH 2/5] CR: EugeneZelenko

---
 .../bugprone/implicit-widening-of-multiplication-result.rst | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst
index 6d72e970d53fb6..1e3a783eab2324 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst
@@ -37,19 +37,19 @@ Options
 .. option:: UseCXXStaticCastsInCppSources
 
    When suggesting fix-its for C++ code, should C++-style ``static_cast<>()``'s
-   be suggested, or C-style casts. Defaults to ``true``.
+   be suggested, or C-style casts. Defaults to `true`.
 
 .. option:: UseCXXHeadersInCppSources
 
    When suggesting to include the appropriate header in C++ code,
    should ``<cstddef>`` header be suggested, or ``<stddef.h>``.
-   Defaults to ``true``.
+   Defaults to `true`.
 
 .. option:: IgnoreConstantIntExpr
 
    If the multiplication operands are compile-time constants (like literals or
    are ``constexpr``) and fit within the source expression type, do not emit a
-   diagnostic or suggested fix.  Defaults to ``false``.
+   diagnostic or suggested fix.  Defaults to `false`.
 
 Examples:
 

>From ac27ca9624bb3da5db4b80c037fe8a1547fc3714 Mon Sep 17 00:00:00 2001
From: Chris Warner <cwarner at esri.com>
Date: Mon, 19 Aug 2024 15:30:35 -0700
Subject: [PATCH 3/5] Add test to unittests

---
 clang/unittests/AST/ASTExprTest.cpp | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/clang/unittests/AST/ASTExprTest.cpp b/clang/unittests/AST/ASTExprTest.cpp
index 5ec6aea8edba38..d278364f154ef5 100644
--- a/clang/unittests/AST/ASTExprTest.cpp
+++ b/clang/unittests/AST/ASTExprTest.cpp
@@ -108,3 +108,19 @@ TEST(ASTExpr, InitListIsConstantInitialized) {
   (void)FooInit->updateInit(Ctx, 2, Ref);
   EXPECT_FALSE(FooInit->isConstantInitializer(Ctx, false));
 }
+
+TEST(ASTExpr, CheckForUnsignedOverflowICE) {
+  auto AST = buildASTFromCode(R"cpp(
+    unsigned u = 1'000'000'000u * 8u;
+  )cpp");
+  ASTContext &Ctx = AST->getASTContext();
+  const VarDecl *Var = getVariableNode(AST.get(), "u");
+
+  const auto ICE = Var->getInit()->getIntegerConstantExpr(Ctx);
+  EXPECT_TRUE(ICE.has_value());
+  EXPECT_TRUE(ICE->isUnsigned());
+  EXPECT_EQ(3'705'032'704u, *ICE);
+
+  const auto ICEOverflowCheck = Var->getInit()->getIntegerConstantExpr(Ctx, nullptr, true);
+  EXPECT_FALSE(ICEOverflowCheck.has_value());
+}

>From 276e48b39864d2c91f97f501d9bc5ed59ca52ced Mon Sep 17 00:00:00 2001
From: Chris Warner <cwarner at esri.com>
Date: Tue, 20 Aug 2024 09:40:35 -0700
Subject: [PATCH 4/5] CR: another test

---
 clang/unittests/AST/ASTExprTest.cpp | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/clang/unittests/AST/ASTExprTest.cpp b/clang/unittests/AST/ASTExprTest.cpp
index d278364f154ef5..f9d451d8c1927b 100644
--- a/clang/unittests/AST/ASTExprTest.cpp
+++ b/clang/unittests/AST/ASTExprTest.cpp
@@ -109,6 +109,24 @@ TEST(ASTExpr, InitListIsConstantInitialized) {
   EXPECT_FALSE(FooInit->isConstantInitializer(Ctx, false));
 }
 
+TEST(ASTExpr, NoUnsignedOverflowICE) {
+  auto AST = buildASTFromCode(R"cpp(
+    unsigned u = 1u * 8u;
+  )cpp");
+  ASTContext &Ctx = AST->getASTContext();
+  const VarDecl *Var = getVariableNode(AST.get(), "u");
+
+  const auto ICE = Var->getInit()->getIntegerConstantExpr(Ctx);
+  EXPECT_TRUE(ICE.has_value());
+  EXPECT_TRUE(ICE->isUnsigned());
+  EXPECT_EQ(8u, *ICE);
+
+  const auto ICEOverflowCheck = Var->getInit()->getIntegerConstantExpr(Ctx, nullptr, true);
+  EXPECT_TRUE(ICEOverflowCheck.has_value());
+  EXPECT_TRUE(ICEOverflowCheck->isUnsigned());
+  EXPECT_EQ(8u, *ICEOverflowCheck);
+}
+
 TEST(ASTExpr, CheckForUnsignedOverflowICE) {
   auto AST = buildASTFromCode(R"cpp(
     unsigned u = 1'000'000'000u * 8u;

>From 6913f631d7869aa27585a916bd0980a83fd0cd05 Mon Sep 17 00:00:00 2001
From: Chris Warner <cwarner at esri.com>
Date: Thu, 29 Aug 2024 08:45:24 -0700
Subject: [PATCH 5/5] CR: clang-format

---
 clang/unittests/AST/ASTExprTest.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/clang/unittests/AST/ASTExprTest.cpp b/clang/unittests/AST/ASTExprTest.cpp
index f9d451d8c1927b..4edb87a6c0f842 100644
--- a/clang/unittests/AST/ASTExprTest.cpp
+++ b/clang/unittests/AST/ASTExprTest.cpp
@@ -121,7 +121,8 @@ TEST(ASTExpr, NoUnsignedOverflowICE) {
   EXPECT_TRUE(ICE->isUnsigned());
   EXPECT_EQ(8u, *ICE);
 
-  const auto ICEOverflowCheck = Var->getInit()->getIntegerConstantExpr(Ctx, nullptr, true);
+  const auto ICEOverflowCheck =
+      Var->getInit()->getIntegerConstantExpr(Ctx, nullptr, true);
   EXPECT_TRUE(ICEOverflowCheck.has_value());
   EXPECT_TRUE(ICEOverflowCheck->isUnsigned());
   EXPECT_EQ(8u, *ICEOverflowCheck);
@@ -139,6 +140,7 @@ TEST(ASTExpr, CheckForUnsignedOverflowICE) {
   EXPECT_TRUE(ICE->isUnsigned());
   EXPECT_EQ(3'705'032'704u, *ICE);
 
-  const auto ICEOverflowCheck = Var->getInit()->getIntegerConstantExpr(Ctx, nullptr, true);
+  const auto ICEOverflowCheck =
+      Var->getInit()->getIntegerConstantExpr(Ctx, nullptr, true);
   EXPECT_FALSE(ICEOverflowCheck.has_value());
 }



More information about the cfe-commits mailing list