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.2...@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.2...@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.2...@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.2...@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.2...@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.2...@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.2...@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); } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits