1 //===--- TypeTraitsCheck.cpp - clang-tidy ---------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "TypeTraitsCheck.h"
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "clang/Lex/Lexer.h"
14 using namespace clang::ast_matchers
;
16 namespace clang::tidy::modernize
{
18 static const llvm::StringSet
<> ValueTraits
= {
23 "has_unique_object_representations",
24 "has_virtual_destructor",
38 "is_copy_constructible",
39 "is_default_constructible",
50 "is_layout_compatible",
51 "is_lvalue_reference",
52 "is_member_function_pointer",
53 "is_member_object_pointer",
56 "is_move_constructible",
57 "is_nothrow_assignable",
58 "is_nothrow_constructible",
59 "is_nothrow_convertible",
60 "is_nothrow_copy_assignable",
61 "is_nothrow_copy_constructible",
62 "is_nothrow_default_constructible",
63 "is_nothrow_destructible",
64 "is_nothrow_invocable",
65 "is_nothrow_invocable_r",
66 "is_nothrow_move_assignable",
67 "is_nothrow_move_constructible",
68 "is_nothrow_swappable",
69 "is_nothrow_swappable_with",
73 "is_pointer_interconvertible_base_of",
76 "is_rvalue_reference",
85 "is_trivially_assignable",
86 "is_trivially_constructible",
87 "is_trivially_copy_assignable",
88 "is_trivially_copy_constructible",
89 "is_trivially_copyable",
90 "is_trivially_default_constructible",
91 "is_trivially_destructible",
92 "is_trivially_move_assignable",
93 "is_trivially_move_constructible",
101 "reference_constructs_from_temporary",
102 "reference_converts_from_temporary",
105 static const llvm::StringSet
<> TypeTraits
= {
113 "add_lvalue_reference",
114 "add_rvalue_reference",
120 "remove_all_extents",
135 static DeclarationName
getName(const DependentScopeDeclRefExpr
&D
) {
136 return D
.getDeclName();
139 static DeclarationName
getName(const DeclRefExpr
&D
) {
140 return D
.getDecl()->getDeclName();
143 static bool isNamedType(const ElaboratedTypeLoc
&ETL
) {
144 if (const auto *TFT
=
145 ETL
.getNamedTypeLoc().getTypePtr()->getAs
<TypedefType
>()) {
146 const TypedefNameDecl
*Decl
= TFT
->getDecl();
147 return Decl
->getDeclName().isIdentifier() && Decl
->getName() == "type";
152 static bool isNamedType(const DependentNameTypeLoc
&DTL
) {
153 return DTL
.getTypePtr()->getIdentifier()->getName() == "type";
157 AST_POLYMORPHIC_MATCHER(isValue
, AST_POLYMORPHIC_SUPPORTED_TYPES(
158 DeclRefExpr
, DependentScopeDeclRefExpr
)) {
159 const IdentifierInfo
*Ident
= getName(Node
).getAsIdentifierInfo();
160 return Ident
&& Ident
->isStr("value");
163 AST_POLYMORPHIC_MATCHER(isType
,
164 AST_POLYMORPHIC_SUPPORTED_TYPES(ElaboratedTypeLoc
,
165 DependentNameTypeLoc
)) {
166 return Node
.getBeginLoc().isValid() && isNamedType(Node
);
170 static constexpr char Bind
[] = "";
172 void TypeTraitsCheck::registerMatchers(MatchFinder
*Finder
) {
173 const ast_matchers::internal::VariadicDynCastAllOfMatcher
<
175 DependentScopeDeclRefExpr
>
176 dependentScopeDeclRefExpr
; // NOLINT(readability-identifier-naming)
177 const ast_matchers::internal::VariadicDynCastAllOfMatcher
<
179 DependentNameTypeLoc
>
180 dependentNameTypeLoc
; // NOLINT(readability-identifier-naming)
182 // Only register matchers for trait<...>::value in c++17 mode.
183 if (getLangOpts().CPlusPlus17
) {
184 Finder
->addMatcher(mapAnyOf(declRefExpr
, dependentScopeDeclRefExpr
)
189 Finder
->addMatcher(mapAnyOf(elaboratedTypeLoc
, dependentNameTypeLoc
)
195 static bool isNamedDeclInStdTraitsSet(const NamedDecl
*ND
,
196 const llvm::StringSet
<> &Set
) {
197 return ND
->isInStdNamespace() && ND
->getDeclName().isIdentifier() &&
198 Set
.contains(ND
->getName());
201 static bool checkTemplatedDecl(const NestedNameSpecifier
*NNS
,
202 const llvm::StringSet
<> &Set
) {
205 const Type
*NNST
= NNS
->getAsType();
208 const auto *TST
= NNST
->getAs
<TemplateSpecializationType
>();
211 if (const TemplateDecl
*TD
= TST
->getTemplateName().getAsTemplateDecl()) {
212 return isNamedDeclInStdTraitsSet(TD
, Set
);
217 TypeTraitsCheck::TypeTraitsCheck(StringRef Name
, ClangTidyContext
*Context
)
218 : ClangTidyCheck(Name
, Context
),
219 IgnoreMacros(Options
.getLocalOrGlobal("IgnoreMacros", false)) {}
221 void TypeTraitsCheck::check(const MatchFinder::MatchResult
&Result
) {
222 auto EmitValueWarning
= [this, &Result
](const NestedNameSpecifierLoc
&QualLoc
,
223 SourceLocation EndLoc
) {
224 SourceLocation TemplateNameEndLoc
;
225 if (auto TSTL
= QualLoc
.getTypeLoc().getAs
<TemplateSpecializationTypeLoc
>();
227 TemplateNameEndLoc
= Lexer::getLocForEndOfToken(
228 TSTL
.getTemplateNameLoc(), 0, *Result
.SourceManager
,
229 Result
.Context
->getLangOpts());
233 if (EndLoc
.isMacroID() || QualLoc
.getEndLoc().isMacroID() ||
234 TemplateNameEndLoc
.isMacroID()) {
237 diag(QualLoc
.getBeginLoc(), "use c++17 style variable templates");
240 diag(QualLoc
.getBeginLoc(), "use c++17 style variable templates")
241 << FixItHint::CreateInsertion(TemplateNameEndLoc
, "_v")
242 << FixItHint::CreateRemoval({QualLoc
.getEndLoc(), EndLoc
});
245 auto EmitTypeWarning
= [this, &Result
](const NestedNameSpecifierLoc
&QualLoc
,
246 SourceLocation EndLoc
,
247 SourceLocation TypenameLoc
) {
248 SourceLocation TemplateNameEndLoc
;
249 if (auto TSTL
= QualLoc
.getTypeLoc().getAs
<TemplateSpecializationTypeLoc
>();
251 TemplateNameEndLoc
= Lexer::getLocForEndOfToken(
252 TSTL
.getTemplateNameLoc(), 0, *Result
.SourceManager
,
253 Result
.Context
->getLangOpts());
257 if (EndLoc
.isMacroID() || QualLoc
.getEndLoc().isMacroID() ||
258 TemplateNameEndLoc
.isMacroID() || TypenameLoc
.isMacroID()) {
261 diag(QualLoc
.getBeginLoc(), "use c++14 style type templates");
264 auto Diag
= diag(QualLoc
.getBeginLoc(), "use c++14 style type templates");
266 if (TypenameLoc
.isValid())
267 Diag
<< FixItHint::CreateRemoval(TypenameLoc
);
268 Diag
<< FixItHint::CreateInsertion(TemplateNameEndLoc
, "_t")
269 << FixItHint::CreateRemoval({QualLoc
.getEndLoc(), EndLoc
});
272 if (const auto *DRE
= Result
.Nodes
.getNodeAs
<DeclRefExpr
>(Bind
)) {
273 if (!DRE
->hasQualifier())
275 if (const auto *CTSD
= dyn_cast_if_present
<ClassTemplateSpecializationDecl
>(
276 DRE
->getQualifier()->getAsRecordDecl())) {
277 if (isNamedDeclInStdTraitsSet(CTSD
, ValueTraits
))
278 EmitValueWarning(DRE
->getQualifierLoc(), DRE
->getEndLoc());
283 if (const auto *ETL
= Result
.Nodes
.getNodeAs
<ElaboratedTypeLoc
>(Bind
)) {
284 const NestedNameSpecifierLoc QualLoc
= ETL
->getQualifierLoc();
285 const auto *NNS
= QualLoc
.getNestedNameSpecifier();
288 if (const auto *CTSD
= dyn_cast_if_present
<ClassTemplateSpecializationDecl
>(
289 NNS
->getAsRecordDecl())) {
290 if (isNamedDeclInStdTraitsSet(CTSD
, TypeTraits
))
291 EmitTypeWarning(ETL
->getQualifierLoc(), ETL
->getEndLoc(),
292 ETL
->getElaboratedKeywordLoc());
297 if (const auto *DSDRE
=
298 Result
.Nodes
.getNodeAs
<DependentScopeDeclRefExpr
>(Bind
)) {
299 if (checkTemplatedDecl(DSDRE
->getQualifier(), ValueTraits
))
300 EmitValueWarning(DSDRE
->getQualifierLoc(), DSDRE
->getEndLoc());
304 if (const auto *DNTL
= Result
.Nodes
.getNodeAs
<DependentNameTypeLoc
>(Bind
)) {
305 NestedNameSpecifierLoc QualLoc
= DNTL
->getQualifierLoc();
306 if (checkTemplatedDecl(QualLoc
.getNestedNameSpecifier(), TypeTraits
))
307 EmitTypeWarning(QualLoc
, DNTL
->getEndLoc(),
308 DNTL
->getElaboratedKeywordLoc());
313 void TypeTraitsCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
314 Options
.store(Opts
, "IgnoreMacros", IgnoreMacros
);
316 } // namespace clang::tidy::modernize