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
18// FIXME: Add chrono::treat_as_floating_point_v and chrono::is_clock_v.
19// This will require restructuring the code to handle type traits not
20// defined directly in std.
21static const llvm::StringSet<> ValueTraits = {
22 "alignment_of",
23 "conjunction",
24 "disjunction",
25 "extent",
26 "has_unique_object_representations",
27 "has_virtual_destructor",
28 "is_abstract",
29 "is_aggregate",
30 "is_arithmetic",
31 "is_array",
32 "is_assignable",
33 "is_base_of",
34 "is_bind_expression",
35 "is_bounded_array",
36 "is_class",
37 "is_compound",
38 "is_const",
39 "is_constructible",
40 "is_convertible",
41 "is_copy_assignable",
42 "is_copy_constructible",
43 "is_default_constructible",
44 "is_destructible",
45 "is_empty",
46 "is_enum",
47 "is_error_code_enum",
48 "is_error_condition_enum",
49 "is_execution_policy",
50 "is_final",
51 "is_floating_point",
52 "is_function",
53 "is_fundamental",
54 "is_implicit_lifetime",
55 "is_integral",
56 "is_invocable",
57 "is_invocable_r",
58 "is_layout_compatible",
59 "is_lvalue_reference",
60 "is_member_function_pointer",
61 "is_member_object_pointer",
62 "is_member_pointer",
63 "is_move_assignable",
64 "is_move_constructible",
65 "is_nothrow_assignable",
66 "is_nothrow_constructible",
67 "is_nothrow_convertible",
68 "is_nothrow_copy_assignable",
69 "is_nothrow_copy_constructible",
70 "is_nothrow_default_constructible",
71 "is_nothrow_destructible",
72 "is_nothrow_invocable",
73 "is_nothrow_invocable_r",
74 "is_nothrow_move_assignable",
75 "is_nothrow_move_constructible",
76 "is_nothrow_relocatable",
77 "is_nothrow_swappable",
78 "is_nothrow_swappable_with",
79 "is_null_pointer",
80 "is_object",
81 "is_placeholder",
82 "is_pointer",
83 "is_pointer_interconvertible_base_of",
84 "is_polymorphic",
85 "is_reference",
86 "is_replaceable",
87 "is_rvalue_reference",
88 "is_same",
89 "is_scalar",
90 "is_scoped_enum",
91 "is_signed",
92 "is_standard_layout",
93 "is_swappable",
94 "is_swappable_with",
95 "is_trivial",
96 "is_trivially_assignable",
97 "is_trivially_constructible",
98 "is_trivially_copy_assignable",
99 "is_trivially_copy_constructible",
100 "is_trivially_copyable",
101 "is_trivially_default_constructible",
102 "is_trivially_destructible",
103 "is_trivially_move_assignable",
104 "is_trivially_move_constructible",
105 "is_trivially_relocatable",
106 "is_unbounded_array",
107 "is_union",
108 "is_unsigned",
109 "is_virtual_base_of",
110 "is_void",
111 "is_volatile",
112 "negation",
113 "rank",
114 "ratio_equal",
115 "ratio_greater_equal",
116 "ratio_greater",
117 "ratio_less_equal",
118 "ratio_less",
119 "ratio_not_equal",
120 "reference_constructs_from_temporary",
121 "reference_converts_from_temporary",
122 "tuple_size",
123 "uses_allocator",
124 "variant_size",
125};
126
127static const llvm::StringSet<> TypeTraits = {
128 "remove_cv",
129 "remove_const",
130 "remove_volatile",
131 "add_cv",
132 "add_const",
133 "add_volatile",
134 "remove_reference",
135 "add_lvalue_reference",
136 "add_rvalue_reference",
137 "remove_pointer",
138 "add_pointer",
139 "make_signed",
140 "make_unsigned",
141 "remove_extent",
142 "remove_all_extents",
143 "aligned_storage",
144 "aligned_union",
145 "decay",
146 "remove_cvref",
147 "enable_if",
148 "conditional",
149 "common_type",
150 "common_reference",
151 "underlying_type",
152 "result_of",
153 "invoke_result",
154 "type_identity",
155 "compare_three_way_result",
156 "common_comparison_category",
157 "unwrap_ref_decay",
158 "unwrap_reference",
159 "tuple_element",
160 "variant_alternative",
161};
162
163static DeclarationName getName(const DependentScopeDeclRefExpr &D) {
164 return D.getDeclName();
165}
166
167static DeclarationName getName(const DeclRefExpr &D) {
168 return D.getDecl()->getDeclName();
169}
170
171static bool isNamedType(const ElaboratedTypeLoc &ETL) {
172 if (const auto *TFT =
173 ETL.getNamedTypeLoc().getTypePtr()->getAs<TypedefType>()) {
174 const TypedefNameDecl *Decl = TFT->getDecl();
175 return Decl->getDeclName().isIdentifier() && Decl->getName() == "type";
176 }
177 return false;
178}
179
180static bool isNamedType(const DependentNameTypeLoc &DTL) {
181 return DTL.getTypePtr()->getIdentifier()->getName() == "type";
182}
183
184namespace {
185AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES(
186 DeclRefExpr, DependentScopeDeclRefExpr)) {
187 const IdentifierInfo *Ident = getName(Node).getAsIdentifierInfo();
188 return Ident && Ident->isStr(Str: "value");
189}
190
191AST_POLYMORPHIC_MATCHER(isType,
192 AST_POLYMORPHIC_SUPPORTED_TYPES(ElaboratedTypeLoc,
193 DependentNameTypeLoc)) {
194 return Node.getBeginLoc().isValid() && isNamedType(Node);
195}
196} // namespace
197
198static constexpr char Bind[] = "";
199
200void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) {
201 const ast_matchers::internal::VariadicDynCastAllOfMatcher<
202 Stmt,
203 DependentScopeDeclRefExpr>
204 dependentScopeDeclRefExpr; // NOLINT(readability-identifier-naming)
205 const ast_matchers::internal::VariadicDynCastAllOfMatcher<
206 TypeLoc,
207 DependentNameTypeLoc>
208 dependentNameTypeLoc; // NOLINT(readability-identifier-naming)
209
210 // Only register matchers for trait<...>::value in c++17 mode.
211 if (getLangOpts().CPlusPlus17) {
212 Finder->addMatcher(NodeMatch: mapAnyOf(declRefExpr, dependentScopeDeclRefExpr)
213 .with(isValue())
214 .bind(ID: Bind),
215 Action: this);
216 }
217 Finder->addMatcher(NodeMatch: mapAnyOf(elaboratedTypeLoc, dependentNameTypeLoc)
218 .with(isType())
219 .bind(ID: Bind),
220 Action: this);
221}
222
223static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND,
224 const llvm::StringSet<> &Set) {
225 return ND->isInStdNamespace() && ND->getDeclName().isIdentifier() &&
226 Set.contains(key: ND->getName());
227}
228
229static bool checkTemplatedDecl(const NestedNameSpecifier *NNS,
230 const llvm::StringSet<> &Set) {
231 if (!NNS)
232 return false;
233 const Type *NNST = NNS->getAsType();
234 if (!NNST)
235 return false;
236 const auto *TST = NNST->getAs<TemplateSpecializationType>();
237 if (!TST)
238 return false;
239 if (const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) {
240 return isNamedDeclInStdTraitsSet(ND: TD, Set);
241 }
242 return false;
243}
244
245TypeTraitsCheck::TypeTraitsCheck(StringRef Name, ClangTidyContext *Context)
246 : ClangTidyCheck(Name, Context),
247 IgnoreMacros(Options.getLocalOrGlobal(LocalName: "IgnoreMacros", Default: false)) {}
248
249void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
250 auto EmitValueWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc,
251 SourceLocation EndLoc) {
252 SourceLocation TemplateNameEndLoc;
253 if (auto TSTL = QualLoc.getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
254 !TSTL.isNull())
255 TemplateNameEndLoc = Lexer::getLocForEndOfToken(
256 Loc: TSTL.getTemplateNameLoc(), Offset: 0, SM: *Result.SourceManager,
257 LangOpts: Result.Context->getLangOpts());
258 else
259 return;
260
261 if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() ||
262 TemplateNameEndLoc.isMacroID()) {
263 if (IgnoreMacros)
264 return;
265 diag(Loc: QualLoc.getBeginLoc(), Description: "use c++17 style variable templates");
266 return;
267 }
268 diag(Loc: QualLoc.getBeginLoc(), Description: "use c++17 style variable templates")
269 << FixItHint::CreateInsertion(InsertionLoc: TemplateNameEndLoc, Code: "_v")
270 << FixItHint::CreateRemoval(RemoveRange: {QualLoc.getEndLoc(), EndLoc});
271 };
272
273 auto EmitTypeWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc,
274 SourceLocation EndLoc,
275 SourceLocation TypenameLoc) {
276 SourceLocation TemplateNameEndLoc;
277 if (auto TSTL = QualLoc.getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
278 !TSTL.isNull())
279 TemplateNameEndLoc = Lexer::getLocForEndOfToken(
280 Loc: TSTL.getTemplateNameLoc(), Offset: 0, SM: *Result.SourceManager,
281 LangOpts: Result.Context->getLangOpts());
282 else
283 return;
284
285 if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() ||
286 TemplateNameEndLoc.isMacroID() || TypenameLoc.isMacroID()) {
287 if (IgnoreMacros)
288 return;
289 diag(Loc: QualLoc.getBeginLoc(), Description: "use c++14 style type templates");
290 return;
291 }
292 auto Diag = diag(Loc: QualLoc.getBeginLoc(), Description: "use c++14 style type templates");
293
294 if (TypenameLoc.isValid())
295 Diag << FixItHint::CreateRemoval(RemoveRange: TypenameLoc);
296 Diag << FixItHint::CreateInsertion(InsertionLoc: TemplateNameEndLoc, Code: "_t")
297 << FixItHint::CreateRemoval(RemoveRange: {QualLoc.getEndLoc(), EndLoc});
298 };
299
300 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>(ID: Bind)) {
301 if (!DRE->hasQualifier())
302 return;
303 if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
304 Val: DRE->getQualifier()->getAsRecordDecl())) {
305 if (isNamedDeclInStdTraitsSet(ND: CTSD, Set: ValueTraits))
306 EmitValueWarning(DRE->getQualifierLoc(), DRE->getEndLoc());
307 }
308 return;
309 }
310
311 if (const auto *ETL = Result.Nodes.getNodeAs<ElaboratedTypeLoc>(ID: Bind)) {
312 const NestedNameSpecifierLoc QualLoc = ETL->getQualifierLoc();
313 const auto *NNS = QualLoc.getNestedNameSpecifier();
314 if (!NNS)
315 return;
316 if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
317 Val: NNS->getAsRecordDecl())) {
318 if (isNamedDeclInStdTraitsSet(ND: CTSD, Set: TypeTraits))
319 EmitTypeWarning(ETL->getQualifierLoc(), ETL->getEndLoc(),
320 ETL->getElaboratedKeywordLoc());
321 }
322 return;
323 }
324
325 if (const auto *DSDRE =
326 Result.Nodes.getNodeAs<DependentScopeDeclRefExpr>(ID: Bind)) {
327 if (checkTemplatedDecl(NNS: DSDRE->getQualifier(), Set: ValueTraits))
328 EmitValueWarning(DSDRE->getQualifierLoc(), DSDRE->getEndLoc());
329 return;
330 }
331
332 if (const auto *DNTL = Result.Nodes.getNodeAs<DependentNameTypeLoc>(ID: Bind)) {
333 NestedNameSpecifierLoc QualLoc = DNTL->getQualifierLoc();
334 if (checkTemplatedDecl(NNS: QualLoc.getNestedNameSpecifier(), Set: TypeTraits))
335 EmitTypeWarning(QualLoc, DNTL->getEndLoc(),
336 DNTL->getElaboratedKeywordLoc());
337 return;
338 }
339}
340
341void TypeTraitsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
342 Options.store(Options&: Opts, LocalName: "IgnoreMacros", Value: IgnoreMacros);
343}
344} // namespace clang::tidy::modernize
345

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