Skip to content

Commit ff7c5c8

Browse files
committed
merge main into amd-staging
2 parents dc6b029 + 613222e commit ff7c5c8

File tree

243 files changed

+54881
-55465
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

243 files changed

+54881
-55465
lines changed

clang-tools-extra/clang-tidy/modernize/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ add_clang_library(clangTidyModernizeModule STATIC
4242
UseNullptrCheck.cpp
4343
UseOverrideCheck.cpp
4444
UseRangesCheck.cpp
45+
UseScopedLockCheck.cpp
4546
UseStartsEndsWithCheck.cpp
4647
UseStdFormatCheck.cpp
4748
UseStdNumbersCheck.cpp

clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "UseNullptrCheck.h"
4444
#include "UseOverrideCheck.h"
4545
#include "UseRangesCheck.h"
46+
#include "UseScopedLockCheck.h"
4647
#include "UseStartsEndsWithCheck.h"
4748
#include "UseStdFormatCheck.h"
4849
#include "UseStdNumbersCheck.h"
@@ -80,6 +81,8 @@ class ModernizeModule : public ClangTidyModule {
8081
CheckFactories.registerCheck<UseIntegerSignComparisonCheck>(
8182
"modernize-use-integer-sign-comparison");
8283
CheckFactories.registerCheck<UseRangesCheck>("modernize-use-ranges");
84+
CheckFactories.registerCheck<UseScopedLockCheck>(
85+
"modernize-use-scoped-lock");
8386
CheckFactories.registerCheck<UseStartsEndsWithCheck>(
8487
"modernize-use-starts-ends-with");
8588
CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format");
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
//===--- UseScopedLockCheck.cpp - clang-tidy ------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "UseScopedLockCheck.h"
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"
20+
21+
using namespace clang::ast_matchers;
22+
23+
namespace clang::tidy::modernize {
24+
25+
static bool isLockGuardDecl(const NamedDecl *Decl) {
26+
return Decl->getDeclName().isIdentifier() &&
27+
Decl->getName() == "lock_guard" && Decl->isInStdNamespace();
28+
}
29+
30+
static bool isLockGuard(const QualType &Type) {
31+
if (const auto *Record = Type->getAs<RecordType>())
32+
if (const RecordDecl *Decl = Record->getDecl())
33+
return isLockGuardDecl(Decl);
34+
35+
if (const auto *TemplateSpecType = Type->getAs<TemplateSpecializationType>())
36+
if (const TemplateDecl *Decl =
37+
TemplateSpecType->getTemplateName().getAsTemplateDecl())
38+
return isLockGuardDecl(Decl);
39+
40+
return false;
41+
}
42+
43+
static llvm::SmallVector<const VarDecl *>
44+
getLockGuardsFromDecl(const DeclStmt *DS) {
45+
llvm::SmallVector<const VarDecl *> LockGuards;
46+
47+
for (const Decl *Decl : DS->decls()) {
48+
if (const auto *VD = dyn_cast<VarDecl>(Decl)) {
49+
const QualType Type =
50+
VD->getType().getCanonicalType().getUnqualifiedType();
51+
if (isLockGuard(Type))
52+
LockGuards.push_back(VD);
53+
}
54+
}
55+
56+
return LockGuards;
57+
}
58+
59+
// Scans through the statements in a block and groups consecutive
60+
// 'std::lock_guard' variable declarations together.
61+
static llvm::SmallVector<llvm::SmallVector<const VarDecl *>>
62+
findLocksInCompoundStmt(const CompoundStmt *Block,
63+
const ast_matchers::MatchFinder::MatchResult &Result) {
64+
// store groups of consecutive 'std::lock_guard' declarations
65+
llvm::SmallVector<llvm::SmallVector<const VarDecl *>> LockGuardGroups;
66+
llvm::SmallVector<const VarDecl *> CurrentLockGuardGroup;
67+
68+
auto AddAndClearCurrentGroup = [&]() {
69+
if (!CurrentLockGuardGroup.empty()) {
70+
LockGuardGroups.push_back(CurrentLockGuardGroup);
71+
CurrentLockGuardGroup.clear();
72+
}
73+
};
74+
75+
for (const Stmt *Stmt : Block->body()) {
76+
if (const auto *DS = dyn_cast<DeclStmt>(Stmt)) {
77+
llvm::SmallVector<const VarDecl *> LockGuards = getLockGuardsFromDecl(DS);
78+
79+
if (!LockGuards.empty()) {
80+
CurrentLockGuardGroup.append(LockGuards);
81+
continue;
82+
}
83+
}
84+
AddAndClearCurrentGroup();
85+
}
86+
87+
AddAndClearCurrentGroup();
88+
89+
return LockGuardGroups;
90+
}
91+
92+
static TemplateSpecializationTypeLoc
93+
getTemplateLockGuardTypeLoc(const TypeSourceInfo *SourceInfo) {
94+
const TypeLoc Loc = SourceInfo->getTypeLoc();
95+
96+
const auto ElaboratedLoc = Loc.getAs<ElaboratedTypeLoc>();
97+
if (!ElaboratedLoc)
98+
return {};
99+
100+
return ElaboratedLoc.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>();
101+
}
102+
103+
// Find the exact source range of the 'lock_guard' token
104+
static SourceRange getLockGuardRange(const TypeSourceInfo *SourceInfo) {
105+
const TypeLoc LockGuardTypeLoc = SourceInfo->getTypeLoc();
106+
107+
return SourceRange(LockGuardTypeLoc.getBeginLoc(),
108+
LockGuardTypeLoc.getEndLoc());
109+
}
110+
111+
// Find the exact source range of the 'lock_guard' name token
112+
static SourceRange getLockGuardNameRange(const TypeSourceInfo *SourceInfo) {
113+
const TemplateSpecializationTypeLoc TemplateLoc =
114+
getTemplateLockGuardTypeLoc(SourceInfo);
115+
if (!TemplateLoc)
116+
return {};
117+
118+
return SourceRange(TemplateLoc.getTemplateNameLoc(),
119+
TemplateLoc.getLAngleLoc().getLocWithOffset(-1));
120+
}
121+
122+
const static StringRef UseScopedLockMessage =
123+
"use 'std::scoped_lock' instead of 'std::lock_guard'";
124+
125+
UseScopedLockCheck::UseScopedLockCheck(StringRef Name,
126+
ClangTidyContext *Context)
127+
: ClangTidyCheck(Name, Context),
128+
WarnOnSingleLocks(Options.get("WarnOnSingleLocks", true)),
129+
WarnOnUsingAndTypedef(Options.get("WarnOnUsingAndTypedef", true)) {}
130+
131+
void UseScopedLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
132+
Options.store(Opts, "WarnOnSingleLocks", WarnOnSingleLocks);
133+
Options.store(Opts, "WarnOnUsingAndTypedef", WarnOnUsingAndTypedef);
134+
}
135+
136+
void UseScopedLockCheck::registerMatchers(MatchFinder *Finder) {
137+
const auto LockGuardClassDecl =
138+
namedDecl(hasName("lock_guard"), isInStdNamespace());
139+
140+
const auto LockGuardType = qualType(anyOf(
141+
hasUnqualifiedDesugaredType(
142+
recordType(hasDeclaration(LockGuardClassDecl))),
143+
elaboratedType(namesType(hasUnqualifiedDesugaredType(
144+
templateSpecializationType(hasDeclaration(LockGuardClassDecl)))))));
145+
146+
const auto LockVarDecl = varDecl(hasType(LockGuardType));
147+
148+
if (WarnOnSingleLocks) {
149+
Finder->addMatcher(
150+
compoundStmt(
151+
unless(isExpansionInSystemHeader()),
152+
has(declStmt(has(LockVarDecl)).bind("lock-decl-single")),
153+
unless(has(declStmt(unless(equalsBoundNode("lock-decl-single")),
154+
has(LockVarDecl))))),
155+
this);
156+
}
157+
158+
Finder->addMatcher(
159+
compoundStmt(unless(isExpansionInSystemHeader()),
160+
has(declStmt(has(LockVarDecl)).bind("lock-decl-multiple")),
161+
has(declStmt(unless(equalsBoundNode("lock-decl-multiple")),
162+
has(LockVarDecl))))
163+
.bind("block-multiple"),
164+
this);
165+
166+
if (WarnOnUsingAndTypedef) {
167+
// Match 'typedef std::lock_guard<std::mutex> Lock'
168+
Finder->addMatcher(typedefDecl(unless(isExpansionInSystemHeader()),
169+
hasUnderlyingType(LockGuardType))
170+
.bind("lock-guard-typedef"),
171+
this);
172+
173+
// Match 'using Lock = std::lock_guard<std::mutex>'
174+
Finder->addMatcher(
175+
typeAliasDecl(
176+
unless(isExpansionInSystemHeader()),
177+
hasType(elaboratedType(namesType(templateSpecializationType(
178+
hasDeclaration(LockGuardClassDecl))))))
179+
.bind("lock-guard-using-alias"),
180+
this);
181+
182+
// Match 'using std::lock_guard'
183+
Finder->addMatcher(
184+
usingDecl(unless(isExpansionInSystemHeader()),
185+
hasAnyUsingShadowDecl(hasTargetDecl(LockGuardClassDecl)))
186+
.bind("lock-guard-using-decl"),
187+
this);
188+
}
189+
}
190+
191+
void UseScopedLockCheck::check(const MatchFinder::MatchResult &Result) {
192+
if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("lock-decl-single")) {
193+
llvm::SmallVector<const VarDecl *> Decls = getLockGuardsFromDecl(DS);
194+
diagOnMultipleLocks({Decls}, Result);
195+
return;
196+
}
197+
198+
if (const auto *Compound =
199+
Result.Nodes.getNodeAs<CompoundStmt>("block-multiple")) {
200+
diagOnMultipleLocks(findLocksInCompoundStmt(Compound, Result), Result);
201+
return;
202+
}
203+
204+
if (const auto *Typedef =
205+
Result.Nodes.getNodeAs<TypedefDecl>("lock-guard-typedef")) {
206+
diagOnSourceInfo(Typedef->getTypeSourceInfo(), Result);
207+
return;
208+
}
209+
210+
if (const auto *UsingAlias =
211+
Result.Nodes.getNodeAs<TypeAliasDecl>("lock-guard-using-alias")) {
212+
diagOnSourceInfo(UsingAlias->getTypeSourceInfo(), Result);
213+
return;
214+
}
215+
216+
if (const auto *Using =
217+
Result.Nodes.getNodeAs<UsingDecl>("lock-guard-using-decl")) {
218+
diagOnUsingDecl(Using, Result);
219+
}
220+
}
221+
222+
void UseScopedLockCheck::diagOnSingleLock(
223+
const VarDecl *LockGuard, const MatchFinder::MatchResult &Result) {
224+
auto Diag = diag(LockGuard->getBeginLoc(), UseScopedLockMessage);
225+
226+
const SourceRange LockGuardTypeRange =
227+
getLockGuardRange(LockGuard->getTypeSourceInfo());
228+
229+
if (LockGuardTypeRange.isInvalid())
230+
return;
231+
232+
// Create Fix-its only if we can find the constructor call to properly handle
233+
// 'std::lock_guard l(m, std::adopt_lock)' case.
234+
const auto *CtorCall = dyn_cast<CXXConstructExpr>(LockGuard->getInit());
235+
if (!CtorCall)
236+
return;
237+
238+
if (CtorCall->getNumArgs() == 1) {
239+
Diag << FixItHint::CreateReplacement(LockGuardTypeRange,
240+
"std::scoped_lock");
241+
return;
242+
}
243+
244+
if (CtorCall->getNumArgs() == 2) {
245+
const Expr *const *CtorArgs = CtorCall->getArgs();
246+
247+
const Expr *MutexArg = CtorArgs[0];
248+
const Expr *AdoptLockArg = CtorArgs[1];
249+
250+
const StringRef MutexSourceText = Lexer::getSourceText(
251+
CharSourceRange::getTokenRange(MutexArg->getSourceRange()),
252+
*Result.SourceManager, Result.Context->getLangOpts());
253+
const StringRef AdoptLockSourceText = Lexer::getSourceText(
254+
CharSourceRange::getTokenRange(AdoptLockArg->getSourceRange()),
255+
*Result.SourceManager, Result.Context->getLangOpts());
256+
257+
Diag << FixItHint::CreateReplacement(LockGuardTypeRange, "std::scoped_lock")
258+
<< FixItHint::CreateReplacement(
259+
SourceRange(MutexArg->getBeginLoc(), AdoptLockArg->getEndLoc()),
260+
(llvm::Twine(AdoptLockSourceText) + ", " + MutexSourceText)
261+
.str());
262+
return;
263+
}
264+
265+
llvm_unreachable("Invalid argument number of std::lock_guard constructor");
266+
}
267+
268+
void UseScopedLockCheck::diagOnMultipleLocks(
269+
const llvm::SmallVector<llvm::SmallVector<const VarDecl *>> &LockGroups,
270+
const ast_matchers::MatchFinder::MatchResult &Result) {
271+
for (const llvm::SmallVector<const VarDecl *> &Group : LockGroups) {
272+
if (Group.size() == 1) {
273+
if (WarnOnSingleLocks)
274+
diagOnSingleLock(Group[0], Result);
275+
} else {
276+
diag(Group[0]->getBeginLoc(),
277+
"use single 'std::scoped_lock' instead of multiple "
278+
"'std::lock_guard'");
279+
280+
for (const VarDecl *Lock : llvm::drop_begin(Group))
281+
diag(Lock->getLocation(), "additional 'std::lock_guard' declared here",
282+
DiagnosticIDs::Note);
283+
}
284+
}
285+
}
286+
287+
void UseScopedLockCheck::diagOnSourceInfo(
288+
const TypeSourceInfo *LockGuardSourceInfo,
289+
const ast_matchers::MatchFinder::MatchResult &Result) {
290+
const TypeLoc TL = LockGuardSourceInfo->getTypeLoc();
291+
292+
if (const auto ElaboratedTL = TL.getAs<ElaboratedTypeLoc>()) {
293+
auto Diag = diag(ElaboratedTL.getBeginLoc(), UseScopedLockMessage);
294+
295+
const SourceRange LockGuardRange =
296+
getLockGuardNameRange(LockGuardSourceInfo);
297+
if (LockGuardRange.isInvalid())
298+
return;
299+
300+
Diag << FixItHint::CreateReplacement(LockGuardRange, "scoped_lock");
301+
}
302+
}
303+
304+
void UseScopedLockCheck::diagOnUsingDecl(
305+
const UsingDecl *UsingDecl,
306+
const ast_matchers::MatchFinder::MatchResult &Result) {
307+
diag(UsingDecl->getLocation(), UseScopedLockMessage)
308+
<< FixItHint::CreateReplacement(UsingDecl->getLocation(), "scoped_lock");
309+
}
310+
311+
} // namespace clang::tidy::modernize
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//===--- UseScopedLockCheck.h - clang-tidy ----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESCOPEDLOCKCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESCOPEDLOCKCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
#include "clang/AST/ASTTypeTraits.h"
14+
#include "clang/AST/Stmt.h"
15+
#include <optional>
16+
17+
namespace clang::tidy::modernize {
18+
19+
/// Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's
20+
/// alternative ``std::scoped_lock``.
21+
///
22+
/// For the user-facing documentation see:
23+
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-scoped-lock.html
24+
class UseScopedLockCheck : public ClangTidyCheck {
25+
public:
26+
UseScopedLockCheck(StringRef Name, ClangTidyContext *Context);
27+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
28+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
29+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
30+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
31+
return LangOpts.CPlusPlus17;
32+
}
33+
std::optional<TraversalKind> getCheckTraversalKind() const override {
34+
return TK_IgnoreUnlessSpelledInSource;
35+
}
36+
37+
private:
38+
void diagOnSingleLock(const VarDecl *LockGuard,
39+
const ast_matchers::MatchFinder::MatchResult &Result);
40+
void diagOnMultipleLocks(
41+
const llvm::SmallVector<llvm::SmallVector<const VarDecl *>> &LockGroups,
42+
const ast_matchers::MatchFinder::MatchResult &Result);
43+
void diagOnSourceInfo(const TypeSourceInfo *LockGuardSourceInfo,
44+
const ast_matchers::MatchFinder::MatchResult &Result);
45+
void diagOnUsingDecl(const UsingDecl *UsingDecl,
46+
const ast_matchers::MatchFinder::MatchResult &Result);
47+
48+
const bool WarnOnSingleLocks;
49+
const bool WarnOnUsingAndTypedef;
50+
};
51+
52+
} // namespace clang::tidy::modernize
53+
54+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESCOPEDLOCKCHECK_H

0 commit comments

Comments
 (0)