Skip to content

[clang-tidy] Add new check readability-use-numeric-limits #127430

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

stellar-aria
Copy link
Contributor

The adds a check that replaces specific numeric literals like 32767 with the equivalent call to std::numeric_limits (such as std::numeric_limits<int16_t>::max()).

Partially addresses #34434, but notably does not handle cases listed in the title post such as ~0 and -1.

Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot
Copy link
Member

llvmbot commented Feb 17, 2025

@llvm/pr-subscribers-clang-tools-extra

@llvm/pr-subscribers-clang-tidy

Author: Katherine Whitlock (stellar-aria)

Changes

The adds a check that replaces specific numeric literals like 32767 with the equivalent call to std::numeric_limits (such as std::numeric_limits&lt;int16_t&gt;::max()).

Partially addresses #34434, but notably does not handle cases listed in the title post such as ~0 and -1.


Full diff: https://github.com/llvm/llvm-project/pull/127430.diff

8 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/CMakeLists.txt (+1)
  • (modified) clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp (+3)
  • (added) clang-tools-extra/clang-tidy/readability/UseNumericLimitsCheck.cpp (+135)
  • (added) clang-tools-extra/clang-tidy/readability/UseNumericLimitsCheck.h (+41)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+5)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/list.rst (+1)
  • (added) clang-tools-extra/docs/clang-tidy/checks/readability/use-numeric-limits.rst (+22)
  • (added) clang-tools-extra/test/clang-tidy/checkers/readability/use-numeric-limits.cpp (+85)
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 8f303c51e1b0d..e06f68bdda73f 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -57,6 +57,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
   UniqueptrDeleteReleaseCheck.cpp
   UppercaseLiteralSuffixCheck.cpp
   UseAnyOfAllOfCheck.cpp
+  UseNumericLimitsCheck.cpp
   UseStdMinMaxCheck.cpp
 
   LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index d61c0ba39658e..054083d25952a 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -60,6 +60,7 @@
 #include "UniqueptrDeleteReleaseCheck.h"
 #include "UppercaseLiteralSuffixCheck.h"
 #include "UseAnyOfAllOfCheck.h"
+#include "UseNumericLimitsCheck.h"
 #include "UseStdMinMaxCheck.h"
 
 namespace clang::tidy {
@@ -170,6 +171,8 @@ class ReadabilityModule : public ClangTidyModule {
         "readability-uppercase-literal-suffix");
     CheckFactories.registerCheck<UseAnyOfAllOfCheck>(
         "readability-use-anyofallof");
+    CheckFactories.registerCheck<UseNumericLimitsCheck>(
+        "readability-use-numeric-limits");
     CheckFactories.registerCheck<UseStdMinMaxCheck>(
         "readability-use-std-min-max");
   }
