[clang-tools-extra] [clang-tidy] Add bugprone-sprintf-to-snprintf check (PR #182823)
Manthan Singla via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 23 01:33:00 PST 2026
https://github.com/ThanSin02426 created https://github.com/llvm/llvm-project/pull/182823
This check finds calls to sprintf where the first argument is a fixed-size array and suggests replacing them with snprintf.
Fixes #40511
>From 380554139e77ce83bb0c998273e7cae094d3c101 Mon Sep 17 00:00:00 2001
From: ThanSin02426 <manthan02426 at gmail.com>
Date: Mon, 23 Feb 2026 14:58:47 +0530
Subject: [PATCH] [clang-tidy] Add bugprone-sprintf-to-snprintf check
This check finds calls to sprintf where the first argument is a fixed-size array and suggests replacing them with snprintf.
Fixes #40511
---
.../bugprone/BugproneTidyModule.cpp | 3 ++
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../bugprone/SprintfToSnprintfCheck.cpp | 48 +++++++++++++++++++
.../bugprone/SprintfToSnprintfCheck.h | 33 +++++++++++++
clang-tools-extra/docs/ReleaseNotes.rst | 6 +++
.../checks/bugprone/sprintf-to-snprintf.rst | 27 +++++++++++
.../docs/clang-tidy/checks/list.rst | 1 +
.../checkers/bugprone/sprintf-to-snprintf.cpp | 16 +++++++
8 files changed, 135 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-to-snprintf.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-to-snprintf.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 4150442c25d61..46d58b984f97d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -74,6 +74,7 @@
#include "SignedCharMisuseCheck.h"
#include "SizeofContainerCheck.h"
#include "SizeofExpressionCheck.h"
+#include "SprintfToSnprintfCheck.h"
#include "SpuriouslyWakeUpFunctionsCheck.h"
#include "StandaloneEmptyCheck.h"
#include "StdNamespaceModificationCheck.h"
@@ -176,6 +177,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-incorrect-enable-if");
CheckFactories.registerCheck<IncorrectEnableSharedFromThisCheck>(
"bugprone-incorrect-enable-shared-from-this");
+ CheckFactories.registerCheck<SprintfToSnprintfCheck>(
+ "bugprone-sprintf-to-snprintf");
CheckFactories.registerCheck<UnintendedCharOstreamOutputCheck>(
"bugprone-unintended-char-ostream-output");
CheckFactories.registerCheck<ReturnConstRefFromParameterCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..21f3c5f00f12f 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -37,6 +37,7 @@ add_clang_library(clangTidyBugproneModule STATIC
IncorrectEnableIfCheck.cpp
IncorrectEnableSharedFromThisCheck.cpp
InvalidEnumDefaultInitializationCheck.cpp
+ SprintfToSnprintfCheck.cpp
UnintendedCharOstreamOutputCheck.cpp
ReturnConstRefFromParameterCheck.cpp
SuspiciousStringviewDataUsageCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.cpp
new file mode 100644
index 0000000000000..e357beb3e182e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "SprintfToSnprintfCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+void SprintfToSnprintfCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(hasName("::sprintf"))),
+ hasArgument(
+ 0, ignoringParenImpCasts(declRefExpr(to(
+ varDecl(hasType(constantArrayType())).bind("buffer")
+ )).bind("arg0"))
+ )
+ ).bind("call"),
+ this);
+}
+
+void SprintfToSnprintfCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
+ const auto *Buffer = Result.Nodes.getNodeAs<VarDecl>("buffer");
+
+ if (!Call || !Buffer)
+ return;
+
+ StringRef BufferName = Buffer->getName();
+
+ auto Diag = diag(Call->getBeginLoc(), "use 'snprintf' instead of 'sprintf' for fixed-size character arrays");
+
+ SourceLocation FuncNameLoc = Call->getExprLoc();
+ Diag << FixItHint::CreateReplacement(FuncNameLoc, "snprintf");
+
+ SourceLocation InsertLoc = Call->getArg(1)->getBeginLoc();
+ std::string SizeArg = "sizeof(" + BufferName.str() + "), ";
+ Diag << FixItHint::CreateInsertion(InsertLoc, SizeArg);
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.h b/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.h
new file mode 100644
index 0000000000000..2a9b4dd3c3041
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.h
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_SPRINTFTOSNPRINTFCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPRINTFTOSNPRINTFCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// FIXME: Write a short description.
+///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/bugprone/sprintf-to-snprintf.html
+class SprintfToSnprintfCheck : public ClangTidyCheck {
+public:
+ SprintfToSnprintfCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ 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;
+ }
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPRINTFTOSNPRINTFCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index e7437e62ee77d..23757aefb13e2 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -97,6 +97,12 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^
+- New :doc:`bugprone-sprintf-to-snprintf
+ <clang-tidy/checks/bugprone/sprintf-to-snprintf>` check.
+
+ Finds calls to ``sprintf`` where the destination is a fixed-size character
+ array and suggests replacing them with the safer ``snprintf``.
+
- New :doc:`llvm-type-switch-case-types
<clang-tidy/checks/llvm/type-switch-case-types>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-to-snprintf.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-to-snprintf.rst
new file mode 100644
index 0000000000000..dc57e1736f3bd
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-to-snprintf.rst
@@ -0,0 +1,27 @@
+.. title:: clang-tidy - bugprone-sprintf-to-snprintf
+
+bugprone-sprintf-to-snprintf
+============================
+
+Finds calls to ``sprintf`` where the destination is a fixed-size character array and replaces them with the safer ``snprintf``.
+
+It's a common idiom to have a fixed-size buffer of characters allocated on the stack and then to ``printf`` into the buffer. This can easily lead to buffer overflows. This check recommends that the counted version of the function is used instead.
+
+Example
+-------
+
+.. code-block:: c++
+
+ void f() {
+ char buff[80];
+ sprintf(buff, "Hello, %s!\n", "world");
+ }
+
+Becomes:
+
+.. code-block:: c++
+
+ void f() {
+ char buff[80];
+ snprintf(buff, sizeof(buff), "Hello, %s!\n", "world");
+ }
\ No newline at end of file
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 0eabd9929dc39..0136a4849a075 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -144,6 +144,7 @@ Clang-Tidy Checks
:doc:`bugprone-signed-char-misuse <bugprone/signed-char-misuse>`,
:doc:`bugprone-sizeof-container <bugprone/sizeof-container>`,
:doc:`bugprone-sizeof-expression <bugprone/sizeof-expression>`,
+ :doc:`bugprone-sprintf-to-snprintf <bugprone/sprintf-to-snprintf>`, "Yes"
:doc:`bugprone-spuriously-wake-up-functions <bugprone/spuriously-wake-up-functions>`,
:doc:`bugprone-standalone-empty <bugprone/standalone-empty>`, "Yes"
:doc:`bugprone-std-namespace-modification <bugprone/std-namespace-modification>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-to-snprintf.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-to-snprintf.cpp
new file mode 100644
index 0000000000000..f5d6ff5c59ee4
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-to-snprintf.cpp
@@ -0,0 +1,16 @@
+// RUN: %check_clang_tidy %s bugprone-sprintf-to-snprintf %t
+
+extern "C" int sprintf(char *str, const char *format, ...);
+extern "C" int snprintf(char *s, unsigned long n, const char *format, ...);
+
+void f() {
+ char buff[80];
+ sprintf(buff, "Hello, %s!\n", "world");
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'snprintf' instead of 'sprintf' for fixed-size character arrays [bugprone-sprintf-to-snprintf]
+ // CHECK-FIXES: snprintf(buff, sizeof(buff), "Hello, %s!\n", "world");
+}
+
+void ignore_pointers(char* ptr) {
+ sprintf(ptr, "Hello");
+ // Should not trigger because it's not a fixed-size array.
+}
\ No newline at end of file
More information about the cfe-commits
mailing list