xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp (revision 376168babb51aa08bc864d4797db4a6dbd53fdbc)
1 //===--- TypeTraitsCheck.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 "TypeTraitsCheck.h"
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/ASTMatchers/ASTMatchers.h"
12 #include "clang/Lex/Lexer.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang::tidy::modernize {
17 
18 static const llvm::StringSet<> ValueTraits = {
19     "alignment_of",
20     "conjunction",
21     "disjunction",
22     "extent",
23     "has_unique_object_representations",
24     "has_virtual_destructor",
25     "is_abstract",
26     "is_aggregate",
27     "is_arithmetic",
28     "is_array",
29     "is_assignable",
30     "is_base_of",
31     "is_bounded_array",
32     "is_class",
33     "is_compound",
34     "is_const",
35     "is_constructible",
36     "is_convertible",
37     "is_copy_assignable",
38     "is_copy_constructible",
39     "is_default_constructible",
40     "is_destructible",
41     "is_empty",
42     "is_enum",
43     "is_final",
44     "is_floating_point",
45     "is_function",
46     "is_fundamental",
47     "is_integral",
48     "is_invocable",
49     "is_invocable_r",
50     "is_layout_compatible",
51     "is_lvalue_reference",
52     "is_member_function_pointer",
53     "is_member_object_pointer",
54     "is_member_pointer",
55     "is_move_assignable",
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",
70     "is_null_pointer",
71     "is_object",
72     "is_pointer",
73     "is_pointer_interconvertible_base_of",
74     "is_polymorphic",
75     "is_reference",
76     "is_rvalue_reference",
77     "is_same",
78     "is_scalar",
79     "is_scoped_enum",
80     "is_signed",
81     "is_standard_layout",
82     "is_swappable",
83     "is_swappable_with",
84     "is_trivial",
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",
94     "is_unbounded_array",
95     "is_union",
96     "is_unsigned",
97     "is_void",
98     "is_volatile",
99     "negation",
100     "rank",
101     "reference_constructs_from_temporary",
102     "reference_converts_from_temporary",
103 };
104 
105 static const llvm::StringSet<> TypeTraits = {
106     "remove_cv",
107     "remove_const",
108     "remove_volatile",
109     "add_cv",
110     "add_const",
111     "add_volatile",
112     "remove_reference",
113     "add_lvalue_reference",
114     "add_rvalue_reference",
115     "remove_pointer",
116     "add_pointer",
117     "make_signed",
118     "make_unsigned",
119     "remove_extent",
120     "remove_all_extents",
121     "aligned_storage",
122     "aligned_union",
123     "decay",
124     "remove_cvref",
125     "enable_if",
126     "conditional",
127     "common_type",
128     "common_reference",
129     "underlying_type",
130     "result_of",
131     "invoke_result",
132     "type_identity",
133 };
134 
getName(const DependentScopeDeclRefExpr & D)135 static DeclarationName getName(const DependentScopeDeclRefExpr &D) {
136   return D.getDeclName();
137 }
138 
getName(const DeclRefExpr & D)139 static DeclarationName getName(const DeclRefExpr &D) {
140   return D.getDecl()->getDeclName();
141 }
142 
isNamedType(const ElaboratedTypeLoc & ETL)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";
148   }
149   return false;
150 }
151 
isNamedType(const DependentNameTypeLoc & DTL)152 static bool isNamedType(const DependentNameTypeLoc &DTL) {
153   return DTL.getTypePtr()->getIdentifier()->getName() == "type";
154 }
155 
156 namespace {
AST_POLYMORPHIC_MATCHER(isValue,AST_POLYMORPHIC_SUPPORTED_TYPES (DeclRefExpr,DependentScopeDeclRefExpr))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");
161 }
162 
AST_POLYMORPHIC_MATCHER(isType,AST_POLYMORPHIC_SUPPORTED_TYPES (ElaboratedTypeLoc,DependentNameTypeLoc))163 AST_POLYMORPHIC_MATCHER(isType,
164                         AST_POLYMORPHIC_SUPPORTED_TYPES(ElaboratedTypeLoc,
165                                                         DependentNameTypeLoc)) {
166   return Node.getBeginLoc().isValid() && isNamedType(Node);
167 }
168 } // namespace
169 
170 static constexpr char Bind[] = "";
171 
registerMatchers(MatchFinder * Finder)172 void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) {
173   const ast_matchers::internal::VariadicDynCastAllOfMatcher<
174       Stmt,
175       DependentScopeDeclRefExpr>
176       dependentScopeDeclRefExpr; // NOLINT(readability-identifier-naming)
177   const ast_matchers::internal::VariadicDynCastAllOfMatcher<
178       TypeLoc,
179       DependentNameTypeLoc>
180       dependentNameTypeLoc; // NOLINT(readability-identifier-naming)
181 
182   // Only register matchers for trait<...>::value in c++17 mode.
183   if (getLangOpts().CPlusPlus17) {
184     Finder->addMatcher(mapAnyOf(declRefExpr, dependentScopeDeclRefExpr)
185                            .with(isValue())
186                            .bind(Bind),
187                        this);
188   }
189   Finder->addMatcher(mapAnyOf(elaboratedTypeLoc, dependentNameTypeLoc)
190                          .with(isType())
191                          .bind(Bind),
192                      this);
193 }
194 
isNamedDeclInStdTraitsSet(const NamedDecl * ND,const llvm::StringSet<> & Set)195 static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND,
196                                       const llvm::StringSet<> &Set) {
197   return ND->isInStdNamespace() && ND->getDeclName().isIdentifier() &&
198          Set.contains(ND->getName());
199 }
200 
checkTemplatedDecl(const NestedNameSpecifier * NNS,const llvm::StringSet<> & Set)201 static bool checkTemplatedDecl(const NestedNameSpecifier *NNS,
202                                const llvm::StringSet<> &Set) {
203   if (!NNS)
204     return false;
205   const Type *NNST = NNS->getAsType();
206   if (!NNST)
207     return false;
208   const auto *TST = NNST->getAs<TemplateSpecializationType>();
209   if (!TST)
210     return false;
211   if (const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) {
212     return isNamedDeclInStdTraitsSet(TD, Set);
213   }
214   return false;
215 }
216 
TypeTraitsCheck(StringRef Name,ClangTidyContext * Context)217 TypeTraitsCheck::TypeTraitsCheck(StringRef Name, ClangTidyContext *Context)
218     : ClangTidyCheck(Name, Context),
219       IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", false)) {}
220 
check(const MatchFinder::MatchResult & Result)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>();
226         !TSTL.isNull())
227       TemplateNameEndLoc = Lexer::getLocForEndOfToken(
228           TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
229           Result.Context->getLangOpts());
230     else
231       return;
232 
233     if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() ||
234         TemplateNameEndLoc.isMacroID()) {
235       if (IgnoreMacros)
236         return;
237       diag(QualLoc.getBeginLoc(), "use c++17 style variable templates");
238       return;
239     }
240     diag(QualLoc.getBeginLoc(), "use c++17 style variable templates")
241         << FixItHint::CreateInsertion(TemplateNameEndLoc, "_v")
242         << FixItHint::CreateRemoval({QualLoc.getEndLoc(), EndLoc});
243   };
244 
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>();
250         !TSTL.isNull())
251       TemplateNameEndLoc = Lexer::getLocForEndOfToken(
252           TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
253           Result.Context->getLangOpts());
254     else
255       return;
256 
257     if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() ||
258         TemplateNameEndLoc.isMacroID() || TypenameLoc.isMacroID()) {
259       if (IgnoreMacros)
260         return;
261       diag(QualLoc.getBeginLoc(), "use c++14 style type templates");
262       return;
263     }
264     auto Diag = diag(QualLoc.getBeginLoc(), "use c++14 style type templates");
265 
266     if (TypenameLoc.isValid())
267       Diag << FixItHint::CreateRemoval(TypenameLoc);
268     Diag << FixItHint::CreateInsertion(TemplateNameEndLoc, "_t")
269          << FixItHint::CreateRemoval({QualLoc.getEndLoc(), EndLoc});
270   };
271 
272   if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>(Bind)) {
273     if (!DRE->hasQualifier())
274       return;
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());
279     }
280     return;
281   }
282 
283   if (const auto *ETL = Result.Nodes.getNodeAs<ElaboratedTypeLoc>(Bind)) {
284     const NestedNameSpecifierLoc QualLoc = ETL->getQualifierLoc();
285     const auto *NNS = QualLoc.getNestedNameSpecifier();
286     if (!NNS)
287       return;
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());
293     }
294     return;
295   }
296 
297   if (const auto *DSDRE =
298           Result.Nodes.getNodeAs<DependentScopeDeclRefExpr>(Bind)) {
299     if (checkTemplatedDecl(DSDRE->getQualifier(), ValueTraits))
300       EmitValueWarning(DSDRE->getQualifierLoc(), DSDRE->getEndLoc());
301     return;
302   }
303 
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());
309     return;
310   }
311 }
312 
storeOptions(ClangTidyOptions::OptionMap & Opts)313 void TypeTraitsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
314   Options.store(Opts, "IgnoreMacros", IgnoreMacros);
315 }
316 } // namespace clang::tidy::modernize
317