diff --git a/clang-tools-extra/clang-tidy/readability/UseNumericLimitsCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseNumericLimitsCheck.cpp
new file mode 100644
index 0000000000000..b26ae008cbdd5
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/UseNumericLimitsCheck.cpp
@@ -0,0 +1,135 @@
+//===--- UseNumericLimitsCheck.cpp - clang-tidy ---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseNumericLimitsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Preprocessor.h"
+#include <cmath>
+#include <limits>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+UseNumericLimitsCheck::UseNumericLimitsCheck(StringRef Name,
+                                             ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      SignedConstants{
+          {std::numeric_limits<int8_t>::min(),
+           "std::numeric_limits<int8_t>::min()"},
+          {std::numeric_limits<int8_t>::max(),
+           "std::numeric_limits<int8_t>::max()"},
+          {std::numeric_limits<int16_t>::min(),
+           "std::numeric_limits<int16_t>::min()"},
+          {std::numeric_limits<int16_t>::max(),
+           "std::numeric_limits<int16_t>::max()"},
+          {std::numeric_limits<int32_t>::min(),
+           "std::numeric_limits<int32_t>::min()"},
+          {std::numeric_limits<int32_t>::max(),
+           "std::numeric_limits<int32_t>::max()"},
+          {std::numeric_limits<int64_t>::min(),
+           "std::numeric_limits<int64_t>::min()"},
+          {std::numeric_limits<int64_t>::max(),
+           "std::numeric_limits<int64_t>::max()"},
+
+      },
+      UnsignedConstants{
+          {std::numeric_limits<uint8_t>::max(),
+           "std::numeric_limits<uint8_t>::max()"},
+          {std::numeric_limits<uint16_t>::max(),
+           "std::numeric_limits<uint16_t>::max()"},
+          {std::numeric_limits<uint32_t>::max(),
+           "std::numeric_limits<uint32_t>::max()"},
+          {std::numeric_limits<uint64_t>::max(),
+           "std::numeric_limits<uint64_t>::max()"},
+      },
+      Inserter(Options.getLocalOrGlobal("IncludeStyle",
+                                        utils::IncludeSorter::IS_LLVM),
+               areDiagsSelfContained()) {}
+
+void UseNumericLimitsCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      expr(anyOf(unaryOperator(hasOperatorName("-"),
+                               hasUnaryOperand(integerLiteral().bind(
+                                   "negative-integer-literal"))),
+                 unaryOperator(hasOperatorName("+"),
+                               hasUnaryOperand(integerLiteral().bind(
+                                   "positive-integer-literal"))),
+                 integerLiteral(unless(hasParent(unaryOperator(
+                                    hasAnyOperatorName("-", "+")))))
+                     .bind("bare-integer-literal")))
+          .bind("unary-op-exp"),
+      this);
+}
+
+void UseNumericLimitsCheck::registerPPCallbacks(
+    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
+  Inserter.registerPreprocessor(PP);
+}
+
+void UseNumericLimitsCheck::check(const MatchFinder::MatchResult &Result) {
+  const IntegerLiteral *MatchedDecl = nullptr;
+
+  const IntegerLiteral *NegativeMatchedDecl =
+      Result.Nodes.getNodeAs<IntegerLiteral>("negative-integer-literal");
+  const IntegerLiteral *PositiveMatchedDecl =
+      Result.Nodes.getNodeAs<IntegerLiteral>("positive-integer-literal");
+  const IntegerLiteral *BareMatchedDecl =
+      Result.Nodes.getNodeAs<IntegerLiteral>("bare-integer-literal");
+
+  if (NegativeMatchedDecl != nullptr) {
+    MatchedDecl = NegativeMatchedDecl;
+  } else if (PositiveMatchedDecl != nullptr) {
+    MatchedDecl = PositiveMatchedDecl;
+  } else if (BareMatchedDecl != nullptr) {
+    MatchedDecl = BareMatchedDecl;
+  }
+
+  // Only valid if unary operator is present
+  const UnaryOperator *UnaryOpExpr =
+      Result.Nodes.getNodeAs<UnaryOperator>("unary-op-exp");
+
+  const llvm::APInt MatchedIntegerConstant = MatchedDecl->getValue();
+  const auto Language = getLangOpts();
+
+  auto Fixer = [&](auto SourceValue, auto Value,
+                   const std::string &Replacement) {
+    SourceLocation Location = MatchedDecl->getExprLoc();
+    SourceRange Range{MatchedDecl->getBeginLoc(), MatchedDecl->getEndLoc()};
+
+    if (MatchedDecl == NegativeMatchedDecl && -SourceValue == Value) {
+      Range = SourceRange(UnaryOpExpr->getBeginLoc(), UnaryOpExpr->getEndLoc());
+      Location = UnaryOpExpr->getExprLoc();
+      SourceValue = -SourceValue;
+    } else if (MatchedDecl == PositiveMatchedDecl && SourceValue == Value) {
+      Range = SourceRange(UnaryOpExpr->getBeginLoc(), UnaryOpExpr->getEndLoc());
+      Location = UnaryOpExpr->getExprLoc();
+    } else if (MatchedDecl != BareMatchedDecl || SourceValue != Value) {
+      return;
+    }
+
+    diag(Location, "The constant " + std::to_string(SourceValue) +
+                       " is being utilized. "
+                       "Consider using " +
+                       Replacement + " instead")
+        << FixItHint::CreateReplacement(Range, Replacement)
+        << Inserter.createIncludeInsertion(
+               Result.SourceManager->getFileID(Location), "<limits>");
+  };
+
+  for (const auto &[Value, Replacement] : SignedConstants) {
+    Fixer(MatchedIntegerConstant.getSExtValue(), Value, Replacement);
+  }
+
+  for (const auto &[Value, Replacement] : UnsignedConstants) {
+    Fixer(MatchedIntegerConstant.getZExtValue(), Value, Replacement);
+  }
+}
+
+} // namespace clang::tidy::readability
diff --git a/clang-tools-extra/clang-tidy/readability/UseNumericLimitsCheck.h b/clang-tools-extra/clang-tidy/readability/UseNumericLimitsCheck.h
new file mode 100644
index 0000000000000..d7231132ef5dd
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/UseNumericLimitsCheck.h
@@ -0,0 +1,41 @@
+//===--- UseNumericLimitsCheck.h - clang-tidy -------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USENUMERICLIMITSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USENUMERICLIMITSCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/IncludeInserter.h"
+#include <vector>
+
+namespace clang::tidy::readability {
+
+/// Replaces certain integer literals with equivalent calls to
+/// ``std::numeric_limits``.
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/readability/use-numeric-limits.html
+class UseNumericLimitsCheck : public ClangTidyCheck {
+public:
+  UseNumericLimitsCheck(StringRef Name, ClangTidyContext *Context);
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus11;
+  }
+
+private:
+  const std::vector<std::tuple<int64_t, std::string>> SignedConstants;
+  const std::vector<std::tuple<uint64_t, std::string>> UnsignedConstants;
+  utils::IncludeInserter Inserter;
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USENUMERICLIMITSCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 6b8fe22242417..b2dfb0de6ae68 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -91,6 +91,11 @@ Improvements to clang-tidy
 New checks
 ^^^^^^^^^^
 
+- New :doc:`readability-use-numeric-limits
+  <clang-tidy/checks/readability/use-numeric-limits>` check to replace certain
+  integer literals with ``std::numeric_limits`` calls.
+
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 7b9b905ef7671..e69ab96cf4bf9 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -403,6 +403,7 @@ Clang-Tidy Checks
    :doc:`readability-uniqueptr-delete-release <readability/uniqueptr-delete-release>`, "Yes"
    :doc:`readability-uppercase-literal-suffix <readability/uppercase-literal-suffix>`, "Yes"
    :doc:`readability-use-anyofallof <readability/use-anyofallof>`,
+   :doc:`readability-use-numeric-limits <readability/use-numeric-limits>`, "Yes"
    :doc:`readability-use-std-min-max <readability/use-std-min-max>`, "Yes"
    :doc:`zircon-temporary-objects <zircon/temporary-objects>`,
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/use-numeric-limits.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/use-numeric-limits.rst
new file mode 100644
index 0000000000000..ed50d00f1085d
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/use-numeric-limits.rst
@@ -0,0 +1,22 @@
+.. title:: clang-tidy - readability-use-numeric-limits
+
+readability-use-numeric-limits
+==============================
+
+Replaces certain integer literals with ``std::numeric_limits`` calls.
+
+Before:
+
+.. code-block:: c++
+
+  void foo() {
+    int32_t a = 2147483647;
+  }
+
+After:
+
+.. code-block:: c++
+
+  void foo() {
+    int32_t a = std::numeric_limits<int32_t>::max();
+  }
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-numeric-limits.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-numeric-limits.cpp
new file mode 100644
index 0000000000000..2f1d5190148b0
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-numeric-limits.cpp
@@ -0,0 +1,85 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s readability-use-numeric-limits %t
+#include <stdint.h>
+
+void constants() {
+  // CHECK-MESSAGES: :[[@LINE+2]]:14: warning: The constant -128 is being utilized. Consider using std::numeric_limits<int8_t>::min() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int8_t _ = std::numeric_limits<int8_t>::min();
+  int8_t _ = -128;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:14: warning: The constant 127 is being utilized. Consider using std::numeric_limits<int8_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int8_t _ = std::numeric_limits<int8_t>::max();
+  int8_t _ = +127;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:14: warning: The constant 127 is being utilized. Consider using std::numeric_limits<int8_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int8_t _ = std::numeric_limits<int8_t>::max();
+  int8_t _ = 127;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant -32768 is being utilized. Consider using std::numeric_limits<int16_t>::min() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int16_t _ = std::numeric_limits<int16_t>::min();
+  int16_t _ = -32768;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant 32767 is being utilized. Consider using std::numeric_limits<int16_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int16_t _ = std::numeric_limits<int16_t>::max();
+  int16_t _ = +32767;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant 32767 is being utilized. Consider using std::numeric_limits<int16_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int16_t _ = std::numeric_limits<int16_t>::max();
+  int16_t _ = 32767;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant -2147483648 is being utilized. Consider using std::numeric_limits<int32_t>::min() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int32_t _ = std::numeric_limits<int32_t>::min();
+  int32_t _ = -2147483648;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant 2147483647 is being utilized. Consider using std::numeric_limits<int32_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int32_t _ = std::numeric_limits<int32_t>::max();
+  int32_t _ = +2147483647;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant 2147483647 is being utilized. Consider using std::numeric_limits<int32_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int32_t _ = std::numeric_limits<int32_t>::max();
+  int32_t _ = 2147483647;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant -9223372036854775808 is being utilized. Consider using std::numeric_limits<int64_t>::min() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int64_t _ = std::numeric_limits<int64_t>::min();
+  int64_t _ = -9223372036854775808;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant 9223372036854775807 is being utilized. Consider using std::numeric_limits<int64_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int64_t _ = std::numeric_limits<int64_t>::max();
+  int64_t _ = +9223372036854775807;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant 9223372036854775807 is being utilized. Consider using std::numeric_limits<int64_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: int64_t _ = std::numeric_limits<int64_t>::max();
+  int64_t _ = 9223372036854775807;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant 255 is being utilized. Consider using std::numeric_limits<uint8_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: uint8_t _ = std::numeric_limits<uint8_t>::max();
+  uint8_t _ = 255;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:15: warning: The constant 255 is being utilized. Consider using std::numeric_limits<uint8_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: uint8_t _ = std::numeric_limits<uint8_t>::max();
+  uint8_t _ = +255;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:16: warning: The constant 65535 is being utilized. Consider using std::numeric_limits<uint16_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: uint16_t _ = std::numeric_limits<uint16_t>::max();
+  uint16_t _ = 65535;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:16: warning: The constant 65535 is being utilized. Consider using std::numeric_limits<uint16_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: uint16_t _ = std::numeric_limits<uint16_t>::max();
+  uint16_t _ = +65535;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:16: warning: The constant 4294967295 is being utilized. Consider using std::numeric_limits<uint32_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: uint32_t _ = std::numeric_limits<uint32_t>::max();
+  uint32_t _ = 4294967295;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:16: warning: The constant 4294967295 is being utilized. Consider using std::numeric_limits<uint32_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: uint32_t _ = std::numeric_limits<uint32_t>::max();
+  uint32_t _ = +4294967295;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:16: warning: The constant 18446744073709551615 is being utilized. Consider using std::numeric_limits<uint64_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: uint64_t _ = std::numeric_limits<uint64_t>::max();
+  uint64_t _ = 18446744073709551615;
+
+  // CHECK-MESSAGES: :[[@LINE+2]]:16: warning: The constant 18446744073709551615 is being utilized. Consider using std::numeric_limits<uint64_t>::max() instead [readability-use-numeric-limits]
+  // CHECK-FIXES: uint64_t _ = std::numeric_limits<uint64_t>::max();
+  uint64_t _ = +18446744073709551615;
+}
+

@dmdlott
Copy link

dmdlott commented Feb 17, 2025

Copy link
Contributor

@HerrCai0907 HerrCai0907 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of check is good.
But I think the matcher need to refactor a lot.

  1. Please place value check in ast matcher part, match all integer will cost lots of cpu and ram to store temporary results.
  2. Maybe consider to write 2 different matcher, one for positive (include e.g. +255 and 255), the other for negative (-128) since they have different value scopes.

@vbvictor
Copy link
Contributor

vbvictor commented Feb 18, 2025

  1. Please place value check in ast matcher part, match all integer will cost lots of cpu and ram to store temporary results.

Code in clang-tools-extra\clang-tidy\bugprone\StringConstructorCheck.cpp may be helpful since it shows work with numbers and have matcher isBiggerThan. Similar matcher I guess can be written for this check.

Comment on lines +126 to +124
auto Fixer = [&](auto SourceValue, auto Value,
const std::string &Replacement) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to make it as template function since SourceValue and Value maybe be the same in this cases?
use 2 auto ignore this relationship.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if I use a template parameter list as in

auto Fixer = [&]<typename ValueType>(ValueType SourceValue, ValueType Value, ...

I get a warning that explicit template parameter lists for lambdas are a C++20 extension?

Using a static_assert like

    static_assert(std::is_same_v<decltype(SourceValue), decltype(Value)>);

and/or an enable_if as in

  auto Fixer = [&](auto SourceValue, auto Value, const std::string &Replacement)
      -> std::enable_if_t<
          std::is_same_v<decltype(SourceValue), decltype(Value)>> {

seems to work, however

Copy link
Contributor

@HerrCai0907 HerrCai0907 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the expected behavior of +128 -127?

@stellar-aria
Copy link
Contributor Author

What is the expected behavior of +128 -127?

I feel like they shouldn't be matched, since with opposite signs they're not really within the set of limits? It just feels unintuitive to me for -127 to be replaced by something like -std::numeric_limits<int8_t>::max(). I've added test cases for this, but I'm open to other possibilities as well.

Copy link
Contributor

@HerrCai0907 HerrCai0907 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. It will be better if parentheses can be supported as well.

stellar-aria and others added 2 commits June 15, 2025 12:02
Co-authored-by: Baranov Victor <bar.victor.2002@gmail.com>
Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, with few with nits

stellar-aria and others added 2 commits June 15, 2025 15:14
Co-authored-by: Baranov Victor <bar.victor.2002@gmail.com>
@vbvictor vbvictor merged commit e7dd223 into llvm:main Jun 21, 2025
8 checks passed
Copy link

@stellar-aria Congratulations on having your first Pull Request (PR) merged into the LLVM Project!

Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR.

Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues.

How to do this, and the rest of the post-merge process, is covered in detail here.

If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again.

If you don't get any reports, no action is required from you. Your changes are working as expected, well done!

@llvm-ci
Copy link
Collaborator

llvm-ci commented Jun 21, 2025

LLVM Buildbot has detected a new failure on builder clang-m68k-linux-cross running on suse-gary-m68k-cross while building clang-tools-extra at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/27/builds/11917

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
...
In file included from /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/include/clang/Parse/Parser.h:20,
                 from /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/unittests/Analysis/MacroExpansionContextTest.cpp:23:
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/include/clang/Sema/Sema.h:839:7: warning: ‘clang::Sema’ declared with greater visibility than the type of its field ‘clang::Sema::UnusedFileScopedDecls’ [-Wattributes]
  839 | class Sema final : public SemaBase {
      |       ^~~~
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/include/clang/Sema/Sema.h:839:7: warning: ‘clang::Sema’ declared with greater visibility than the type of its field ‘clang::Sema::TentativeDefinitions’ [-Wattributes]
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/include/clang/Sema/Sema.h:839:7: warning: ‘clang::Sema’ declared with greater visibility than the type of its field ‘clang::Sema::ExtVectorDecls’ [-Wattributes]
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/include/clang/Sema/Sema.h:839:7: warning: ‘clang::Sema’ declared with greater visibility than the type of its field ‘clang::Sema::DelegatingCtorDecls’ [-Wattributes]
[193/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/StaticAnalyzer/AnalyzerOptionsTest.cpp.o
[194/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp.o
FAILED: tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp.o 
/usr/bin/c++ -DGTEST_HAS_RTTI=0 -DLLVM_BUILD_STATIC -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/tools/clang/unittests -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/unittests -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/tools/clang/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/llvm/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/unittests/Tooling -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/third-party/unittest/googletest/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/third-party/unittest/googlemock/include -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-maybe-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -O3 -DNDEBUG -std=c++17  -Wno-variadic-macros -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -Wno-suggest-override -MD -MT tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp.o -MF tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp.o.d -o tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp.o -c /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/unittests/Analysis/FlowSensitive/MultiVarConstantPropagationTest.cpp
c++: fatal error: Killed signal terminated program cc1plus
compilation terminated.
[195/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/StaticAnalyzer/BlockEntranceCallbackTest.cpp.o
[196/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/MapLatticeTest.cpp.o
[197/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp.o
[198/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/CFGTest.cpp.o
[199/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/MatchSwitchTest.cpp.o
[200/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/StaticAnalyzer/CallEventTest.cpp.o
[201/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/DeterminismTest.cpp.o
[202/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp.o
[203/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/TransferBranchTest.cpp.o
[204/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/SimplifyConstraintsTest.cpp.o
[205/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/CFGDominatorTree.cpp.o
[206/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/TestingSupportTest.cpp.o
[207/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/StaticAnalyzer/ConflictingEvalCallsTest.cpp.o
[208/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/DebugSupportTest.cpp.o
[209/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/StaticAnalyzer/ExprEngineVisitTest.cpp.o
[210/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/SignAnalysisTest.cpp.o
[211/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/StaticAnalyzer/APSIntTypeTest.cpp.o
[212/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/StaticAnalyzer/BugReportInterestingnessTest.cpp.o
[213/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/IntervalPartitionTest.cpp.o
[214/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp.o
[215/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/RecordOpsTest.cpp.o
[216/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/TestingSupport.cpp.o
[217/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/WatchedLiteralsSolverTest.cpp.o
[218/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/ASTOpsTest.cpp.o
[219/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/CachedConstAccessorsLatticeTest.cpp.o
[220/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/LoggerTest.cpp.o
[221/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp.o
[222/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/ExprMutationAnalyzerTest.cpp.o
[223/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/SingleVarConstantPropagationTest.cpp.o
[224/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/StaticAnalyzer/CallDescriptionTest.cpp.o
[225/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp.o
[226/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp.o
[227/1159] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/TransferTest.cpp.o
ninja: build stopped: subcommand failed.

@vbvictor
Copy link
Contributor

Buildbots failures seems irrelevant

@qinkunbao
Copy link
Member

Hi, this PR broken a few build bots. Could you take a look?
e.g.,

https://lab.llvm.org/buildbot/#/builders/169/builds/12435

@stellar-aria
Copy link
Contributor Author

I'm assuming the MSAN failure is a result of the ASAN failure, since I don't see any reference to UseNumericLimitsCheck in that output.

The ASAN output however...
/home/b/sanitizer-x86_64-linux-fast/build/llvm-project/clang-tools-extra/clang-tidy/readability/UseNumericLimitsCheck.cpp:69:64: runtime error: negation of -9223372036854775808 cannot be represented in type 'long'; cast to an unsigned type to negate this value to itself

Looks like the auto Value in this matcher might be getting type inferred to long instead of long long?

auto NegativeIntegerMatcher = [](auto Value) {
return unaryOperator(hasOperatorName("-"),
hasUnaryOperand(integerLiteral(equals(-Value))
.bind("negative-integer-literal")))
.bind("unary-op");
};

@vbvictor
Copy link
Contributor

One of the logs also suggests UseNumericLimitsCheck.cpp:81:31.
As of LLVM policy, we need to revert it. @stellar-aria, I will create a revert patch in a couple of hours or accept yours if you do it first.

@stellar-aria
Copy link
Contributor Author

UseNumericLimitsCheck.cpp:81:31 is just the call site for that matcher, so that's why it's in the backtrace, I think. I'll leave the revert patch to you.

vbvictor added a commit that referenced this pull request Jun 23, 2025
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jun 23, 2025
@vbvictor
Copy link
Contributor

Reverted, thank you qinkunbao for the report!
When making a new PR for this check, please make the title Reland "[clang-tidy] Add ..." provide a description of what broke and how it was fixed.

miguelcsx pushed a commit to miguelcsx/llvm-project that referenced this pull request Jun 23, 2025
Jaddyen pushed a commit to Jaddyen/llvm-project that referenced this pull request Jun 23, 2025
)

The adds a check that replaces specific numeric literals like `32767`
with the equivalent call to `std::numeric_limits` (such as
`std::numeric_limits<int16_t>::max())`.

Partially addresses llvm#34434, but notably does not handle cases listed in
the title post such as `~0` and `-1`.
Jaddyen pushed a commit to Jaddyen/llvm-project that referenced this pull request Jun 23, 2025
@henrickarlsson
Copy link
Collaborator

I see this is reverted now, but while it was in I noticed that it also triggers for .c files. But the suggested fix is for C++
Would it be possible to limit this to run on only C++ code or suggest a separate fix targeted for C?

@vbvictor
Copy link
Contributor

Would it be possible to limit this to run on only C++ code

Yes, It should only be limited to C++ code for now. I overlooked isLanguageVersionSupported method, sorry for the inconvenience.

@earnol
Copy link
Contributor

earnol commented Jun 24, 2025

For C the macro constants from limits.h can be used: UINT8_MAX from stdint.h instead of std::numeric_limits<uint8_t>::max(). Please refer to https://en.cppreference.com/w/c/types/integer.html. Please also check compatibility requirement because list of available constants depends on the selected standard.

anthonyhatran pushed a commit to anthonyhatran/llvm-project that referenced this pull request Jun 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants