[clang-tools-extra] Add bugprone-uninitialized-thread-local to detect when thread-locals might be accessed without initialization (PR #136602)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 21 13:16:48 PDT 2025
https://github.com/higher-performance created https://github.com/llvm/llvm-project/pull/136602
This is to partially address issue #112782.
>From f86372ac92bda26a99bf0b17acd226d677853a47 Mon Sep 17 00:00:00 2001
From: higher-performance <higher.performance.github at gmail.com>
Date: Mon, 21 Apr 2025 16:09:44 -0400
Subject: [PATCH] Add bugprone-uninitialized-thread-local to detect when
thread-locals might be accessed without initialization
This is to partially address issue #112782.
---
.../bugprone/BugproneTidyModule.cpp | 1 +
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../UninitializedThreadLocalCheck.cpp | 46 +++++++++++++++++++
.../bugprone/UninitializedThreadLocalCheck.h | 43 +++++++++++++++++
.../bugprone/uninitialized-thread-local.cpp | 16 +++++++
5 files changed, 107 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/UninitializedThreadLocalCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/UninitializedThreadLocalCheck.h
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/uninitialized-thread-local.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index b780a85bdf3fe..7b75b26c97fdb 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -91,6 +91,7 @@
#include "UndelegatedConstructorCheck.h"
#include "UnhandledExceptionAtNewCheck.h"
#include "UnhandledSelfAssignmentCheck.h"
+#include "UninitializedThreadLocalCheck.h"
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index e310ea9c94543..56ef12f48987a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -30,6 +30,7 @@ add_clang_library(clangTidyBugproneModule STATIC
InaccurateEraseCheck.cpp
IncorrectEnableIfCheck.cpp
IncorrectEnableSharedFromThisCheck.cpp
+ UninitializedThreadLocalCheck.cpp
UnintendedCharOstreamOutputCheck.cpp
ReturnConstRefFromParameterCheck.cpp
SuspiciousStringviewDataUsageCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UninitializedThreadLocalCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UninitializedThreadLocalCheck.cpp
new file mode 100644
index 0000000000000..c6037944caff3
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UninitializedThreadLocalCheck.cpp
@@ -0,0 +1,46 @@
+//===--- UninitializedThreadLocalCheck.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 "UninitializedThreadLocalCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UninitializedThreadLocalCheck::UninitializedThreadLocalCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+void UninitializedThreadLocalCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ declRefExpr(
+ // Fast check -- bail out quickly before slower filters
+ to(varDecl(hasThreadStorageDuration(),
+ hasDeclContext(functionDecl()))),
+ forCallable(decl().bind("ctx")),
+ to(varDecl(unless(hasDeclContext(equalsBoundNode("ctx"))))))
+ .bind("declref"),
+ this);
+}
+
+void UninitializedThreadLocalCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *E = Result.Nodes.getNodeAs<DeclRefExpr>("declref");
+ diag(E->getLocation(),
+ "variable '%0' might not have been initialized on the current thread. "
+ "To guarantee prior initialization on the same thread that performs the "
+ "access, consider capturing the address of the variable in the same "
+ "block as its initialization, then use the captured address to the "
+ "desired code.")
+ << E;
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/UninitializedThreadLocalCheck.h b/clang-tools-extra/clang-tidy/bugprone/UninitializedThreadLocalCheck.h
new file mode 100644
index 0000000000000..4f31b0984ebc2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UninitializedThreadLocalCheck.h
@@ -0,0 +1,43 @@
+//===--- UninitializedThreadLocalCheck.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 "UninitializedThreadLocalCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UninitializedThreadLocalCheck::UninitializedThreadLocalCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+void UninitializedThreadLocalCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ declRefExpr(to(varDecl(hasThreadStorageDuration()).bind("var")),
+ hasAncestor(functionDecl(
+ unless(hasDescendant(varDecl(equalsBoundNode("var")))))))
+ .bind("declref"),
+ this);
+}
+
+void UninitializedThreadLocalCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *E = Result.Nodes.getNodeAs<DeclRefExpr>("declref");
+ diag(E->getLocation(),
+ "variable '%0' might not have been initialized on the current thread. "
+ "To guarantee prior initialization on the same thread that performs the "
+ "access, consider capturing the address of the variable in the same "
+ "block as its initialization, then use the captured address to the "
+ "desired code.")
+ << E;
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/uninitialized-thread-local.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/uninitialized-thread-local.cpp
new file mode 100644
index 0000000000000..59f773f843a24
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/uninitialized-thread-local.cpp
@@ -0,0 +1,16 @@
+// RUN: %check_clang_tidy %s bugprone-uninitialized-thread-local %t
+
+thread_local int global_tls = 1;
+
+int main() {
+ thread_local int local_tls = 2;
+ {
+ ++global_tls; // no warning
+ ++local_tls; // no warning
+ }
+ auto f = []() {
+ return local_tls + global_tls;
+ // CHECK-MESSAGES: [[@LINE-1]]:12: warning: variable 'local_tls' might not have been initialized on the current thread.
+ };
+ return f();
+}
More information about the cfe-commits
mailing list