================ @@ -0,0 +1,319 @@ +//===--- 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" +#include <iterator> + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +namespace { + +bool isLockGuard(const QualType &Type) { + if (const auto *Record = Type->getAs<RecordType>()) { + if (const RecordDecl *Decl = Record->getDecl()) { + return Decl->getName() == "lock_guard" && Decl->isInStdNamespace(); + } + } + + if (const auto *TemplateSpecType = + Type->getAs<TemplateSpecializationType>()) { + if (const TemplateDecl *Decl = + TemplateSpecType->getTemplateName().getAsTemplateDecl()) { + return Decl->getName() == "lock_guard" && Decl->isInStdNamespace(); + } + } + + return false; +} + +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(); + 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. +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.insert( + CurrentLockGuardGroup.end(), + std::make_move_iterator(LockGuards.begin()), + std::make_move_iterator(LockGuards.end())); + continue; + } + } + AddAndClearCurrentGroup(); + } + + AddAndClearCurrentGroup(); + + return LockGuardGroups; +} + +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 +SourceRange getLockGuardTemplateRange(const TypeSourceInfo *SourceInfo) { + const TemplateSpecializationTypeLoc TemplateLoc = + getTemplateLockGuardTypeLoc(SourceInfo); + if (!TemplateLoc) + return {}; + + return SourceRange(TemplateLoc.getTemplateNameLoc(), + TemplateLoc.getRAngleLoc()); +} + +// Find the exact source range of the 'lock_guard' token +SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) { + const TemplateSpecializationTypeLoc TemplateLoc = + getTemplateLockGuardTypeLoc(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), + 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) { + auto LockGuardClassDecl = + namedDecl(anyOf(classTemplateDecl(), classTemplateSpecializationDecl()), + hasName("lock_guard"), isInStdNamespace()); + auto LockGuardType = qualType(hasDeclaration(LockGuardClassDecl)); + auto LockVarDecl = varDecl(hasType(LockGuardType)); + + if (WarnOnSingleLocks) { + // Match CompoundStmt with only one 'std::lock_guard' + Finder->addMatcher( + compoundStmt(unless(isExpansionInSystemHeader()), + 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(unless(isExpansionInSystemHeader()), + has(declStmt(has(LockVarDecl.bind("lock-decl-multiple")))), + hasDescendant(declStmt(has(varDecl( + hasType(LockGuardType), + unless(equalsBoundNode("lock-decl-multiple"))))))) + .bind("block-multiple"), ---------------- vbvictor wrote:
I added your example to _use-scoped-lock-warn-on-single-locks-false.cpp_ test file. In function `Negative()` does not have diag because `WarnOnSingleLocks` set to false. https://github.com/llvm/llvm-project/pull/126434 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits