[clang-tools-extra] a3a60e0 - [clang-tidy] add new check: modernize-use-scoped-lock (#126434)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jun 29 12:34:36 PDT 2025
Author: Baranov Victor
Date: 2025-06-29T22:34:32+03:00
New Revision: a3a60e03e2bf7b79683517584a9a7b3e4c8cd297
URL: https://github.com/llvm/llvm-project/commit/a3a60e03e2bf7b79683517584a9a7b3e4c8cd297
DIFF: https://github.com/llvm/llvm-project/commit/a3a60e03e2bf7b79683517584a9a7b3e4c8cd297.diff
LOG: [clang-tidy] add new check: modernize-use-scoped-lock (#126434)
Add new clang-tidy check that finds uses of `std::lock_guard` and suggests
replacing them with C++17's more flexible and safer alternative
`std::scoped_lock`.
Here is a small description of how it works for better understanding of
the code:
Two separate AST matchers are registered:
- The first one matches declarations of `std::lock_guard` that are
single in their scope (only one `std::lock_guard` in `CompoundStmt`).
It's an easy case, we can emit warning right away.
- The second one matches `CompoundStmt`'s that have multiple
`std::lock_guard` declarations, which means that we may have consecutive
declarations of `std::lock_guard` that can be replaced by a single
`std::scoped_lock`. In order to ensure that declarations are
consecutive, we need to loop over `Stmt`'s in `CompoundStmt`. Here is a
small example:
```cpp
{
std::mutex m1, m2;
std::lock(m1, m2);
std::lock_guard<std::mutex> l1(m, std::adopt_lock); // first declaration of 'std::lock_guard'
std::lock_guard<std::mutex> l2(m, std::adopt_lock); // second declaration of 'std::lock_guard' that can be merged with first using 'scoped_lock'
}
```
This PR closes https://github.com/llvm/llvm-project/issues/107839.
Added:
clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/mutex
clang-tools-extra/test/clang-tidy/checkers/modernize/use-scope-lock-warn-on-using-and-typedef-false.cpp
clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-warn-on-single-locks-false.cpp
clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
Modified:
clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
Removed:
################################################################################
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 0cf59b6e0216a..fdf38bc4b6308 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..9c2fc9e06fb45
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
@@ -0,0 +1,311 @@
+//===--- 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/SmallVector.h"
+#include "llvm/ADT/Twine.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+static bool isLockGuardDecl(const NamedDecl *Decl) {
+ return Decl->getDeclName().isIdentifier() &&
+ Decl->getName() == "lock_guard" && Decl->isInStdNamespace();
+}
+
+static bool isLockGuard(const QualType &Type) {
+ if (const auto *Record = Type->getAs<RecordType>())
+ if (const RecordDecl *Decl = Record->getDecl())
+ return isLockGuardDecl(Decl);
+
+ if (const auto *TemplateSpecType = Type->getAs<TemplateSpecializationType>())
+ if (const TemplateDecl *Decl =
+ TemplateSpecType->getTemplateName().getAsTemplateDecl())
+ return isLockGuardDecl(Decl);
+
+ return false;
+}
+
+static 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)) {
+ const QualType Type =
+ VD->getType().getCanonicalType().getUnqualifiedType();
+ 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.
+static llvm::SmallVector<llvm::SmallVector<const VarDecl *>>
+findLocksInCompoundStmt(const CompoundStmt *Block,
+ const ast_matchers::MatchFinder::MatchResult &Result) {
+ // store groups of consecutive 'std::lock_guard' declarations
+ llvm::SmallVector<llvm::SmallVector<const VarDecl *>> LockGuardGroups;
+ llvm::SmallVector<const VarDecl *> CurrentLockGuardGroup;
+
+ auto AddAndClearCurrentGroup = [&]() {
+ if (!CurrentLockGuardGroup.empty()) {
+ LockGuardGroups.push_back(CurrentLockGuardGroup);
+ CurrentLockGuardGroup.clear();
+ }
+ };
+
+ for (const Stmt *Stmt : Block->body()) {
+ if (const auto *DS = dyn_cast<DeclStmt>(Stmt)) {
+ llvm::SmallVector<const VarDecl *> LockGuards = getLockGuardsFromDecl(DS);
+
+ if (!LockGuards.empty()) {
+ CurrentLockGuardGroup.append(LockGuards);
+ continue;
+ }
+ }
+ AddAndClearCurrentGroup();
+ }
+
+ AddAndClearCurrentGroup();
+
+ return LockGuardGroups;
+}
+
+static TemplateSpecializationTypeLoc
+getTemplateLockGuardTypeLoc(const TypeSourceInfo *SourceInfo) {
+ const TypeLoc Loc = SourceInfo->getTypeLoc();
+
+ const auto ElaboratedLoc = Loc.getAs<ElaboratedTypeLoc>();
+ if (!ElaboratedLoc)
+ return {};
+
+ return ElaboratedLoc.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>();
+}
+
+// Find the exact source range of the 'lock_guard' token
+static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) {
+ const TypeLoc LockGuardTypeLoc = SourceInfo->getTypeLoc();
+
+ return SourceRange(LockGuardTypeLoc.getBeginLoc(),
+ LockGuardTypeLoc.getEndLoc());
+}
+
+// Find the exact source range of the 'lock_guard' name token
+static SourceRange getLockGuardNameRange(const TypeSourceInfo *SourceInfo) {
+ const TemplateSpecializationTypeLoc TemplateLoc =
+ getTemplateLockGuardTypeLoc(SourceInfo);
+ if (!TemplateLoc)
+ return {};
+
+ return SourceRange(TemplateLoc.getTemplateNameLoc(),
+ TemplateLoc.getLAngleLoc().getLocWithOffset(-1));
+}
+
+const static StringRef UseScopedLockMessage =
+ "use 'std::scoped_lock' instead of 'std::lock_guard'";
+
+UseScopedLockCheck::UseScopedLockCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ WarnOnSingleLocks(Options.get("WarnOnSingleLocks", true)),
+ WarnOnUsingAndTypedef(Options.get("WarnOnUsingAndTypedef", true)) {}
+
+void UseScopedLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "WarnOnSingleLocks", WarnOnSingleLocks);
+ Options.store(Opts, "WarnOnUsingAndTypedef", WarnOnUsingAndTypedef);
+}
+
+void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
+ const auto LockGuardClassDecl =
+ namedDecl(hasName("lock_guard"), isInStdNamespace());
+
+ const auto LockGuardType = qualType(anyOf(
+ hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(LockGuardClassDecl))),
+ elaboratedType(namesType(hasUnqualifiedDesugaredType(
+ templateSpecializationType(hasDeclaration(LockGuardClassDecl)))))));
+
+ const auto LockVarDecl = varDecl(hasType(LockGuardType));
+
+ if (WarnOnSingleLocks) {
+ Finder->addMatcher(
+ compoundStmt(
+ unless(isExpansionInSystemHeader()),
+ has(declStmt(has(LockVarDecl)).bind("lock-decl-single")),
+ unless(has(declStmt(unless(equalsBoundNode("lock-decl-single")),
+ has(LockVarDecl))))),
+ this);
+ }
+
+ Finder->addMatcher(
+ compoundStmt(unless(isExpansionInSystemHeader()),
+ has(declStmt(has(LockVarDecl)).bind("lock-decl-multiple")),
+ has(declStmt(unless(equalsBoundNode("lock-decl-multiple")),
+ has(LockVarDecl))))
+ .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(LockGuardClassDecl)))
+ .bind("lock-guard-using-decl"),
+ this);
+ }
+}
+
+void UseScopedLockCheck::check(const MatchFinder::MatchResult &Result) {
+ if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("lock-decl-single")) {
+ llvm::SmallVector<const VarDecl *> Decls = getLockGuardsFromDecl(DS);
+ diagOnMultipleLocks({Decls}, Result);
+ return;
+ }
+
+ if (const auto *Compound =
+ Result.Nodes.getNodeAs<CompoundStmt>("block-multiple")) {
+ diagOnMultipleLocks(findLocksInCompoundStmt(Compound, Result), Result);
+ return;
+ }
+
+ if (const auto *Typedef =
+ Result.Nodes.getNodeAs<TypedefDecl>("lock-guard-typedef")) {
+ diagOnSourceInfo(Typedef->getTypeSourceInfo(), Result);
+ return;
+ }
+
+ if (const auto *UsingAlias =
+ Result.Nodes.getNodeAs<TypeAliasDecl>("lock-guard-using-alias")) {
+ diagOnSourceInfo(UsingAlias->getTypeSourceInfo(), Result);
+ return;
+ }
+
+ if (const auto *Using =
+ Result.Nodes.getNodeAs<UsingDecl>("lock-guard-using-decl")) {
+ diagOnUsingDecl(Using, Result);
+ }
+}
+
+void UseScopedLockCheck::diagOnSingleLock(
+ const VarDecl *LockGuard, const MatchFinder::MatchResult &Result) {
+ auto Diag = diag(LockGuard->getBeginLoc(), UseScopedLockMessage);
+
+ const SourceRange LockGuardTypeRange =
+ getLockGuardRange(LockGuard->getTypeSourceInfo());
+
+ if (LockGuardTypeRange.isInvalid())
+ return;
+
+ // Create Fix-its only if we can find the constructor call to properly handle
+ // 'std::lock_guard l(m, std::adopt_lock)' case.
+ const auto *CtorCall = dyn_cast<CXXConstructExpr>(LockGuard->getInit());
+ if (!CtorCall)
+ return;
+
+ if (CtorCall->getNumArgs() == 1) {
+ Diag << FixItHint::CreateReplacement(LockGuardTypeRange,
+ "std::scoped_lock");
+ return;
+ }
+
+ if (CtorCall->getNumArgs() == 2) {
+ const Expr *const *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, "std::scoped_lock")
+ << FixItHint::CreateReplacement(
+ SourceRange(MutexArg->getBeginLoc(), AdoptLockArg->getEndLoc()),
+ (llvm::Twine(AdoptLockSourceText) + ", " + MutexSourceText)
+ .str());
+ return;
+ }
+
+ llvm_unreachable("Invalid argument number of std::lock_guard constructor");
+}
+
+void UseScopedLockCheck::diagOnMultipleLocks(
+ 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) {
+ if (WarnOnSingleLocks)
+ diagOnSingleLock(Group[0], Result);
+ } else {
+ diag(Group[0]->getBeginLoc(),
+ "use single 'std::scoped_lock' instead of multiple "
+ "'std::lock_guard'");
+
+ for (const VarDecl *Lock : llvm::drop_begin(Group))
+ diag(Lock->getLocation(), "additional 'std::lock_guard' declared here",
+ DiagnosticIDs::Note);
+ }
+ }
+}
+
+void UseScopedLockCheck::diagOnSourceInfo(
+ 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 =
+ getLockGuardNameRange(LockGuardSourceInfo);
+ if (LockGuardRange.isInvalid())
+ return;
+
+ Diag << FixItHint::CreateReplacement(LockGuardRange, "scoped_lock");
+ }
+}
+
+void UseScopedLockCheck::diagOnUsingDecl(
+ 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
new file mode 100644
index 0000000000000..a5697805c15ca
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
@@ -0,0 +1,54 @@
+//===--- 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
+/// 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);
+ 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 diagOnSingleLock(const VarDecl *LockGuard,
+ const ast_matchers::MatchFinder::MatchResult &Result);
+ void diagOnMultipleLocks(
+ const llvm::SmallVector<llvm::SmallVector<const VarDecl *>> &LockGroups,
+ const ast_matchers::MatchFinder::MatchResult &Result);
+ void diagOnSourceInfo(const TypeSourceInfo *LockGuardSourceInfo,
+ const ast_matchers::MatchFinder::MatchResult &Result);
+ void diagOnUsingDecl(const UsingDecl *UsingDecl,
+ const ast_matchers::MatchFinder::MatchResult &Result);
+
+ const bool WarnOnSingleLocks;
+ const bool WarnOnUsingAndTypedef;
+};
+
+} // 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 ccd6aa239c1cf..e50d40b76e8c4 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -142,6 +142,12 @@ New checks
Finds unscoped (non-class) ``enum`` declarations and suggests using
``enum class`` instead.
+- 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
+ alternative ``std::scoped_lock``.
+
- New :doc:`portability-avoid-pragma-once
<clang-tidy/checks/portability/avoid-pragma-once>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index ccb78ee45e9c4..5098582d0c42b 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -312,6 +312,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..d184f1aefd806
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-scoped-lock.rst
@@ -0,0 +1,101 @@
+.. 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
+alternative ``std::scoped_lock``.
+
+Fix-its are provided for single declarations of ``std::lock_guard`` and warning
+is emitted 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
+``template`` parameter:
+
+.. code-block:: c++
+
+ template <template <typename> typename Lock>
+ void TemplatedLock() {
+ std::mutex M;
+ Lock<std::mutex> L(M); // no warning
+ }
+
+ void instantiate() {
+ TemplatedLock<std::lock_guard>();
+ }
+
+
+Options
+-------
+
+.. option:: WarnOnSingleLocks
+
+ When `true`, the check will warn on single ``std::lock_guard`` declarations.
+ Set this option to `false` if you want to get warnings only on multiple
+ ``std::lock_guard`` declarations that can be replaced with a single
+ ``std::scoped_lock``. Default is `true`.
+
+.. option:: WarnOnUsingAndTypedef
+
+ When `true`, the check will emit warnings if ``std::lock_guard`` is used
+ in ``using`` or ``typedef`` context. Default is `true`.
+
+ .. code-block:: c++
+
+ template <typename T>
+ using Lock = std::lock_guard<T>; // warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+
+ using LockMutex = std::lock_guard<std::mutex>; // warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+
+ typedef std::lock_guard<std::mutex> LockDef; // warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+
+ using std::lock_guard; // warning: use 'std::scoped_lock' instead of 'std::lock_guard'
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/mutex b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/mutex
new file mode 100644
index 0000000000000..a0030a2b9da0a
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/mutex
@@ -0,0 +1,33 @@
+#ifndef _MUTEX_
+#define _MUTEX_
+
+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) {}
+ scoped_lock(const scoped_lock&) = delete;
+};
+
+} // namespace std
+
+#endif // _MUTEX_
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..77df845756530
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scope-lock-warn-on-using-and-typedef-false.cpp
@@ -0,0 +1,31 @@
+// 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: -- -isystem %clang_tidy_headers -fno-delayed-template-parsing
+
+#include <mutex>
+
+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;
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-warn-on-single-locks-false.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-warn-on-single-locks-false.cpp
new file mode 100644
index 0000000000000..7b30cdd75c801
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock-warn-on-single-locks-false.cpp
@@ -0,0 +1,102 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t -- \
+// RUN: -config="{CheckOptions: {modernize-use-scoped-lock.WarnOnSingleLocks: false}}" \
+// RUN: -- -isystem %clang_tidy_headers -fno-delayed-template-parsing
+
+#include <mutex>
+
+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);
+ }
+
+ {
+ std::lock_guard<std::mutex> l3(m);
+ int a = 0;
+ std::lock_guard<std::mutex> l4(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..45eabdbece68c
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-scoped-lock.cpp
@@ -0,0 +1,471 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-scoped-lock %t -- -- -isystem %clang_tidy_headers -fno-delayed-template-parsing
+
+#include <mutex>
+
+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);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-2]]:40: 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);
+ }
+}
+
+
+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);
+}
+
+
+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);
+ }
+}
+
+void PositiveLambda() {
+ std::mutex m;
+ std::lock_guard<std::mutex> l1(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l1(m);
+ auto lambda1 = [&]() {
+ std::lock_guard<std::mutex> l1(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l1(m);
+ };
+
+ std::lock_guard<std::mutex> l3(m);
+ std::lock_guard<std::mutex> l4(m);
+ // 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
+ auto lamda2 = [&]() {
+ std::lock_guard<std::mutex> 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-2]]:33: note: additional 'std::lock_guard' declared here
+ };
+
+ auto lamda3 = [&]() {
+ 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);
+ };
+
+ auto lamda4 = [&]() {
+ std::lock_guard<std::mutex> l1(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l1(m);
+ int a = 0;
+ std::lock_guard<std::mutex> l2(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l2(m);
+ };
+}
+
+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>;
+// 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;
+
+ std::mutex m;
+ lock_guard<std::mutex> l(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(m);
+
+ 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 PositiveInUsingTypedefs() {
+ std::mutex m;
+
+ {
+ Lock<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);
+ }
+
+ {
+ LockM l(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l(m);
+ }
+
+ {
+ LockDef 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(m, m);
+ Lock<std::mutex> l1(m, std::adopt_lock);
+ LockM l2(m, std::adopt_lock);
+ LockDef l3(m), l4(m);
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-3]]:11: note: additional 'std::lock_guard' declared here
+ // CHECK-MESSAGES: :[[@LINE-3]]:13: note: additional 'std::lock_guard' declared here
+ // CHECK-MESSAGES: :[[@LINE-4]]:20: note: additional 'std::lock_guard' declared here
+ int a = 0;
+ LockDef l5(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ // CHECK-FIXES: std::scoped_lock l5(m);
+ }
+}
+
+template <typename Mutex>
+void PositiveInUsingTypedefsTemplated() {
+ Mutex m;
+
+ {
+ Lock<Mutex> l(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ }
+
+ {
+ std::lock(m, m);
+ Lock<Mutex> l1(m, std::adopt_lock);
+ LockM l2(m, std::adopt_lock);
+ LockDef l3(m), l4(m);
+ // CHECK-MESSAGES: :[[@LINE-3]]:5: warning: use single 'std::scoped_lock' instead of multiple 'std::lock_guard'
+ // CHECK-MESSAGES: :[[@LINE-3]]:11: note: additional 'std::lock_guard' declared here
+ // CHECK-MESSAGES: :[[@LINE-3]]:13: note: additional 'std::lock_guard' declared here
+ // CHECK-MESSAGES: :[[@LINE-4]]:20: note: additional 'std::lock_guard' declared here
+ int a = 0;
+ LockDef l5(m);
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use 'std::scoped_lock' instead of 'std::lock_guard'
+ }
+}
+
+// Non-STD 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);
+ }
+}
More information about the cfe-commits
mailing list