[clang-tools-extra] [clang-tidy] Add bugprone-container-bounds-check-overflow check (PR #206349)

via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 30 00:24:34 PDT 2026


https://github.com/Harald-R updated https://github.com/llvm/llvm-project/pull/206349

>From 20a2f0181d7a2d2f7643e596799bff3374ad538c Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Wed, 24 Jun 2026 13:56:25 +0300
Subject: [PATCH 1/3] Add bugprone-container-bounds-check-overflow check

---
 .../bugprone/BugproneTidyModule.cpp           |   3 +
 .../clang-tidy/bugprone/CMakeLists.txt        |   1 +
 .../ContainerBoundsCheckOverflowCheck.cpp     | 157 ++++++++++++++++++
 .../ContainerBoundsCheckOverflowCheck.h       |  38 +++++
 clang-tools-extra/docs/ReleaseNotes.rst       |   6 +
 .../container-bounds-check-overflow.rst       |  33 ++++
 .../docs/clang-tidy/checks/list.rst           |   1 +
 .../container-bounds-check-overflow.cpp       | 153 +++++++++++++++++
 8 files changed, 392 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.h
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/container-bounds-check-overflow.cpp

diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 3aa39d10ceb5d..485749a13b47f 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -21,6 +21,7 @@
 #include "ChainedComparisonCheck.h"
 #include "CommandProcessorCheck.h"
 #include "ComparePointerToMemberVirtualFunctionCheck.h"
+#include "ContainerBoundsCheckOverflowCheck.h"
 #include "CopyConstructorInitCheck.h"
 #include "CopyConstructorMutatesArgumentCheck.h"
 #include "CrtpConstructorAccessibilityCheck.h"
@@ -150,6 +151,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-command-processor");
     CheckFactories.registerCheck<ComparePointerToMemberVirtualFunctionCheck>(
         "bugprone-compare-pointer-to-member-virtual-function");
+    CheckFactories.registerCheck<ContainerBoundsCheckOverflowCheck>(
+        "bugprone-container-bounds-check-overflow");
     CheckFactories.registerCheck<CopyConstructorInitCheck>(
         "bugprone-copy-constructor-init");
     CheckFactories.registerCheck<CopyConstructorMutatesArgumentCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 43e85b1407f21..8db97682e1505 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -18,6 +18,7 @@ add_clang_library(clangTidyBugproneModule STATIC
   ChainedComparisonCheck.cpp
   CommandProcessorCheck.cpp
   ComparePointerToMemberVirtualFunctionCheck.cpp
+  ContainerBoundsCheckOverflowCheck.cpp
   CopyConstructorInitCheck.cpp
   CopyConstructorMutatesArgumentCheck.cpp
   CrtpConstructorAccessibilityCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.cpp
new file mode 100644
index 0000000000000..d677b3609bf53
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.cpp
@@ -0,0 +1,157 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "ContainerBoundsCheckOverflowCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+ContainerBoundsCheckOverflowCheck::ContainerBoundsCheckOverflowCheck(
+    StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IgnoredContainers(utils::options::parseStringList(
+          Options.get("IgnoredContainers", ""))) {}
+
+void ContainerBoundsCheckOverflowCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IgnoredContainers",
+                utils::options::serializeStringList(IgnoredContainers));
+}
+
+void ContainerBoundsCheckOverflowCheck::registerMatchers(MatchFinder *Finder) {
+  auto RecordMatcher = cxxRecordDecl();
+  if (!IgnoredContainers.empty())
+    RecordMatcher = cxxRecordDecl(unless(hasAnyName(IgnoredContainers)));
+  auto SizeMethodCall = cxxMemberCallExpr(
+      callee(cxxMethodDecl(hasName("size"))),
+      on(hasType(hasCanonicalType(hasDeclaration(RecordMatcher)))));
+  // The size can either be a direct size() call or a reference to a variable
+  // initialized from one (e.g. auto s = v.size();)
+  auto SizeExpr = ignoringParenImpCasts(
+      expr(anyOf(SizeMethodCall, declRefExpr(to(varDecl(hasInitializer(
+                                     ignoringParenImpCasts(SizeMethodCall)))))))
+          .bind("size_expr"));
+
+  // Operands must be unsigned integers, as overflow in signed integer addition
+  // is undefined behavior
+  auto Addition =
+      binaryOperator(hasOperatorName("+"), hasLHS(hasType(isUnsignedInteger())),
+                     hasRHS(hasType(isUnsignedInteger())))
+          .bind("addition");
+  auto Comparison = hasAnyOperatorName("<", "<=", ">", ">=");
+  // Match cases: [Addition] </<=/>/>= [Size]
+  Finder->addMatcher(binaryOperator(Comparison, hasLHS(Addition),
+                                    hasRHS(ignoringParenImpCasts(SizeExpr)))
+                         .bind("comparison_addition_lhs"),
+                     this);
+  // Match cases: [Size] </<=/>/>= [Addition]
+  Finder->addMatcher(binaryOperator(Comparison, hasRHS(Addition),
+                                    hasLHS(ignoringParenImpCasts(SizeExpr)))
+                         .bind("comparison_addition_rhs"),
+                     this);
+}
+
+void ContainerBoundsCheckOverflowCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Addition = Result.Nodes.getNodeAs<BinaryOperator>("addition");
+  const auto *SizeExpr = Result.Nodes.getNodeAs<Expr>("size_expr");
+  if (!Addition || !SizeExpr)
+    return;
+  const auto *ComparisonAddLhs =
+      Result.Nodes.getNodeAs<BinaryOperator>("comparison_addition_lhs");
+  const auto *ComparisonAddRhs =
+      Result.Nodes.getNodeAs<BinaryOperator>("comparison_addition_rhs");
+  const auto NoComparison = !ComparisonAddLhs && !ComparisonAddRhs;
+  if (NoComparison)
+    return;
+
+  auto AdditionType = Addition->getType().getCanonicalType();
+  auto SizeExprType = SizeExpr->getType().getCanonicalType();
+
+  auto &Context = *Result.Context;
+  // If the type of the addition is smaller than the type of the size() call,
+  // then the addition will be promoted to the size() type before the
+  // comparison, so there is no risk of overflow. The case where the type of the
+  // addition is larger than the type of the size() call is not handled by this
+  // check
+  if (Context.getTypeSize(AdditionType) != Context.getTypeSize(SizeExprType))
+    return;
+
+  const auto *Comparison =
+      ComparisonAddLhs ? ComparisonAddLhs : ComparisonAddRhs;
+
+  auto Diag = diag(Comparison->getOperatorLoc(),
+                   "potential overflow in unsigned integer addition "
+                   "before comparison");
+
+  // A fix is only produced when both operands of the addition are simple
+  // expressions. If an operand is itself a compound expression (e.g. 'a + b' in
+  // 'a + b + c'), only a diagnostic is emitted, as rewriting such cases is
+  // error-prone
+  const bool HasCompoundOperand =
+      isa<BinaryOperator>(Addition->getLHS()->IgnoreParenImpCasts()) ||
+      isa<BinaryOperator>(Addition->getRHS()->IgnoreParenImpCasts());
+  if (HasCompoundOperand)
+    return;
+
+  // Introduce parentheses around the addition to avoid changing the order of
+  // operations when replacing the comparison with a logical AND/OR expression.
+  // The parentheses are only added if the original expression is not already
+  // wrapped in parentheses
+  bool NeedsParens = true;
+  const auto &Parents = Context.getParents(*Comparison);
+  if (!Parents.empty()) {
+    if (Parents[0].get<ParenExpr>() || Parents[0].get<IfStmt>() ||
+        Parents[0].get<WhileStmt>())
+      NeedsParens = false;
+  }
+
+  auto GetText = [&](SourceRange Range) -> StringRef {
+    return Lexer::getSourceText(CharSourceRange::getTokenRange(Range),
+                                *Result.SourceManager, getLangOpts());
+  };
+  const std::string StrA = GetText(Addition->getLHS()->getSourceRange()).str();
+  const std::string StrB = GetText(Addition->getRHS()->getSourceRange()).str();
+  const std::string StrSize = GetText(SizeExpr->getSourceRange()).str();
+
+  const auto ComparisonType = Comparison->getOpcodeStr();
+  std::string Replacement;
+  if (ComparisonAddLhs) {
+    // Matches cases where the addition is on the left side of the comparison
+    // (a + b < size())  -> (a < size() && b < size() - a)
+    // (a + b <= size()) -> (a <= size() && b <= size() - a)
+    // (a + b > size())  -> (a > size() || b > size() - a)
+    // (a + b >= size()) -> (a >= size() || b >= size() - a)
+    const auto *Expr =
+        (ComparisonType == "<" || ComparisonType == "<=") ? " && " : " || ";
+    Replacement = (NeedsParens ? "(" : "") + StrA + " " + ComparisonType.str() +
+                  " " + StrSize + Expr + StrB + " " + ComparisonType.str() +
+                  " " + StrSize + " - " + StrA + (NeedsParens ? ")" : "");
+  } else {
+    // Matches cases where the addition is on the right side of the comparison,
+    // (size() < a + b)  -> (size() < a || size() - a < b)
+    // (size() <= a + b) -> (size() <= a || size() - a <= b)
+    // (size() > a + b)  -> (size() > a && size() - a > b)
+    // (size() >= a + b) -> (size() >= a && size() - a >= b)
+    const auto *Expr =
+        (ComparisonType == "<" || ComparisonType == "<=") ? " || " : " && ";
+    Replacement = (NeedsParens ? "(" : "") + StrSize + " " +
+                  ComparisonType.str() + " " + StrA + Expr + StrSize + " - " +
+                  StrA + " " + ComparisonType.str() + " " + StrB +
+                  (NeedsParens ? ")" : "");
+  }
+
+  Diag << FixItHint::CreateReplacement(Comparison->getSourceRange(),
+                                       Replacement);
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.h b/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.h
new file mode 100644
index 0000000000000..2809863857c0c
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.h
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_BUGPRONE_CONTAINERBOUNDSCHECKOVERFLOWCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CONTAINERBOUNDSCHECKOVERFLOWCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Check for potential overflow in unsigned integer addition before comparison
+/// with a container's size() method. For example a + b > v.size() can overflow
+/// if a and b are large enough, leading to incorrect behavior
+///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/bugprone/container-bounds-check-overflow.html
+class ContainerBoundsCheckOverflowCheck : public ClangTidyCheck {
+public:
+  ContainerBoundsCheckOverflowCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+
+private:
+  const std::vector<StringRef> IgnoredContainers;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_CONTAINERBOUNDSCHECKOVERFLOWCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 2a980cc089a65..f1f8aae26b031 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -221,6 +221,12 @@ New checks
 
   Finds assignments within selection statements.
 
+- New :doc:`bugprone-container-bounds-check-overflow
+  <clang-tidy/checks/bugprone/container-bounds-check-overflow>` check.
+
+  Finds potential overflow in unsigned integer addition before comparison
+  with a container's ``size()`` method.
+
 - New :doc:`bugprone-missing-end-comparison
   <clang-tidy/checks/bugprone/missing-end-comparison>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst
new file mode 100644
index 0000000000000..887a1784800b1
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst
@@ -0,0 +1,33 @@
+.. title:: clang-tidy - bugprone-container-bounds-check-overflow
+
+bugprone-container-bounds-check-overflow
+========================================
+
+This check finds potential overflow in unsigned integer addition before comparison with a container's
+``size()`` method. It flags all of the following combinations:
+- ``a + b < v.size()``
+- ``a + b <= v.size()``
+- ``a + b > v.size()``
+- ``a + b >= v.size()``
+- ``v.size() < a + b``
+- ``v.size() <= a + b``
+- ``v.size() > a + b``
+- ``v.size() >= a + b``
+
+The addition ``a + b`` can overflow if ``a`` and ``b`` are large enough, leading to incorrect behavior.
+For example, if ``a`` is ``UINT_MAX`` and ``b`` is ``1``, then ``a + b`` will wrap around to ``0``,
+and the comparison can be true, even if the container is empty.
+
+The comparison is flagged only if size of the unsigned integers being added is the same
+as the size of the container's ``size()`` return type. Smaller types are promoted to the size
+of the container's ``size()`` return type before the addition, so they are safe from overflow.
+
+Options
+-------
+
+.. option:: IgnoredContainers
+
+    When set, the check will ignore the specified containers. The value is a
+    comma-separated list of fully qualified container names. For example, to ignore
+    ``std::array`` and ``CustomClass``, set the option to ``::std::array,::CustomClass``.
+    The default is empty, meaning no containers are ignored.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 2a44dc78fbc89..2aac5b79b2b8d 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -90,6 +90,7 @@ Clang-Tidy Checks
    :doc:`bugprone-chained-comparison <bugprone/chained-comparison>`,
    :doc:`bugprone-command-processor <bugprone/command-processor>`,
    :doc:`bugprone-compare-pointer-to-member-virtual-function <bugprone/compare-pointer-to-member-virtual-function>`,
+   :doc:`bugprone-container-bounds-check-overflow <bugprone/container-bounds-check-overflow>`, "Yes"
    :doc:`bugprone-copy-constructor-init <bugprone/copy-constructor-init>`, "Yes"
    :doc:`bugprone-copy-constructor-mutates-argument <bugprone/copy-constructor-mutates-argument>`,
    :doc:`bugprone-crtp-constructor-accessibility <bugprone/crtp-constructor-accessibility>`, "Yes"
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/container-bounds-check-overflow.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/container-bounds-check-overflow.cpp
new file mode 100644
index 0000000000000..9023e1639df07
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/container-bounds-check-overflow.cpp
@@ -0,0 +1,153 @@
+// RUN: %check_clang_tidy %s bugprone-container-bounds-check-overflow %t -- -config='{CheckOptions: { bugprone-container-bounds-check-overflow.IgnoredContainers: "::CustomClass"}}'
+
+#include <cstddef>
+#include <vector>
+#include <string>
+
+#define IS_WITHIN_BOUNDS(a, b, c) (a + b > c)
+#define IS_WITHIN_BOUNDS2(a, b, c) ((a) + (b) > (c))
+
+namespace {
+
+class CustomClass {
+public:
+  size_t size() const {
+    return 0;
+  }
+};
+
+size_t size() {
+  return 0;
+}
+
+// The check must work inside templates and across multiple instantiations
+template <typename T>
+void templated(size_t a, size_t b, const std::vector<T> &v) {
+  if (a + b > v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (a > v.size() || b > v.size() - a) {}
+}
+
+}
+
+void positives(size_t a, size_t b, const std::vector<int> &v) {
+  if (a + b > v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (a > v.size() || b > v.size() - a) {}
+  if (a + b >= v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (a >= v.size() || b >= v.size() - a) {}
+  if (a + b < v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (a < v.size() && b < v.size() - a) {}
+  if (a + b <= v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (a <= v.size() && b <= v.size() - a) {}
+  if (v.size() < a + b) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (v.size() < a || v.size() - a < b) {}
+  if (v.size() <= a + b) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (v.size() <= a || v.size() - a <= b) {}
+  if (v.size() > a + b) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (v.size() > a && v.size() - a > b) {}
+  if (v.size() >= a + b) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (v.size() >= a && v.size() - a >= b) {}
+
+  // Introduces parantheses to avoid changing the order of operations
+  if (true && a + b > v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (true && (a > v.size() || b > v.size() - a)) {}
+  if (a + b > v.size() && true) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if ((a > v.size() || b > v.size() - a) && true) {}
+
+  // Avoid introducing parantheses if the comparison is already wrapped in one
+  while(a + b > v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: while(a > v.size() || b > v.size() - a) {}
+  auto result = a + b > v.size();
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: auto result = (a > v.size() || b > v.size() - a);
+  result = (a + b > v.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: result = (a > v.size() || b > v.size() - a);
+  (void)result;
+
+  // Confirm the fix works well with different named variables and container types
+  size_t x = 1;
+  size_t y = 2;
+  std::string str = "Hello, world!";
+  if (x + y > str.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (x > str.size() || y > str.size() - x) {}
+
+  // The check should match local variables that are assigned the result of a size() call
+  auto local_size = v.size();
+  if (a + b < local_size) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (a < local_size && b < local_size - a) {}
+  if (local_size < a + b) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  // CHECK-FIXES: if (local_size < a || local_size - a < b) {}
+
+  IS_WITHIN_BOUNDS(x, y, v.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  IS_WITHIN_BOUNDS2(x, y, v.size());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+
+  // Chained additions: a compound operand of the addition produces a diagnostic only, with no fix
+  size_t c = 10;
+  if (a + b + c > v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  if (v.size() < a + b + c) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  if (a + (b + c) > v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  if (v.size() < a + b + c || v.empty()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+  if (!v.empty() && a + b + c > v.size()) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: potential overflow in unsigned integer addition before comparison [bugprone-container-bounds-check-overflow]
+
+  // Instantiating templated function which contains the target pattern
+  templated(a, b, std::vector<int>{});
+  templated(a, b, std::vector<double>{});
+}
+
+void negatives(size_t a, size_t b, const std::vector<int> &v) {
+  // Cannot overflow because of the comparison order
+  if (a > v.size() || b > v.size() - a) {}
+  if (b > v.size() || a > v.size() - b) {}
+
+  // Cannot overflow because the operands of '+' are smaller than the result of size(); the addition result gets promoted to size_t before the comparison
+  unsigned short x = 1;
+  unsigned short y = 2;
+  if (x + y > v.size()) {}
+
+  // Intentionally ignored class
+  CustomClass custom;
+  if (a + b > custom.size()) {}
+  if (a + b >= custom.size()) {}
+  if (a + b < custom.size()) {}
+  if (a + b <= custom.size()) {}
+  if (custom.size() < a + b) {}
+  if (custom.size() <= a + b) {}
+  if (custom.size() > a + b) {}
+  if (custom.size() >= a + b) {}
+
+  // Call to a non-member size() method is not matched
+  if (a + b > size()) {}
+
+  // A plain variable (not a size() call) is not matched
+  size_t n = 10;
+  if (a + b > n) {}
+
+  // Signed operands cannot overflow in a way this check cares about (signed overflow is UB), so they must not be matched
+  int i = 1;
+  int j = 2;
+  if (i + j > v.size()) {}
+  if (v.size() < i + j) {}
+}
+

>From b4b2dc7335cf2d6a1c35e72a4307c5334b13381e Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Mon, 29 Jun 2026 20:40:00 +0300
Subject: [PATCH 2/3] Change option description

---
 .../checks/bugprone/container-bounds-check-overflow.rst       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst
index 887a1784800b1..c1d36f5baadca 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst
@@ -29,5 +29,5 @@ Options
 
     When set, the check will ignore the specified containers. The value is a
     comma-separated list of fully qualified container names. For example, to ignore
-    ``std::array`` and ``CustomClass``, set the option to ``::std::array,::CustomClass``.
-    The default is empty, meaning no containers are ignored.
+    ``std::array`` and ``CustomClass``, set the option to `::std::array,::CustomClass`.
+    The default is empty string, meaning no containers are ignored.

>From c1cb1e02f259b7bdb7ca9fc9cdb3e6aad8d65309 Mon Sep 17 00:00:00 2001
From: Harald-R <rotuna.razvan at gmail.com>
Date: Tue, 30 Jun 2026 10:19:59 +0300
Subject: [PATCH 3/3] Align documentation and code comment

---
 .../clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.h | 6 +++---
 .../checks/bugprone/container-bounds-check-overflow.rst     | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.h b/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.h
index 2809863857c0c..d9207ff9d0900 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/ContainerBoundsCheckOverflowCheck.h
@@ -13,9 +13,9 @@
 
 namespace clang::tidy::bugprone {
 
-/// Check for potential overflow in unsigned integer addition before comparison
-/// with a container's size() method. For example a + b > v.size() can overflow
-/// if a and b are large enough, leading to incorrect behavior
+/// Finds potential overflow in unsigned integer addition before comparison with
+/// a container's size() method. For example a + b > v.size() can overflow if a
+/// and b are large enough, leading to incorrect behavior.
 ///
 /// For the user-facing documentation see:
 /// https://clang.llvm.org/extra/clang-tidy/checks/bugprone/container-bounds-check-overflow.html
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst
index c1d36f5baadca..79f353584eb76 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/container-bounds-check-overflow.rst
@@ -3,7 +3,7 @@
 bugprone-container-bounds-check-overflow
 ========================================
 
-This check finds potential overflow in unsigned integer addition before comparison with a container's
+Finds potential overflow in unsigned integer addition before comparison with a container's
 ``size()`` method. It flags all of the following combinations:
 - ``a + b < v.size()``
 - ``a + b <= v.size()``



More information about the cfe-commits mailing list