[clang-tools-extra] [clang-tidy] add new check: modernize-use-scoped-lock (PR #126434)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 13 23:38:49 PST 2025
https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/126434
>From 92588a7eb3f87e74887e94f88d3402ec25c6ee53 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 9 Feb 2025 22:34:26 +0300
Subject: [PATCH 1/7] [clang-tidy] add modernize-use-scoped-lock check
---
.../clang-tidy/modernize/CMakeLists.txt | 1 +
.../modernize/ModernizeTidyModule.cpp | 3 +
.../modernize/UseScopedLockCheck.cpp | 234 ++++++++++++++
.../clang-tidy/modernize/UseScopedLockCheck.h | 50 +++
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../checks/modernize/use-scoped-lock.rst | 81 +++++
.../use-scoped-lock-only-warn-on-multiple.cpp | 122 ++++++++
.../checkers/modernize/use-scoped-lock.cpp | 290 ++++++++++++++++++
9 files changed, 788 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index bab1167fb15ff..619a27b2f9bb6 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -42,6 +42,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseNullptrCheck.cpp
UseOverrideCheck.cpp
UseRangesCheck.cpp
+ UseScopedLockCheck.cpp
UseStartsEndsWithCheck.cpp
UseStdFormatCheck.cpp
UseStdNumbersCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index fc46c72982fdc..b2d4ddd667502 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -43,6 +43,7 @@
#include "UseNullptrCheck.h"
#include "UseOverrideCheck.h"
#include "UseRangesCheck.h"
+#include "UseScopedLockCheck.h"
#include "UseStartsEndsWithCheck.h"
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
@@ -80,6 +81,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseIntegerSignComparisonCheck>(
"modernize-use-integer-sign-comparison");
CheckFactories.registerCheck<UseRangesCheck>("modernize-use-ranges");
+ CheckFactories.registerCheck<UseScopedLockCheck>(
+ "modernize-use-scoped-lock");
CheckFactories.registerCheck<UseStartsEndsWithCheck>(
"modernize-use-starts-ends-with");
CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format");
diff --git a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
new file mode 100644
index 0000000000000..af2fea5ad310e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
@@ -0,0 +1,234 @@
+//===--- UseScopedLockCheck.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 "UseScopedLockCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/Twine.h"
+#include <iterator>
+#include <optional>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+namespace {
+
+bool isLockGuard(const QualType &Type) {
+ if (const auto *RecordTy = Type->getAs<RecordType>()) {
+ if (const auto *RecordDecl = RecordTy->getDecl()) {
+ return RecordDecl->getQualifiedNameAsString() == "std::lock_guard";
+ }
+ }
+
+ if (const auto *TemplateSpecType =
+ Type->getAs<TemplateSpecializationType>()) {
+ if (const auto *TemplateDecl =
+ TemplateSpecType->getTemplateName().getAsTemplateDecl()) {
+ return TemplateDecl->getQualifiedNameAsString() == "std::lock_guard";
+ }
+ }
+
+ return false;
+}
+
+std::vector<const VarDecl *> getLockGuardsFromDecl(const DeclStmt *DS) {
+ std::vector<const VarDecl *> LockGuards;
+
+ for (const auto *Decl : DS->decls()) {
+ if (const auto *VD = dyn_cast<VarDecl>(Decl)) {
+ const QualType Type = VD->getType().getCanonicalType();
+ if (isLockGuard(Type)) {
+ LockGuards.push_back(VD);
+ }
+ }
+ }
+
+ return LockGuards;
+}
+
+// Scans through the statements in a block and groups consecutive
+// 'std::lock_guard' variable declarations together.
+std::vector<std::vector<const VarDecl *>>
+findLocksInCompoundStmt(const CompoundStmt *Block,
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ // store groups of consecutive 'std::lock_guard' declarations
+ std::vector<std::vector<const VarDecl *>> LockGuardGroups;
+ std::vector<const VarDecl *> CurrentLockGuardGroup;
+
+ auto AddAndClearCurrentGroup = [&]() {
+ if (!CurrentLockGuardGroup.empty()) {
+ LockGuardGroups.push_back(CurrentLockGuardGroup);
+ CurrentLockGuardGroup.clear();
+ }
+ };
+
+ for (const auto *Stmt : Block->body()) {
+ if (const auto *DS = dyn_cast<DeclStmt>(Stmt)) {
+ std::vector<const VarDecl *> LockGuards = getLockGuardsFromDecl(DS);
+
+ if (!LockGuards.empty()) {
+ CurrentLockGuardGroup.insert(
+ CurrentLockGuardGroup.end(),
+ std::make_move_iterator(LockGuards.begin()),
+ std::make_move_iterator(LockGuards.end()));
+ }
+
+ if (LockGuards.empty()) {
+ AddAndClearCurrentGroup();
+ }
+ } else {
+ AddAndClearCurrentGroup();
+ }
+ }
+
+ AddAndClearCurrentGroup();
+
+ return LockGuardGroups;
+}
+
+// Find the exact source range of the 'lock_guard<...>' token
+std::optional<SourceRange> getLockGuardRange(const VarDecl *LockGuard,
+ SourceManager &SM) {
+ const auto *SourceInfo = LockGuard->getTypeSourceInfo();
+ const auto TypeLoc = SourceInfo->getTypeLoc();
+
+ const auto ElaboratedLoc = TypeLoc.getAs<ElaboratedTypeLoc>();
+ if (!ElaboratedLoc)
+ return std::nullopt;
+
+ const auto TemplateLoc =
+ ElaboratedLoc.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>();
+ if (!TemplateLoc)
+ return std::nullopt;
+
+ const SourceLocation LockGuardBeginLoc = TemplateLoc.getTemplateNameLoc();
+ const SourceLocation LockGuardRAngleLoc = TemplateLoc.getRAngleLoc();
+
+ return SourceRange(LockGuardBeginLoc, LockGuardRAngleLoc);
+}
+
+} // namespace
+
+void UseScopedLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "WarnOnlyMultipleLocks", WarnOnlyMultipleLocks);
+}
+
+void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
+ auto LockGuardType = qualType(hasDeclaration(namedDecl(
+ hasName("::std::lock_guard"),
+ anyOf(classTemplateDecl(), classTemplateSpecializationDecl()))));
+ auto LockVarDecl = varDecl(hasType(LockGuardType));
+
+ // Match CompoundStmt with only one 'std::lock_guard'
+ if (!WarnOnlyMultipleLocks) {
+ Finder->addMatcher(
+ compoundStmt(has(declStmt(has(LockVarDecl.bind("lock-decl-single")))),
+ unless(hasDescendant(declStmt(has(varDecl(
+ hasType(LockGuardType),
+ unless(equalsBoundNode("lock-decl-single")))))))),
+ this);
+ }
+
+ // Match CompoundStmt with multiple 'std::lock_guard'
+ Finder->addMatcher(
+ compoundStmt(has(declStmt(has(LockVarDecl.bind("lock-decl-multiple")))),
+ hasDescendant(declStmt(has(varDecl(
+ hasType(LockGuardType),
+ unless(equalsBoundNode("lock-decl-multiple")))))))
+ .bind("block-multiple"),
+ this);
+}
+
+void UseScopedLockCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *Decl = Result.Nodes.getNodeAs<VarDecl>("lock-decl-single")) {
+ emitDiag(Decl, Result);
+ }
+
+ if (const auto *Compound =
+ Result.Nodes.getNodeAs<CompoundStmt>("block-multiple")) {
+ emitDiag(findLocksInCompoundStmt(Compound, Result), Result);
+ }
+}
+
+void UseScopedLockCheck::emitDiag(const VarDecl *LockGuard,
+ const MatchFinder::MatchResult &Result) {
+ auto Diag = diag(LockGuard->getBeginLoc(),
+ "use 'std::scoped_lock' instead of 'std::lock_guard'");
+
+ const std::optional<SourceRange> LockGuardTypeRange =
+ getLockGuardRange(LockGuard, *Result.SourceManager);
+
+ if (!LockGuardTypeRange) {
+ return;
+ }
+
+ // Create Fix-its only if we can find the constructor call to handle
+ // 'std::lock_guard l(m, std::adopt_lock)' case
+ const auto *CtorCall = dyn_cast<CXXConstructExpr>(LockGuard->getInit());
+ if (!CtorCall) {
+ return;
+ }
+
+ switch (CtorCall->getNumArgs()) {
+ case 1:
+ Diag << FixItHint::CreateReplacement(LockGuardTypeRange.value(),
+ "scoped_lock");
+ return;
+ case 2:
+ const auto *CtorArgs = CtorCall->getArgs();
+
+ const Expr *MutexArg = CtorArgs[0];
+ const Expr *AdoptLockArg = CtorArgs[1];
+
+ const StringRef MutexSourceText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(MutexArg->getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+ const StringRef AdoptLockSourceText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(AdoptLockArg->getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+
+ Diag << FixItHint::CreateReplacement(LockGuardTypeRange.value(),
+ "scoped_lock")
+ << FixItHint::CreateReplacement(
+ SourceRange(CtorArgs[0]->getBeginLoc(),
+ CtorArgs[1]->getEndLoc()),
+ (llvm::Twine(AdoptLockSourceText) + ", " + MutexSourceText)
+ .str());
+ return;
+ }
+
+ llvm_unreachable("Invalid argument number of std::lock_guard constructor");
+}
+
+void UseScopedLockCheck::emitDiag(
+ const std::vector<std::vector<const VarDecl *>> &LockGuardGroups,
+ const MatchFinder::MatchResult &Result) {
+ for (const auto &Group : LockGuardGroups) {
+ if (Group.size() == 1 && !WarnOnlyMultipleLocks) {
+ emitDiag(Group[0], Result);
+ } else {
+ diag(Group[0]->getBeginLoc(),
+ "use single 'std::scoped_lock' instead of multiple "
+ "'std::lock_guard'");
+
+ for (size_t I = 1; I < Group.size(); ++I) {
+ diag(Group[I]->getLocation(),
+ "additional 'std::lock_guard' declared here", DiagnosticIDs::Note);
+ }
+ }
+ }
+}
+
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
new file mode 100644
index 0000000000000..0e1fd8067ddd1
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
@@ -0,0 +1,50 @@
+//===--- UseScopedLockCheck.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_MODERNIZE_USESCOPEDLOCKCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESCOPEDLOCKCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "clang/AST/ASTTypeTraits.h"
+#include "clang/AST/Stmt.h"
+#include <optional>
+
+namespace clang::tidy::modernize {
+
+/// Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's
+/// more flexible and safer alternative ``std::scoped_lock``.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-scoped-lock.html
+class UseScopedLockCheck : public ClangTidyCheck {
+public:
+ UseScopedLockCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ WarnOnlyMultipleLocks(Options.get("WarnOnlyMultipleLocks", false)) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus17;
+ }
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ void emitDiag(const VarDecl *LockGuard,
+ const ast_matchers::MatchFinder::MatchResult &Result);
+ void emitDiag(const std::vector<std::vector<const VarDecl *>> &LockGroups,
+ const ast_matchers::MatchFinder::MatchResult &Result);
+
+ const bool WarnOnlyMultipleLocks;
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESCOPEDLOCKCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3bddeeda06e06..6aefbc4737276 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -91,6 +91,12 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^
+- New :doc:`use-scoped-lock
+ <clang-tidy/checks/modernize/use-scoped-lock>` check.
+
+ Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's
+ more flexible and safer alternative ``std::scoped_lock``.
+
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..b21633459a95c 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -308,6 +308,7 @@ Clang-Tidy Checks
:doc:`modernize-use-nullptr <modernize/use-nullptr>`, "Yes"
:doc:`modernize-use-override <modernize/use-override>`, "Yes"
:doc:`modernize-use-ranges <modernize/use-ranges>`, "Yes"
+ :doc:`modernize-use-scoped-lock <modernize/use-scoped-lock>`, "Yes"
:doc:`modernize-use-starts-ends-with <modernize/use-starts-ends-with>`, "Yes"
:doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
:doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
new file mode 100644
index 0000000000000..04b35d0081b3c
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
@@ -0,0 +1,81 @@
+.. title:: clang-tidy - modernize-use-scoped-lock
+
+modernize-use-scoped-lock
+=========================
+
+Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's more
+flexible and safer alternative ``std::scoped_lock``. The check will
+automatically transform only single declarations of ``std::lock_guard`` and
+emit warnings for multiple declarations of ``std::lock_guard`` that can be
+replaced with a single declaration of ``std::scoped_lock``.
+
+Examples
+--------
+
+Single ``std::lock_guard`` declaration:
+
+.. code-block:: c++
+
+ std::mutex M;
+ std::lock_guard<std::mutex> L(M);
+
+
+Transforms to:
+
+.. code-block:: c++
+
+ std::mutex m;
+ std::scoped_lock L(M);
+
+Single ``std::lock_guard`` declaration with ``std::adopt_lock``:
+
+.. code-block:: c++
+
+ std::mutex M;
+ std::lock(M);
+ std::lock_guard<std::mutex> L(M, std::adopt_lock);
+
+
+Transforms to:
+
+.. code-block:: c++
+
+ std::mutex M;
+ std::lock(M);
+ std::scoped_lock L(std::adopt_lock, M);
+
+Multiple ``std::lock_guard`` declarations only emit warnings:
+
+.. code-block:: c++
+
+ std::mutex M1, M2;
+ std::lock(M1, M2);
+ std::lock_guard Lock1(M, std::adopt_lock); // warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ std::lock_guard Lock2(M, std::adopt_lock); // note: additional 'std::lock_guard' declared here
+
+
+Limitations
+-----------
+
+The check will not emit warnings if ``std::lock_guard`` is used implicitly via
+``using``, ``typedef`` or ``template``.
+
+.. code-block:: c
+
+ template <template <typename> typename Lock>
+ void TemplatedLock() {
+ std::mutex m;
+ Lock<std::mutex> l(m); // no warning
+ }
+
+ void UsingLock() {
+ using Lock = std::lock_guard<std::mutex>;
+ std::mutex m;
+ Lock l(m); // no warning
+ }
+
+.. option:: WarnOnlyMultipleLocks
+
+ When `true`, the check will only emit warnings if the there are multiple
+ consecutive ``std::lock_guard`` declarations that can be replaced with a
+ single ``std::scoped_lock`` declaration. Default is `false`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
new file mode 100644
index 0000000000000..6d46e43bc2be4
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
@@ -0,0 +1,122 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t -- \
+// RUN: -config="{CheckOptions: {modernize-use-scoped-lock.WarnOnlyMultipleLocks: true}}"
+
+namespace std {
+
+struct mutex {
+ void lock() {}
+ void unlock() {}
+};
+
+template<class Lockable1, class Lockable2, class... LockableN >
+void lock(Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );
+
+struct adopt_lock_t { };
+std::adopt_lock_t adopt_lock {};
+
+template <typename Mutex>
+struct lock_guard {
+ lock_guard(Mutex &m) { }
+ lock_guard(Mutex &m, std::adopt_lock_t t) {}
+ lock_guard( const lock_guard& ) = delete;
+};
+
+template <typename... MutexTypes>
+struct scoped_lock {
+ scoped_lock(MutexTypes&... m) {}
+ scoped_lock(std::adopt_lock_t t, MutexTypes&... m) {}
+};
+
+} // namespace std
+
+
+void Positive() {
+ std::mutex m;
+
+ {
+ std::lock_guard<std::mutex> l1(m);
+ std::lock_guard<std::mutex> l2(m);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: note: additional 'std::lock_guard' declared here
+ }
+
+ {
+ std::lock_guard<std::mutex> l1(m), l2(m), l3(m);
+ std::lock_guard<std::mutex> l4(m);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-3]]:40: note: additional 'std::lock_guard' declared here
+ // CHECK-MESSAGES: :[[@LINE-4]]:47: note: additional 'std::lock_guard' declared here
+ // CHECK-MESSAGES: :[[@LINE-4]]:33: note: additional 'std::lock_guard' declared here
+ }
+
+ {
+ std::lock(m, m);
+ std::lock_guard<std::mutex> l1(m, std::adopt_lock);
+ std::lock_guard<std::mutex> l2(m, std::adopt_lock);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: note: additional 'std::lock_guard' declared here
+ }
+}
+
+void Negative() {
+ std::mutex m;
+ {
+ std::lock_guard<std::mutex> l(m);
+ }
+
+ {
+ std::lock_guard<std::mutex> l(m, std::adopt_lock);
+ }
+}
+
+void PositiveInsideArg(std::mutex &m1, std::mutex &m2, std::mutex &m3) {
+ std::lock_guard<std::mutex> l1(m1);
+ std::lock_guard<std::mutex> l2(m2);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:31: note: additional 'std::lock_guard' declared here
+}
+
+
+void NegativeInsideArg(std::mutex &m1, std::mutex &m2, std::mutex &m3) {
+ std::lock_guard<std::mutex> l3(m3);
+}
+
+template <typename T>
+void PositiveTemplated() {
+ std::mutex m1, m2;
+
+ std::lock_guard<std::mutex> l1(m1);
+ std::lock_guard<std::mutex> l2(m2);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:31: note: additional 'std::lock_guard' declared here
+}
+
+template <typename T>
+void NegativeTemplated() {
+ std::mutex m1, m2, m3;
+ std::lock_guard<std::mutex> l(m1);
+}
+
+template <typename Mutex>
+void PositiveTemplatedMutex() {
+ Mutex m1, m2;
+
+ std::lock_guard<Mutex> l1(m1);
+ std::lock_guard<Mutex> l2(m2);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:26: note: additional 'std::lock_guard' declared here
+}
+
+template <typename Mutex>
+void NegativeTemplatedMutex() {
+ Mutex m1;
+ std::lock_guard<Mutex> l(m1);
+}
+
+struct NegativeClass {
+ void Negative() {
+ std::lock_guard<std::mutex> l(m1);
+ }
+
+ std::mutex m1;
+};
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
new file mode 100644
index 0000000000000..87ccdf1b04227
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
@@ -0,0 +1,290 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t
+
+namespace std {
+
+struct mutex {
+ void lock() {}
+ void unlock() {}
+};
+
+template<class Lockable1, class Lockable2, class... LockableN >
+void lock(Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );
+
+struct adopt_lock_t { };
+std::adopt_lock_t adopt_lock {};
+
+template <typename Mutex>
+struct lock_guard {
+ lock_guard(Mutex &m) { }
+ lock_guard(Mutex &m, std::adopt_lock_t t) {}
+ lock_guard( const lock_guard& ) = delete;
+};
+
+template <typename... MutexTypes>
+struct scoped_lock {
+ scoped_lock(MutexTypes&... m) {}
+ scoped_lock(std::adopt_lock_t t, MutexTypes&... m) {}
+};
+
+} // namespace std
+
+
+void Positive() {
+ std::mutex m;
+ {
+ std::lock_guard<std::mutex> l(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(m);
+ }
+
+ {
+ std::lock_guard<std::mutex> l(m, std::adopt_lock);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(std::adopt_lock, m);
+ }
+
+ {
+ std::lock_guard<std::mutex> l1(m);
+ std::lock_guard<std::mutex> l2(m);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: note: additional 'std::lock_guard' declared here
+ }
+
+ {
+ std::lock_guard<std::mutex> l1(m), l2(m), l3(m);
+ std::lock_guard<std::mutex> l4(m);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-3]]:40: note: additional 'std::lock_guard' declared here
+ // CHECK-MESSAGES: :[[@LINE-4]]:47: note: additional 'std::lock_guard' declared here
+ // CHECK-MESSAGES: :[[@LINE-4]]:33: note: additional 'std::lock_guard' declared here
+ }
+
+ {
+ std::lock(m, m);
+ std::lock_guard<std::mutex> l1(m, std::adopt_lock);
+ std::lock_guard<std::mutex> l2(m, std::adopt_lock);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: note: additional 'std::lock_guard' declared here
+ int a = 0;
+ std::lock_guard<std::mutex> l3(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l3(m);
+ int b = 0;
+ std::lock_guard<std::mutex> l4(m, std::adopt_lock);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l4(std::adopt_lock, m);
+ }
+
+}
+
+
+void PositiveInsideArg(std::mutex &m1, std::mutex &m2, std::mutex &m3) {
+ std::lock_guard<std::mutex> l1(m1);
+ std::lock_guard<std::mutex> l2(m2);
+ // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:31: note: additional 'std::lock_guard' declared here
+ int a = 0;
+ std::lock_guard<std::mutex> l3(m3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l3(m3);
+}
+
+void PositiveInsideConditional() {
+ std::mutex m1;
+ if (true) {
+ std::lock_guard<std::mutex> l1(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l1(m1);
+ } else {
+ std::lock_guard<std::mutex> l1(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l1(m1);
+ }
+
+ while (true) {
+ std::lock_guard<std::mutex> l1(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l1(m1);
+ }
+
+ for (int i = 0; i < 10; ++i) {
+ std::lock_guard<std::mutex> l1(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l1(m1);
+ }
+}
+
+template <typename T>
+void PositiveTemplated() {
+ std::mutex m1, m2, m3;
+ {
+ std::lock_guard<std::mutex> l(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(m1);
+ }
+
+ {
+ std::lock_guard<std::mutex> l1(m1);
+ std::lock_guard<std::mutex> l2(m2);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: note: additional 'std::lock_guard' declared here
+ }
+
+ {
+ std::lock(m1, m2);
+ std::lock_guard<std::mutex> l1(m1, std::adopt_lock);
+ std::lock_guard<std::mutex> l2(m2, std::adopt_lock);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:33: note: additional 'std::lock_guard' declared here
+ int a = 0;
+ std::lock_guard<std::mutex> l3(m3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l3(m3);
+ }
+}
+
+template <typename Mutex>
+void PositiveTemplatedMutex() {
+ Mutex m1, m2, m3;
+ {
+ std::lock_guard<Mutex> l(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ }
+
+ {
+ std::lock_guard<Mutex> l1(m1);
+ std::lock_guard<Mutex> l2(m2);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:28: note: additional 'std::lock_guard' declared here
+ }
+
+ {
+ std::lock(m1, m2);
+ std::lock_guard<Mutex> l1(m1, std::adopt_lock);
+ std::lock_guard<Mutex> l2(m2, std::adopt_lock);
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:28: note: additional 'std::lock_guard' declared here
+ int a = 0;
+ std::lock_guard<Mutex> l3(m3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ }
+}
+
+template <template <typename> typename Lock>
+void NegativeTemplate() {
+ std::mutex m1, m2;
+ {
+ Lock<std::mutex> l(m1);
+ }
+
+ {
+ Lock<std::mutex> l1(m1);
+ Lock<std::mutex> l2(m2);
+ }
+}
+
+void instantiate() {
+ NegativeTemplate<std::lock_guard>();
+}
+
+struct PositiveClass {
+ void Positive() {
+ {
+ std::lock_guard<std::mutex> l(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(m1);
+ }
+
+ {
+ std::lock_guard<std::mutex> l1(m1);
+ std::lock_guard<std::mutex> l2(m2);
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:35: note: additional 'std::lock_guard' declared here
+ }
+
+ {
+ std::lock(m1, m2);
+ std::lock_guard<std::mutex> l1(m1, std::adopt_lock);
+ std::lock_guard<std::mutex> l2(m2, std::adopt_lock);
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:35: note: additional 'std::lock_guard' declared here
+ int a = 0;
+ std::lock_guard<std::mutex> l3(m3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l3(m3);
+ }
+ }
+
+ std::mutex m1;
+ std::mutex m2;
+ std::mutex m3;
+};
+
+template <typename T>
+struct PositiveTemplatedClass {
+ void Positive() {
+ {
+ std::lock_guard<std::mutex> l(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(m1);
+ }
+
+ {
+ std::lock(m1, m2);
+ std::lock_guard<std::mutex> l1(m1, std::adopt_lock);
+ std::lock_guard<std::mutex> l2(m2, std::adopt_lock);
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:35: note: additional 'std::lock_guard' declared here
+ int a = 0;
+ std::lock_guard<std::mutex> l3(m3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l3(m3);
+ }
+ }
+
+ template <typename... Ts>
+ void TemplatedPositive() {
+ {
+ std::lock_guard<std::mutex> l(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(m1);
+ }
+
+ {
+ std::lock(m1, m2);
+ std::lock_guard<std::mutex> l1(m1, std::adopt_lock);
+ std::lock_guard<std::mutex> l2(m2, std::adopt_lock);
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:35: note: additional 'std::lock_guard' declared here
+ int a = 0;
+ std::lock_guard<std::mutex> l3(m3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l3(m3);
+ }
+ }
+
+ std::mutex m1;
+ std::mutex m2;
+ std::mutex m3;
+};
+
+template <typename T>
+using Lock = std::lock_guard<T>;
+using LockM = std::lock_guard<std::mutex>;
+typedef std::lock_guard<std::mutex> LockDef;
+
+void NegativeUsingTypedefs() {
+ std::mutex m;
+
+ {
+ Lock<std::mutex> l(m);
+ }
+
+ {
+ LockM l(m);
+ }
+
+ {
+ LockDef l(m);
+ }
+}
>From 068c9217436b963a0ce3fa7251e1d52e088ba73b Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Mon, 10 Feb 2025 13:42:12 +0300
Subject: [PATCH 2/7] [clang-tidy] feat: add more tests for
modernize-use-lock-guard
---
.../checkers/modernize/use-scoped-lock.cpp | 45 ++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
index 87ccdf1b04227..dae0b72076d4b 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
@@ -73,8 +73,44 @@ void Positive() {
std::lock_guard<std::mutex> l4(m, std::adopt_lock);
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
// CHECK-FIXES: std::scoped_lock l4(std::adopt_lock, m);
+ }
+}
+
+
+std::mutex p_m1;
+void PositiveShortFunction() {
+ std::lock_guard<std::mutex> l(p_m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(p_m1);
+}
+
+
+void PositiveNested() {
+ std::mutex m1;
+ if (true) {
+ std::lock_guard<std::mutex> l(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(m1);
+ {
+ std::lock_guard<std::mutex> l2(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l2(m1);
+ {
+ std::lock_guard<std::mutex> l3(m1);
+ std::lock_guard<std::mutex> l4(m1);
+ // CHECK-MESSAGES: :[[@LINE-2]]:9: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:37: note: additional 'std::lock_guard' declared here
+ }
+ {
+ std::lock_guard<std::mutex> l2(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l2(m1);
+ }
+ }
}
-
+ std::lock_guard<std::mutex> l(m1);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(m1);
}
@@ -89,6 +125,7 @@ void PositiveInsideArg(std::mutex &m1, std::mutex &m2, std::mutex &m3) {
// CHECK-FIXES: std::scoped_lock l3(m3);
}
+
void PositiveInsideConditional() {
std::mutex m1;
if (true) {
@@ -114,6 +151,7 @@ void PositiveInsideConditional() {
}
}
+
template <typename T>
void PositiveTemplated() {
std::mutex m1, m2, m3;
@@ -143,6 +181,7 @@ void PositiveTemplated() {
}
}
+
template <typename Mutex>
void PositiveTemplatedMutex() {
Mutex m1, m2, m3;
@@ -170,6 +209,7 @@ void PositiveTemplatedMutex() {
}
}
+
template <template <typename> typename Lock>
void NegativeTemplate() {
std::mutex m1, m2;
@@ -187,6 +227,7 @@ void instantiate() {
NegativeTemplate<std::lock_guard>();
}
+
struct PositiveClass {
void Positive() {
{
@@ -220,6 +261,7 @@ struct PositiveClass {
std::mutex m3;
};
+
template <typename T>
struct PositiveTemplatedClass {
void Positive() {
@@ -268,6 +310,7 @@ struct PositiveTemplatedClass {
std::mutex m3;
};
+
template <typename T>
using Lock = std::lock_guard<T>;
using LockM = std::lock_guard<std::mutex>;
>From 7c5c295111824b2a95b6a1412a2a8c2c7b5550d2 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Mon, 10 Feb 2025 20:32:49 +0300
Subject: [PATCH 3/7] [clang-tidy] removed auto's and added
no-delayed-template-parsing
---
.../modernize/UseScopedLockCheck.cpp | 27 +++++++++----------
.../use-scoped-lock-only-warn-on-multiple.cpp | 2 +-
.../checkers/modernize/use-scoped-lock.cpp | 2 +-
3 files changed, 15 insertions(+), 16 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
index af2fea5ad310e..5d8ece18865a2 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
@@ -26,17 +26,17 @@ namespace clang::tidy::modernize {
namespace {
bool isLockGuard(const QualType &Type) {
- if (const auto *RecordTy = Type->getAs<RecordType>()) {
- if (const auto *RecordDecl = RecordTy->getDecl()) {
- return RecordDecl->getQualifiedNameAsString() == "std::lock_guard";
+ if (const auto *Record = Type->getAs<RecordType>()) {
+ if (const RecordDecl *Decl = Record->getDecl()) {
+ return Decl->getQualifiedNameAsString() == "std::lock_guard";
}
}
if (const auto *TemplateSpecType =
Type->getAs<TemplateSpecializationType>()) {
- if (const auto *TemplateDecl =
+ if (const TemplateDecl *Decl =
TemplateSpecType->getTemplateName().getAsTemplateDecl()) {
- return TemplateDecl->getQualifiedNameAsString() == "std::lock_guard";
+ return Decl->getQualifiedNameAsString() == "std::lock_guard";
}
}
@@ -46,7 +46,7 @@ bool isLockGuard(const QualType &Type) {
std::vector<const VarDecl *> getLockGuardsFromDecl(const DeclStmt *DS) {
std::vector<const VarDecl *> LockGuards;
- for (const auto *Decl : DS->decls()) {
+ for (const Decl *Decl : DS->decls()) {
if (const auto *VD = dyn_cast<VarDecl>(Decl)) {
const QualType Type = VD->getType().getCanonicalType();
if (isLockGuard(Type)) {
@@ -74,7 +74,7 @@ findLocksInCompoundStmt(const CompoundStmt *Block,
}
};
- for (const auto *Stmt : Block->body()) {
+ for (const Stmt *Stmt : Block->body()) {
if (const auto *DS = dyn_cast<DeclStmt>(Stmt)) {
std::vector<const VarDecl *> LockGuards = getLockGuardsFromDecl(DS);
@@ -101,10 +101,10 @@ findLocksInCompoundStmt(const CompoundStmt *Block,
// Find the exact source range of the 'lock_guard<...>' token
std::optional<SourceRange> getLockGuardRange(const VarDecl *LockGuard,
SourceManager &SM) {
- const auto *SourceInfo = LockGuard->getTypeSourceInfo();
- const auto TypeLoc = SourceInfo->getTypeLoc();
+ const TypeSourceInfo *SourceInfo = LockGuard->getTypeSourceInfo();
+ const TypeLoc Loc = SourceInfo->getTypeLoc();
- const auto ElaboratedLoc = TypeLoc.getAs<ElaboratedTypeLoc>();
+ const auto ElaboratedLoc = Loc.getAs<ElaboratedTypeLoc>();
if (!ElaboratedLoc)
return std::nullopt;
@@ -187,7 +187,7 @@ void UseScopedLockCheck::emitDiag(const VarDecl *LockGuard,
"scoped_lock");
return;
case 2:
- const auto *CtorArgs = CtorCall->getArgs();
+ const Expr *const *CtorArgs = CtorCall->getArgs();
const Expr *MutexArg = CtorArgs[0];
const Expr *AdoptLockArg = CtorArgs[1];
@@ -202,8 +202,7 @@ void UseScopedLockCheck::emitDiag(const VarDecl *LockGuard,
Diag << FixItHint::CreateReplacement(LockGuardTypeRange.value(),
"scoped_lock")
<< FixItHint::CreateReplacement(
- SourceRange(CtorArgs[0]->getBeginLoc(),
- CtorArgs[1]->getEndLoc()),
+ SourceRange(MutexArg->getBeginLoc(), AdoptLockArg->getEndLoc()),
(llvm::Twine(AdoptLockSourceText) + ", " + MutexSourceText)
.str());
return;
@@ -215,7 +214,7 @@ void UseScopedLockCheck::emitDiag(const VarDecl *LockGuard,
void UseScopedLockCheck::emitDiag(
const std::vector<std::vector<const VarDecl *>> &LockGuardGroups,
const MatchFinder::MatchResult &Result) {
- for (const auto &Group : LockGuardGroups) {
+ for (const std::vector<const VarDecl *> &Group : LockGuardGroups) {
if (Group.size() == 1 && !WarnOnlyMultipleLocks) {
emitDiag(Group[0], Result);
} else {
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
index 6d46e43bc2be4..2def300cad22c 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
@@ -1,5 +1,5 @@
// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t -- \
-// RUN: -config="{CheckOptions: {modernize-use-scoped-lock.WarnOnlyMultipleLocks: true}}"
+// RUN: -config="{CheckOptions: {modernize-use-scoped-lock.WarnOnlyMultipleLocks: true}} -- -- -fno-delayed-template-parsing"
namespace std {
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
index dae0b72076d4b..d7193b999a483 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t -- -- -fno-delayed-template-parsing
namespace std {
>From b4dcae8dab7e8ad282c7a8d89123f352208f9109 Mon Sep 17 00:00:00 2001
From: baranov-V-V <bar.victor.2002 at gmail.com>
Date: Wed, 12 Feb 2025 02:59:09 +0300
Subject: [PATCH 4/7] [clang-tidy] added fno-delayed-template-parsing to fix
WIN tests
---
.../modernize/use-scoped-lock-only-warn-on-multiple.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
index 2def300cad22c..2948aaed4f7c5 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
@@ -1,5 +1,6 @@
// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t -- \
-// RUN: -config="{CheckOptions: {modernize-use-scoped-lock.WarnOnlyMultipleLocks: true}} -- -- -fno-delayed-template-parsing"
+// RUN: -config="{CheckOptions: {modernize-use-scoped-lock.WarnOnlyMultipleLocks: true}}" \
+// RUN: -- -fno-delayed-template-parsing
namespace std {
>From e6b587ac00bd379df7ecae9b3df3f3673a73b426 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Thu, 13 Feb 2025 01:33:48 +0300
Subject: [PATCH 5/7] [clang-tidy] fix pr comments and add
WarnOnUsingAndTypedefs
---
.../modernize/UseScopedLockCheck.cpp | 193 +++++++++++++-----
.../clang-tidy/modernize/UseScopedLockCheck.h | 16 +-
clang-tools-extra/docs/ReleaseNotes.rst | 4 +-
.../checks/modernize/use-scoped-lock.rst | 48 +++--
...e-lock-warn-on-using-and-typedef-false.cpp | 57 ++++++
.../use-scoped-lock-only-warn-on-multiple.cpp | 2 +-
.../checkers/modernize/use-scoped-lock.cpp | 61 ++++++
7 files changed, 305 insertions(+), 76 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-scope-lock-warn-on-using-and-typedef-false.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
index 5d8ece18865a2..ecde9f24d4d20 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
@@ -15,9 +15,9 @@
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include <iterator>
-#include <optional>
using namespace clang::ast_matchers;
@@ -28,7 +28,7 @@ namespace {
bool isLockGuard(const QualType &Type) {
if (const auto *Record = Type->getAs<RecordType>()) {
if (const RecordDecl *Decl = Record->getDecl()) {
- return Decl->getQualifiedNameAsString() == "std::lock_guard";
+ return Decl->getName() == "lock_guard" && Decl->isInStdNamespace();
}
}
@@ -36,15 +36,15 @@ bool isLockGuard(const QualType &Type) {
Type->getAs<TemplateSpecializationType>()) {
if (const TemplateDecl *Decl =
TemplateSpecType->getTemplateName().getAsTemplateDecl()) {
- return Decl->getQualifiedNameAsString() == "std::lock_guard";
+ return Decl->getName() == "lock_guard" && Decl->isInStdNamespace();
}
}
return false;
}
-std::vector<const VarDecl *> getLockGuardsFromDecl(const DeclStmt *DS) {
- std::vector<const VarDecl *> LockGuards;
+llvm::SmallVector<const VarDecl *> getLockGuardsFromDecl(const DeclStmt *DS) {
+ llvm::SmallVector<const VarDecl *> LockGuards;
for (const Decl *Decl : DS->decls()) {
if (const auto *VD = dyn_cast<VarDecl>(Decl)) {
@@ -60,12 +60,12 @@ std::vector<const VarDecl *> getLockGuardsFromDecl(const DeclStmt *DS) {
// Scans through the statements in a block and groups consecutive
// 'std::lock_guard' variable declarations together.
-std::vector<std::vector<const VarDecl *>>
+llvm::SmallVector<llvm::SmallVector<const VarDecl *>>
findLocksInCompoundStmt(const CompoundStmt *Block,
const ast_matchers::MatchFinder::MatchResult &Result) {
// store groups of consecutive 'std::lock_guard' declarations
- std::vector<std::vector<const VarDecl *>> LockGuardGroups;
- std::vector<const VarDecl *> CurrentLockGuardGroup;
+ llvm::SmallVector<llvm::SmallVector<const VarDecl *>> LockGuardGroups;
+ llvm::SmallVector<const VarDecl *> CurrentLockGuardGroup;
auto AddAndClearCurrentGroup = [&]() {
if (!CurrentLockGuardGroup.empty()) {
@@ -76,21 +76,17 @@ findLocksInCompoundStmt(const CompoundStmt *Block,
for (const Stmt *Stmt : Block->body()) {
if (const auto *DS = dyn_cast<DeclStmt>(Stmt)) {
- std::vector<const VarDecl *> LockGuards = getLockGuardsFromDecl(DS);
+ llvm::SmallVector<const VarDecl *> LockGuards = getLockGuardsFromDecl(DS);
if (!LockGuards.empty()) {
CurrentLockGuardGroup.insert(
CurrentLockGuardGroup.end(),
std::make_move_iterator(LockGuards.begin()),
std::make_move_iterator(LockGuards.end()));
+ continue;
}
-
- if (LockGuards.empty()) {
- AddAndClearCurrentGroup();
- }
- } else {
- AddAndClearCurrentGroup();
}
+ AddAndClearCurrentGroup();
}
AddAndClearCurrentGroup();
@@ -98,43 +94,65 @@ findLocksInCompoundStmt(const CompoundStmt *Block,
return LockGuardGroups;
}
-// Find the exact source range of the 'lock_guard<...>' token
-std::optional<SourceRange> getLockGuardRange(const VarDecl *LockGuard,
- SourceManager &SM) {
- const TypeSourceInfo *SourceInfo = LockGuard->getTypeSourceInfo();
+TemplateSpecializationTypeLoc
+getTemplateSpecializationTypeLoc(const TypeSourceInfo *SourceInfo) {
const TypeLoc Loc = SourceInfo->getTypeLoc();
const auto ElaboratedLoc = Loc.getAs<ElaboratedTypeLoc>();
if (!ElaboratedLoc)
- return std::nullopt;
+ return {};
+
+ return ElaboratedLoc.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>();
+}
- const auto TemplateLoc =
- ElaboratedLoc.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>();
+// Find the exact source range of the 'lock_guard<...>' token
+SourceRange getLockGuardTemplateRange(const TypeSourceInfo *SourceInfo) {
+ const TemplateSpecializationTypeLoc TemplateLoc =
+ getTemplateSpecializationTypeLoc(SourceInfo);
if (!TemplateLoc)
- return std::nullopt;
+ return {};
- const SourceLocation LockGuardBeginLoc = TemplateLoc.getTemplateNameLoc();
- const SourceLocation LockGuardRAngleLoc = TemplateLoc.getRAngleLoc();
+ return SourceRange(TemplateLoc.getTemplateNameLoc(),
+ TemplateLoc.getRAngleLoc());
+}
- return SourceRange(LockGuardBeginLoc, LockGuardRAngleLoc);
+SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) {
+ const TemplateSpecializationTypeLoc TemplateLoc =
+ getTemplateSpecializationTypeLoc(SourceInfo);
+ if (!TemplateLoc)
+ return {};
+
+ return SourceRange(TemplateLoc.getTemplateNameLoc(),
+ TemplateLoc.getLAngleLoc().getLocWithOffset(-1));
}
+const StringRef UseScopedLockMessage =
+ "use 'std::scoped_lock' instead of 'std::lock_guard'";
+
} // namespace
+UseScopedLockCheck::UseScopedLockCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ WarnOnlyOnMultipleLocks(Options.get("WarnOnlyOnMultipleLocks", false)),
+ WarnOnUsingAndTypedef(Options.get("WarnOnUsingAndTypedef", true)) {}
+
void UseScopedLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
- Options.store(Opts, "WarnOnlyMultipleLocks", WarnOnlyMultipleLocks);
+ Options.store(Opts, "WarnOnlyMultipleLocks", WarnOnlyOnMultipleLocks);
}
void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
- auto LockGuardType = qualType(hasDeclaration(namedDecl(
- hasName("::std::lock_guard"),
- anyOf(classTemplateDecl(), classTemplateSpecializationDecl()))));
+ auto LockGuardClassDecl =
+ namedDecl(anyOf(classTemplateDecl(), classTemplateSpecializationDecl()),
+ hasName("::std::lock_guard"));
+ auto LockGuardType = qualType(hasDeclaration(LockGuardClassDecl));
auto LockVarDecl = varDecl(hasType(LockGuardType));
// Match CompoundStmt with only one 'std::lock_guard'
- if (!WarnOnlyMultipleLocks) {
+ if (!WarnOnlyOnMultipleLocks) {
Finder->addMatcher(
- compoundStmt(has(declStmt(has(LockVarDecl.bind("lock-decl-single")))),
+ compoundStmt(unless(isExpansionInSystemHeader()),
+ has(declStmt(has(LockVarDecl.bind("lock-decl-single")))),
unless(hasDescendant(declStmt(has(varDecl(
hasType(LockGuardType),
unless(equalsBoundNode("lock-decl-single")))))))),
@@ -143,50 +161,94 @@ void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
// Match CompoundStmt with multiple 'std::lock_guard'
Finder->addMatcher(
- compoundStmt(has(declStmt(has(LockVarDecl.bind("lock-decl-multiple")))),
+ compoundStmt(unless(isExpansionInSystemHeader()),
+ has(declStmt(has(LockVarDecl.bind("lock-decl-multiple")))),
hasDescendant(declStmt(has(varDecl(
hasType(LockGuardType),
unless(equalsBoundNode("lock-decl-multiple")))))))
.bind("block-multiple"),
this);
+
+ if (WarnOnUsingAndTypedef) {
+ // Match 'typedef std::lock_guard<std::mutex> Lock'
+ Finder->addMatcher(typedefDecl(unless(isExpansionInSystemHeader()),
+ hasUnderlyingType(LockGuardType))
+ .bind("lock-guard-typedef"),
+ this);
+
+ // Match 'using Lock = std::lock_guard<std::mutex>'
+ Finder->addMatcher(
+ typeAliasDecl(
+ unless(isExpansionInSystemHeader()),
+ hasType(elaboratedType(namesType(templateSpecializationType(
+ hasDeclaration(LockGuardClassDecl))))))
+ .bind("lock-guard-using-alias"),
+ this);
+
+ // Match 'using std::lock_guard'
+ Finder->addMatcher(
+ usingDecl(unless(isExpansionInSystemHeader()),
+ hasAnyUsingShadowDecl(hasTargetDecl(
+ namedDecl(hasName("lock_guard"), isInStdNamespace()))))
+ .bind("lock-guard-using-decl"),
+ this);
+ }
}
void UseScopedLockCheck::check(const MatchFinder::MatchResult &Result) {
if (const auto *Decl = Result.Nodes.getNodeAs<VarDecl>("lock-decl-single")) {
emitDiag(Decl, Result);
+ return;
}
if (const auto *Compound =
Result.Nodes.getNodeAs<CompoundStmt>("block-multiple")) {
emitDiag(findLocksInCompoundStmt(Compound, Result), Result);
+ return;
+ }
+
+ if (const auto *Typedef =
+ Result.Nodes.getNodeAs<TypedefDecl>("lock-guard-typedef")) {
+ emitDiag(Typedef->getTypeSourceInfo(), Result);
+ return;
+ }
+
+ if (const auto *UsingAlias =
+ Result.Nodes.getNodeAs<TypeAliasDecl>("lock-guard-using-alias")) {
+ emitDiag(UsingAlias->getTypeSourceInfo(), Result);
+ return;
+ }
+
+ if (const auto *Using =
+ Result.Nodes.getNodeAs<UsingDecl>("lock-guard-using-decl")) {
+ emitDiag(Using, Result);
}
}
void UseScopedLockCheck::emitDiag(const VarDecl *LockGuard,
const MatchFinder::MatchResult &Result) {
- auto Diag = diag(LockGuard->getBeginLoc(),
- "use 'std::scoped_lock' instead of 'std::lock_guard'");
+ auto Diag = diag(LockGuard->getBeginLoc(), UseScopedLockMessage);
- const std::optional<SourceRange> LockGuardTypeRange =
- getLockGuardRange(LockGuard, *Result.SourceManager);
+ const SourceRange LockGuardTypeRange =
+ getLockGuardTemplateRange(LockGuard->getTypeSourceInfo());
- if (!LockGuardTypeRange) {
+ if (LockGuardTypeRange.isInvalid()) {
return;
}
- // Create Fix-its only if we can find the constructor call to handle
- // 'std::lock_guard l(m, std::adopt_lock)' case
+ // Create Fix-its only if we can find the constructor call to properly handle
+ // 'std::lock_guard l(m, std::adopt_lock)' case. Otherwise create fix-notes.
const auto *CtorCall = dyn_cast<CXXConstructExpr>(LockGuard->getInit());
if (!CtorCall) {
return;
}
- switch (CtorCall->getNumArgs()) {
- case 1:
- Diag << FixItHint::CreateReplacement(LockGuardTypeRange.value(),
- "scoped_lock");
+ if (CtorCall->getNumArgs() == 1) {
+ Diag << FixItHint::CreateReplacement(LockGuardTypeRange, "scoped_lock");
return;
- case 2:
+ }
+
+ if (CtorCall->getNumArgs() == 2) {
const Expr *const *CtorArgs = CtorCall->getArgs();
const Expr *MutexArg = CtorArgs[0];
@@ -199,8 +261,7 @@ void UseScopedLockCheck::emitDiag(const VarDecl *LockGuard,
CharSourceRange::getTokenRange(AdoptLockArg->getSourceRange()),
*Result.SourceManager, Result.Context->getLangOpts());
- Diag << FixItHint::CreateReplacement(LockGuardTypeRange.value(),
- "scoped_lock")
+ Diag << FixItHint::CreateReplacement(LockGuardTypeRange, "scoped_lock")
<< FixItHint::CreateReplacement(
SourceRange(MutexArg->getBeginLoc(), AdoptLockArg->getEndLoc()),
(llvm::Twine(AdoptLockSourceText) + ", " + MutexSourceText)
@@ -212,22 +273,46 @@ void UseScopedLockCheck::emitDiag(const VarDecl *LockGuard,
}
void UseScopedLockCheck::emitDiag(
- const std::vector<std::vector<const VarDecl *>> &LockGuardGroups,
- const MatchFinder::MatchResult &Result) {
- for (const std::vector<const VarDecl *> &Group : LockGuardGroups) {
- if (Group.size() == 1 && !WarnOnlyMultipleLocks) {
+ const llvm::SmallVector<llvm::SmallVector<const VarDecl *>> &LockGroups,
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ for (const llvm::SmallVector<const VarDecl *> &Group : LockGroups) {
+ if (Group.size() == 1 && !WarnOnlyOnMultipleLocks) {
emitDiag(Group[0], Result);
} else {
diag(Group[0]->getBeginLoc(),
"use single 'std::scoped_lock' instead of multiple "
"'std::lock_guard'");
- for (size_t I = 1; I < Group.size(); ++I) {
- diag(Group[I]->getLocation(),
- "additional 'std::lock_guard' declared here", DiagnosticIDs::Note);
+ for (const VarDecl *Lock : llvm::drop_begin(Group)) {
+ diag(Lock->getLocation(), "additional 'std::lock_guard' declared here",
+ DiagnosticIDs::Note);
}
}
}
}
+void UseScopedLockCheck::emitDiag(
+ const TypeSourceInfo *LockGuardSourceInfo,
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ const TypeLoc TL = LockGuardSourceInfo->getTypeLoc();
+
+ if (const auto ElaboratedTL = TL.getAs<ElaboratedTypeLoc>()) {
+ auto Diag = diag(ElaboratedTL.getBeginLoc(), UseScopedLockMessage);
+
+ const SourceRange LockGuardRange = getLockGuardRange(LockGuardSourceInfo);
+ if (LockGuardRange.isInvalid()) {
+ return;
+ }
+
+ Diag << FixItHint::CreateReplacement(LockGuardRange, "scoped_lock");
+ }
+}
+
+void UseScopedLockCheck::emitDiag(
+ const UsingDecl *UsingDecl,
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ diag(UsingDecl->getLocation(), UseScopedLockMessage)
+ << FixItHint::CreateReplacement(UsingDecl->getLocation(), "scoped_lock");
+}
+
} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
index 0e1fd8067ddd1..0091939eaef3a 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
@@ -17,15 +17,13 @@
namespace clang::tidy::modernize {
/// Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's
-/// more flexible and safer alternative ``std::scoped_lock``.
+/// alternative ``std::scoped_lock``.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-scoped-lock.html
class UseScopedLockCheck : public ClangTidyCheck {
public:
- UseScopedLockCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context),
- WarnOnlyMultipleLocks(Options.get("WarnOnlyMultipleLocks", false)) {}
+ UseScopedLockCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
@@ -39,10 +37,16 @@ class UseScopedLockCheck : public ClangTidyCheck {
private:
void emitDiag(const VarDecl *LockGuard,
const ast_matchers::MatchFinder::MatchResult &Result);
- void emitDiag(const std::vector<std::vector<const VarDecl *>> &LockGroups,
+ void emitDiag(
+ const llvm::SmallVector<llvm::SmallVector<const VarDecl *>> &LockGroups,
+ const ast_matchers::MatchFinder::MatchResult &Result);
+ void emitDiag(const TypeSourceInfo *LockGuardSourceInfo,
+ const ast_matchers::MatchFinder::MatchResult &Result);
+ void emitDiag(const UsingDecl *UsingDecl,
const ast_matchers::MatchFinder::MatchResult &Result);
- const bool WarnOnlyMultipleLocks;
+ const bool WarnOnlyOnMultipleLocks;
+ const bool WarnOnUsingAndTypedef;
};
} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 6aefbc4737276..192d185fb9677 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -91,11 +91,11 @@ Improvements to clang-tidy
New checks
^^^^^^^^^^
-- New :doc:`use-scoped-lock
+- New :doc:`modernize-use-scoped-lock
<clang-tidy/checks/modernize/use-scoped-lock>` check.
Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's
- more flexible and safer alternative ``std::scoped_lock``.
+ alternative ``std::scoped_lock``.
New check aliases
^^^^^^^^^^^^^^^^^
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
index 04b35d0081b3c..cb0a0737a7f02 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
@@ -3,11 +3,13 @@
modernize-use-scoped-lock
=========================
-Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's more
-flexible and safer alternative ``std::scoped_lock``. The check will
-automatically transform only single declarations of ``std::lock_guard`` and
-emit warnings for multiple declarations of ``std::lock_guard`` that can be
-replaced with a single declaration of ``std::scoped_lock``.
+Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's
+alternative ``std::scoped_lock``. ``std::scoped_lock`` is a superior version
+of ``std::lock_guard`` that can lock multiple mutexes at once with
+deadlock-avoidance algorithm. The check will automatically transform only
+single declarations of ``std::lock_guard`` and emit warnings for multiple
+declarations of ``std::lock_guard`` that can be replaced with a single
+declaration of ``std::scoped_lock``.
Examples
--------
@@ -24,7 +26,7 @@ Transforms to:
.. code-block:: c++
- std::mutex m;
+ std::mutex M;
std::scoped_lock L(M);
Single ``std::lock_guard`` declaration with ``std::adopt_lock``:
@@ -58,24 +60,44 @@ Limitations
-----------
The check will not emit warnings if ``std::lock_guard`` is used implicitly via
-``using``, ``typedef`` or ``template``.
+``using``, ``typedef`` or ``template``:
-.. code-block:: c
+.. code-block:: c++
template <template <typename> typename Lock>
void TemplatedLock() {
- std::mutex m;
- Lock<std::mutex> l(m); // no warning
+ std::mutex M;
+ Lock<std::mutex> L(M); // no warning
}
void UsingLock() {
using Lock = std::lock_guard<std::mutex>;
- std::mutex m;
- Lock l(m); // no warning
+ std::mutex M;
+ Lock L(M); // no warning
}
-.. option:: WarnOnlyMultipleLocks
+
+Options
+-------
+
+.. option:: WarnOnlyOnMultipleLocks
When `true`, the check will only emit warnings if the there are multiple
consecutive ``std::lock_guard`` declarations that can be replaced with a
single ``std::scoped_lock`` declaration. Default is `false`.
+
+.. option:: WarnOnUsingAndTypedef
+
+ When `true`, the check will emit warnings if ``std::lock_guard`` is used
+ in ``using`` or ``typedef`` declarations. Default is `true`.
+
+ .. code-block:: c++
+
+ template <typename T>
+ using Lock = std::lock_guard<T>; // warning
+
+ using LockMutex = std::lock_guard<std::mutex>; // warning
+
+ typedef std::lock_guard<std::mutex> LockDef; // warning
+
+ using std::lock_guard; // warning
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scope-lock-warn-on-using-and-typedef-false.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scope-lock-warn-on-using-and-typedef-false.cpp
new file mode 100644
index 0000000000000..ffa9445ffc580
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scope-lock-warn-on-using-and-typedef-false.cpp
@@ -0,0 +1,57 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t -- \
+// RUN: -config="{CheckOptions: {modernize-use-scoped-lock.WarnOnUsingAndTypedef: false}}" \
+// RUN: -- -fno-delayed-template-parsing
+
+namespace std {
+
+struct mutex {
+ void lock() {}
+ void unlock() {}
+};
+
+template<class Lockable1, class Lockable2, class... LockableN >
+void lock(Lockable1& lock1, Lockable2& lock2, LockableN&... lockn );
+
+struct adopt_lock_t { };
+std::adopt_lock_t adopt_lock {};
+
+template <typename Mutex>
+struct lock_guard {
+ lock_guard(Mutex &m) { }
+ lock_guard(Mutex &m, std::adopt_lock_t t) {}
+ lock_guard( const lock_guard& ) = delete;
+};
+
+template <typename... MutexTypes>
+struct scoped_lock {
+ scoped_lock(MutexTypes&... m) {}
+ scoped_lock(std::adopt_lock_t t, MutexTypes&... m) {}
+};
+
+} // namespace std
+
+template <typename T>
+using Lock = std::lock_guard<T>;
+
+using LockM = std::lock_guard<std::mutex>;
+
+typedef std::lock_guard<std::mutex> LockDef;
+
+void PositiveUsingDecl() {
+ using std::lock_guard;
+
+ using LockMFun = std::lock_guard<std::mutex>;
+
+ typedef std::lock_guard<std::mutex> LockDefFun;
+}
+
+template <typename T>
+void PositiveUsingDeclTemplate() {
+ using std::lock_guard;
+
+ using LockFunT = std::lock_guard<T>;
+
+ using LockMFunT = std::lock_guard<std::mutex>;
+
+ typedef std::lock_guard<std::mutex> LockDefFunT;
+}
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
index 2948aaed4f7c5..bf92d4498b28e 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-only-warn-on-multiple.cpp
@@ -1,5 +1,5 @@
// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t -- \
-// RUN: -config="{CheckOptions: {modernize-use-scoped-lock.WarnOnlyMultipleLocks: true}}" \
+// RUN: -config="{CheckOptions: {modernize-use-scoped-lock.WarnOnlyOnMultipleLocks: true}}" \
// RUN: -- -fno-delayed-template-parsing
namespace std {
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
index d7193b999a483..32305f64c97d0 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
@@ -313,8 +313,50 @@ struct PositiveTemplatedClass {
template <typename T>
using Lock = std::lock_guard<T>;
+// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+// CHECK-FIXES: using Lock = std::scoped_lock<T>;
+
using LockM = std::lock_guard<std::mutex>;
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+// CHECK-FIXES: using LockM = std::scoped_lock<std::mutex>;
+
typedef std::lock_guard<std::mutex> LockDef;
+// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+// CHECK-FIXES: typedef std::scoped_lock<std::mutex> LockDef;
+
+
+void PositiveUsingDecl() {
+ using std::lock_guard;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: using std::scoped_lock;
+
+ using LockMFun = std::lock_guard<std::mutex>;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: using LockMFun = std::scoped_lock<std::mutex>;
+
+ typedef std::lock_guard<std::mutex> LockDefFun;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: typedef std::scoped_lock<std::mutex> LockDefFun;
+}
+
+template <typename T>
+void PositiveUsingDeclTemplate() {
+ using std::lock_guard;
+ // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: using std::scoped_lock;
+
+ using LockFunT = std::lock_guard<T>;
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: using LockFunT = std::scoped_lock<T>;
+
+ using LockMFunT = std::lock_guard<std::mutex>;
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: using LockMFunT = std::scoped_lock<std::mutex>;
+
+ typedef std::lock_guard<std::mutex> LockDefFunT;
+ // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: typedef std::scoped_lock<std::mutex> LockDefFunT;
+}
void NegativeUsingTypedefs() {
std::mutex m;
@@ -331,3 +373,22 @@ void NegativeUsingTypedefs() {
LockDef l(m);
}
}
+
+// Non-standard lock_guard.
+template <typename Mutex>
+struct lock_guard {
+ lock_guard(Mutex &m) { }
+ lock_guard(const lock_guard& ) = delete;
+};
+
+void NegativeNonStdLockGuard() {
+ std::mutex m;
+ {
+ lock_guard<std::mutex> l(m);
+ }
+
+ {
+ lock_guard<std::mutex> l1(m);
+ lock_guard<std::mutex> l2(m);
+ }
+}
\ No newline at end of file
>From a09f14b5d598d85833e6379249b1556da13a1091 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Thu, 13 Feb 2025 01:39:38 +0300
Subject: [PATCH 6/7] [clang-tidy] small fixes to use-scoped-lock.rst
---
.../docs/clang-tidy/checks/modernize/use-scoped-lock.rst | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
index cb0a0737a7f02..0563d18e54d04 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
@@ -4,9 +4,7 @@ modernize-use-scoped-lock
=========================
Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's
-alternative ``std::scoped_lock``. ``std::scoped_lock`` is a superior version
-of ``std::lock_guard`` that can lock multiple mutexes at once with
-deadlock-avoidance algorithm. The check will automatically transform only
+alternative ``std::scoped_lock``. The check will automatically transform only
single declarations of ``std::lock_guard`` and emit warnings for multiple
declarations of ``std::lock_guard`` that can be replaced with a single
declaration of ``std::scoped_lock``.
>From a4413911665143fcbea9c7c401dcb909a3c983c9 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Fri, 14 Feb 2025 10:38:33 +0300
Subject: [PATCH 7/7] [clang-tidy] small improvements
---
.../clang-tidy/modernize/UseScopedLockCheck.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
index ecde9f24d4d20..f69db50be0439 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
@@ -95,7 +95,7 @@ findLocksInCompoundStmt(const CompoundStmt *Block,
}
TemplateSpecializationTypeLoc
-getTemplateSpecializationTypeLoc(const TypeSourceInfo *SourceInfo) {
+getTemplateLockGuardTypeLoc(const TypeSourceInfo *SourceInfo) {
const TypeLoc Loc = SourceInfo->getTypeLoc();
const auto ElaboratedLoc = Loc.getAs<ElaboratedTypeLoc>();
@@ -108,7 +108,7 @@ getTemplateSpecializationTypeLoc(const TypeSourceInfo *SourceInfo) {
// Find the exact source range of the 'lock_guard<...>' token
SourceRange getLockGuardTemplateRange(const TypeSourceInfo *SourceInfo) {
const TemplateSpecializationTypeLoc TemplateLoc =
- getTemplateSpecializationTypeLoc(SourceInfo);
+ getTemplateLockGuardTypeLoc(SourceInfo);
if (!TemplateLoc)
return {};
@@ -116,9 +116,10 @@ SourceRange getLockGuardTemplateRange(const TypeSourceInfo *SourceInfo) {
TemplateLoc.getRAngleLoc());
}
+// Find the exact source range of the 'lock_guard' token
SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) {
const TemplateSpecializationTypeLoc TemplateLoc =
- getTemplateSpecializationTypeLoc(SourceInfo);
+ getTemplateLockGuardTypeLoc(SourceInfo);
if (!TemplateLoc)
return {};
@@ -144,7 +145,7 @@ void UseScopedLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
auto LockGuardClassDecl =
namedDecl(anyOf(classTemplateDecl(), classTemplateSpecializationDecl()),
- hasName("::std::lock_guard"));
+ hasName("lock_guard"), isInStdNamespace());
auto LockGuardType = qualType(hasDeclaration(LockGuardClassDecl));
auto LockVarDecl = varDecl(hasType(LockGuardType));
@@ -188,8 +189,7 @@ void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
// Match 'using std::lock_guard'
Finder->addMatcher(
usingDecl(unless(isExpansionInSystemHeader()),
- hasAnyUsingShadowDecl(hasTargetDecl(
- namedDecl(hasName("lock_guard"), isInStdNamespace()))))
+ hasAnyUsingShadowDecl(hasTargetDecl(LockGuardClassDecl)))
.bind("lock-guard-using-decl"),
this);
}
More information about the cfe-commits
mailing list