[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.
This commit is contained in:
@@ -42,6 +42,7 @@ add_clang_library(clangTidyModernizeModule STATIC
|
||||
UseNullptrCheck.cpp
|
||||
UseOverrideCheck.cpp
|
||||
UseRangesCheck.cpp
|
||||
UseScopedLockCheck.cpp
|
||||
UseStartsEndsWithCheck.cpp
|
||||
UseStdFormatCheck.cpp
|
||||
UseStdNumbersCheck.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 @@ public:
|
||||
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");
|
||||
|
||||
311
clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
Normal file
311
clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.cpp
Normal file
@@ -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
|
||||
54
clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
Normal file
54
clang-tools-extra/clang-tidy/modernize/UseScopedLockCheck.h
Normal file
@@ -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
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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'
|
||||
@@ -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_
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user