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
14using namespace clang::ast_matchers;
15
16namespace clang::tidy::modernize {
17
18static 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
105static 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
135static DeclarationName getName(const DependentScopeDeclRefExpr &D) {
136 return D.getDeclName();
137}
138
139static DeclarationName getName(const DeclRefExpr &D) {
140 return D.getDecl()->getDeclName();
141}
142
143static 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
152static bool isNamedType(const DependentNameTypeLoc &DTL) {
153 return DTL.getTypePtr()->getIdentifier()->getName() == "type";
154}
155
156namespace {
157AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES(
158 DeclRefExpr, DependentScopeDeclRefExpr)) {
159 const IdentifierInfo *Ident = getName(Node).getAsIdentifierInfo();
160 return Ident && Ident->isStr(Str: "value");
161}
162
163AST_POLYMORPHIC_MATCHER(isType,
164 AST_POLYMORPHIC_SUPPORTED_TYPES(ElaboratedTypeLoc,
165 DependentNameTypeLoc)) {
166 return Node.getBeginLoc().isValid() && isNamedType(Node);
167}
168} // namespace
169
170static constexpr char Bind[] = "";
171
172void 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(NodeMatch: mapAnyOf(declRefExpr, dependentScopeDeclRefExpr)
185 .with(isValue())
186 .bind(ID: Bind),
187 Action: this);
188 }
189 Finder->addMatcher(mapAnyOf(elaboratedTypeLoc, dependentNameTypeLoc)
190 .with(isType())
191 .bind(Bind),
192 this);
193}
194
195static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND,
196 const llvm::StringSet<> &Set) {
197 return ND->isInStdNamespace() && ND->getDeclName().isIdentifier() &&
198 Set.contains(key: ND->getName());
199}
200
201static 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
217TypeTraitsCheck::TypeTraitsCheck(StringRef Name, ClangTidyContext *Context)
218 : ClangTidyCheck(Name, Context),
219 IgnoreMacros(Options.getLocalOrGlobal(LocalName: "IgnoreMacros", Default: false)) {}
220
221void 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 Loc: TSTL.getTemplateNameLoc(), Offset: 0, SM: *Result.SourceManager,
229 LangOpts: 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(Loc: QualLoc.getBeginLoc(), Description: "use c++17 style variable templates");
238 return;
239 }
240 diag(Loc: QualLoc.getBeginLoc(), Description: "use c++17 style variable templates")
241 << FixItHint::CreateInsertion(InsertionLoc: TemplateNameEndLoc, Code: "_v")
242 << FixItHint::CreateRemoval(RemoveRange: {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 Loc: TSTL.getTemplateNameLoc(), Offset: 0, SM: *Result.SourceManager,
253 LangOpts: 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(Loc: QualLoc.getBeginLoc(), Description: "use c++14 style type templates");
262 return;
263 }
264 auto Diag = diag(Loc: QualLoc.getBeginLoc(), Description: "use c++14 style type templates");
265
266 if (TypenameLoc.isValid())
267 Diag << FixItHint::CreateRemoval(RemoveRange: TypenameLoc);
268 Diag << FixItHint::CreateInsertion(InsertionLoc: TemplateNameEndLoc, Code: "_t")
269 << FixItHint::CreateRemoval(RemoveRange: {QualLoc.getEndLoc(), EndLoc});
270 };
271
272 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>(ID: Bind)) {
273 if (!DRE->hasQualifier())
274 return;
275 if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
276 Val: 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>(ID: 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 Val: 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>(ID: Bind)) {
299 if (checkTemplatedDecl(NNS: DSDRE->getQualifier(), Set: ValueTraits))
300 EmitValueWarning(DSDRE->getQualifierLoc(), DSDRE->getEndLoc());
301 return;
302 }
303
304 if (const auto *DNTL = Result.Nodes.getNodeAs<DependentNameTypeLoc>(ID: Bind)) {
305 NestedNameSpecifierLoc QualLoc = DNTL->getQualifierLoc();
306 if (checkTemplatedDecl(NNS: QualLoc.getNestedNameSpecifier(), Set: TypeTraits))
307 EmitTypeWarning(QualLoc, DNTL->getEndLoc(),
308 DNTL->getElaboratedKeywordLoc());
309 return;
310 }
311}
312
313void TypeTraitsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
314 Options.store(Options&: Opts, LocalName: "IgnoreMacros", Value: IgnoreMacros);
315}
316} // namespace clang::tidy::modernize
317

source code of clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp