10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Decl.h"
12#include "clang/AST/Stmt.h"
13#include "clang/AST/Type.h"
14#include "clang/ASTMatchers/ASTMatchFinder.h"
15#include "clang/ASTMatchers/ASTMatchers.h"
16#include "clang/Basic/SourceLocation.h"
17#include "clang/Lex/Lexer.h"
18#include "llvm/ADT/SmallVector.h"
19#include "llvm/ADT/Twine.h"
26 return Decl->getDeclName().isIdentifier() &&
27 Decl->getName() ==
"lock_guard" && Decl->isInStdNamespace();
31 if (
const auto *Record = Type->getAsCanonical<RecordType>())
32 if (
const RecordDecl *Decl = Record->getDecl())
35 if (
const auto *TemplateSpecType = Type->getAs<TemplateSpecializationType>())
36 if (
const TemplateDecl *Decl =
37 TemplateSpecType->getTemplateName().getAsTemplateDecl())
43static llvm::SmallVector<const VarDecl *>
45 llvm::SmallVector<const VarDecl *> LockGuards;
47 for (
const Decl *Decl : DS->decls()) {
48 if (
const auto *VD = dyn_cast<VarDecl>(Decl)) {
50 VD->getType().getCanonicalType().getUnqualifiedType();
52 LockGuards.push_back(VD);
61static llvm::SmallVector<llvm::SmallVector<const VarDecl *>>
63 const ast_matchers::MatchFinder::MatchResult &Result) {
65 llvm::SmallVector<llvm::SmallVector<const VarDecl *>> LockGuardGroups;
66 llvm::SmallVector<const VarDecl *> CurrentLockGuardGroup;
68 auto AddAndClearCurrentGroup = [&]() {
69 if (!CurrentLockGuardGroup.empty()) {
70 LockGuardGroups.push_back(CurrentLockGuardGroup);
71 CurrentLockGuardGroup.clear();
75 for (
const Stmt *Stmt :
Block->body()) {
76 if (
const auto *DS = dyn_cast<DeclStmt>(Stmt)) {
77 const llvm::SmallVector<const VarDecl *> LockGuards =
80 if (!LockGuards.empty()) {
81 CurrentLockGuardGroup.append(LockGuards);
85 AddAndClearCurrentGroup();
88 AddAndClearCurrentGroup();
90 return LockGuardGroups;
95 const TypeLoc LockGuardTypeLoc = SourceInfo->getTypeLoc();
97 return {LockGuardTypeLoc.getBeginLoc(), LockGuardTypeLoc.getEndLoc()};
102 const auto TemplateLoc =
103 SourceInfo->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
107 return {TemplateLoc.getTemplateNameLoc(),
108 TemplateLoc.getLAngleLoc().getLocWithOffset(-1)};
112 "use 'std::scoped_lock' instead of 'std::lock_guard'";
117 WarnOnSingleLocks(Options.get(
"WarnOnSingleLocks", true)),
118 WarnOnUsingAndTypedef(Options.get(
"WarnOnUsingAndTypedef", true)) {}
121 Options.store(Opts,
"WarnOnSingleLocks", WarnOnSingleLocks);
122 Options.store(Opts,
"WarnOnUsingAndTypedef", WarnOnUsingAndTypedef);
126 const auto LockGuardClassDecl =
127 namedDecl(hasName(
"lock_guard"), isInStdNamespace());
129 const auto LockGuardType =
130 qualType(anyOf(hasUnqualifiedDesugaredType(
131 recordType(hasDeclaration(LockGuardClassDecl))),
132 hasUnqualifiedDesugaredType(templateSpecializationType(
133 hasDeclaration(LockGuardClassDecl)))));
135 const auto LockVarDecl = varDecl(hasType(LockGuardType));
137 if (WarnOnSingleLocks) {
140 unless(isExpansionInSystemHeader()),
141 has(declStmt(has(LockVarDecl)).bind(
"lock-decl-single")),
142 unless(has(declStmt(unless(equalsBoundNode(
"lock-decl-single")),
143 has(LockVarDecl))))),
148 compoundStmt(unless(isExpansionInSystemHeader()),
149 has(declStmt(has(LockVarDecl)).bind(
"lock-decl-multiple")),
150 has(declStmt(unless(equalsBoundNode(
"lock-decl-multiple")),
152 .bind(
"block-multiple"),
155 if (WarnOnUsingAndTypedef) {
157 Finder->addMatcher(typedefDecl(unless(isExpansionInSystemHeader()),
158 hasType(hasUnderlyingType(LockGuardType)))
159 .bind(
"lock-guard-typedef"),
163 Finder->addMatcher(typeAliasDecl(unless(isExpansionInSystemHeader()),
164 hasType(templateSpecializationType(
165 hasDeclaration(LockGuardClassDecl))))
166 .bind(
"lock-guard-using-alias"),
171 usingDecl(unless(isExpansionInSystemHeader()),
172 hasAnyUsingShadowDecl(hasTargetDecl(LockGuardClassDecl)))
173 .bind(
"lock-guard-using-decl"),
179 if (
const auto *DS = Result.Nodes.getNodeAs<DeclStmt>(
"lock-decl-single")) {
181 diagOnMultipleLocks({Decls}, Result);
185 if (
const auto *Compound =
186 Result.Nodes.getNodeAs<CompoundStmt>(
"block-multiple")) {
191 if (
const auto *Typedef =
192 Result.Nodes.getNodeAs<TypedefDecl>(
"lock-guard-typedef")) {
193 diagOnSourceInfo(Typedef->getTypeSourceInfo(), Result);
197 if (
const auto *UsingAlias =
198 Result.Nodes.getNodeAs<TypeAliasDecl>(
"lock-guard-using-alias")) {
199 diagOnSourceInfo(UsingAlias->getTypeSourceInfo(), Result);
203 if (
const auto *Using =
204 Result.Nodes.getNodeAs<UsingDecl>(
"lock-guard-using-decl")) {
205 diagOnUsingDecl(Using, Result);
209void UseScopedLockCheck::diagOnSingleLock(
210 const VarDecl *LockGuard,
const MatchFinder::MatchResult &Result) {
213 const SourceRange LockGuardTypeRange =
216 if (LockGuardTypeRange.isInvalid())
221 const auto *CtorCall =
222 dyn_cast_if_present<CXXConstructExpr>(LockGuard->getInit());
226 if (CtorCall->getNumArgs() == 1) {
227 Diag << FixItHint::CreateReplacement(LockGuardTypeRange,
232 if (CtorCall->getNumArgs() == 2) {
233 const Expr *
const *CtorArgs = CtorCall->getArgs();
235 const Expr *MutexArg = CtorArgs[0];
236 const Expr *AdoptLockArg = CtorArgs[1];
238 const StringRef MutexSourceText = Lexer::getSourceText(
239 CharSourceRange::getTokenRange(MutexArg->getSourceRange()),
240 *Result.SourceManager, Result.Context->getLangOpts());
241 const StringRef AdoptLockSourceText = Lexer::getSourceText(
242 CharSourceRange::getTokenRange(AdoptLockArg->getSourceRange()),
243 *Result.SourceManager, Result.Context->getLangOpts());
245 Diag << FixItHint::CreateReplacement(LockGuardTypeRange,
"std::scoped_lock")
246 << FixItHint::CreateReplacement(
247 SourceRange(MutexArg->getBeginLoc(), AdoptLockArg->getEndLoc()),
248 (llvm::Twine(AdoptLockSourceText) +
", " + MutexSourceText)
253 llvm_unreachable(
"Invalid argument number of std::lock_guard constructor");
256void UseScopedLockCheck::diagOnMultipleLocks(
257 const llvm::SmallVector<llvm::SmallVector<const VarDecl *>> &LockGroups,
258 const ast_matchers::MatchFinder::MatchResult &Result) {
259 for (
const llvm::SmallVector<const VarDecl *> &Group : LockGroups) {
260 if (Group.size() == 1) {
261 if (WarnOnSingleLocks)
262 diagOnSingleLock(Group[0], Result);
264 diag(Group[0]->getBeginLoc(),
265 "use single 'std::scoped_lock' instead of multiple "
266 "'std::lock_guard'");
268 for (
const VarDecl *Lock : llvm::drop_begin(Group))
269 diag(Lock->getLocation(),
"additional 'std::lock_guard' declared here",
270 DiagnosticIDs::Note);
275void UseScopedLockCheck::diagOnSourceInfo(
276 const TypeSourceInfo *LockGuardSourceInfo,
277 const ast_matchers::MatchFinder::MatchResult &Result) {
278 const TypeLoc TL = LockGuardSourceInfo->getTypeLoc();
280 if (
const auto TTL = TL.getAs<TemplateSpecializationTypeLoc>()) {
283 const SourceRange LockGuardRange =
285 if (LockGuardRange.isInvalid())
288 Diag << FixItHint::CreateReplacement(LockGuardRange,
"scoped_lock");
292void UseScopedLockCheck::diagOnUsingDecl(
293 const UsingDecl *UsingDecl,
294 const ast_matchers::MatchFinder::MatchResult &Result) {
296 << FixItHint::CreateReplacement(UsingDecl->getLocation(),
"scoped_lock");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
UseScopedLockCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
static bool isLockGuard(const QualType &Type)
static llvm::SmallVector< llvm::SmallVector< const VarDecl * > > findLocksInCompoundStmt(const CompoundStmt *Block, const ast_matchers::MatchFinder::MatchResult &Result)
static SourceRange getLockGuardNameRange(const TypeSourceInfo *SourceInfo)
static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo)
static bool isLockGuardDecl(const NamedDecl *Decl)
static llvm::SmallVector< const VarDecl * > getLockGuardsFromDecl(const DeclStmt *DS)
static const StringRef UseScopedLockMessage
llvm::StringMap< ClangTidyValue > OptionMap