[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