clang-tools 22.0.0git
TypeTraitsCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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_rvalue_reference",
87 "is_same",
88 "is_scalar",
89 "is_scoped_enum",
90 "is_signed",
91 "is_standard_layout",
92 "is_swappable",
93 "is_swappable_with",
94 "is_trivial",
95 "is_trivially_assignable",
96 "is_trivially_constructible",
97 "is_trivially_copy_assignable",
98 "is_trivially_copy_constructible",
99 "is_trivially_copyable",
100 "is_trivially_default_constructible",
101 "is_trivially_destructible",
102 "is_trivially_move_assignable",
103 "is_trivially_move_constructible",
104 "is_trivially_relocatable",
105 "is_unbounded_array",
106 "is_union",
107 "is_unsigned",
108 "is_virtual_base_of",
109 "is_void",
110 "is_volatile",
111 "negation",
112 "rank",
113 "ratio_equal",
114 "ratio_greater_equal",
115 "ratio_greater",
116 "ratio_less_equal",
117 "ratio_less",
118 "ratio_not_equal",
119 "reference_constructs_from_temporary",
120 "reference_converts_from_temporary",
121 "tuple_size",
122 "uses_allocator",
123 "variant_size",
124};
125
126static const llvm::StringSet<> TypeTraits = {
127 "remove_cv",
128 "remove_const",
129 "remove_volatile",
130 "add_cv",
131 "add_const",
132 "add_volatile",
133 "remove_reference",
134 "add_lvalue_reference",
135 "add_rvalue_reference",
136 "remove_pointer",
137 "add_pointer",
138 "make_signed",
139 "make_unsigned",
140 "remove_extent",
141 "remove_all_extents",
142 "aligned_storage",
143 "aligned_union",
144 "decay",
145 "remove_cvref",
146 "enable_if",
147 "conditional",
148 "common_type",
149 "common_reference",
150 "underlying_type",
151 "result_of",
152 "invoke_result",
153 "type_identity",
154 "compare_three_way_result",
155 "common_comparison_category",
156 "unwrap_ref_decay",
157 "unwrap_reference",
158 "tuple_element",
159 "variant_alternative",
160};
161
162static DeclarationName getName(const DependentScopeDeclRefExpr &D) {
163 return D.getDeclName();
164}
165
166static DeclarationName getName(const DeclRefExpr &D) {
167 return D.getDecl()->getDeclName();
168}
169
170namespace {
171AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES(
172 DeclRefExpr, DependentScopeDeclRefExpr)) {
173 const IdentifierInfo *Ident = getName(Node).getAsIdentifierInfo();
174 return Ident && Ident->isStr("value");
175}
176
177AST_MATCHER(TypeLoc, isType) {
178 if (auto TL = Node.getAs<TypedefTypeLoc>()) {
179 const auto *TD = TL.getDecl();
180 return TD->getDeclName().isIdentifier() && TD->getName() == "type";
181 }
182 if (auto TL = Node.getAs<DependentNameTypeLoc>())
183 return TL.getTypePtr()->getIdentifier()->getName() == "type";
184 return false;
185}
186} // namespace
187
188static constexpr char Bind[] = "";
189
190void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) {
191 // Only register matchers for trait<...>::value in c++17 mode.
192 if (getLangOpts().CPlusPlus17) {
193 Finder->addMatcher(mapAnyOf(declRefExpr, dependentScopeDeclRefExpr)
194 .with(isValue())
195 .bind(Bind),
196 this);
197 }
198 Finder->addMatcher(typeLoc(isType()).bind(Bind), this);
199}
200
201static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND,
202 const llvm::StringSet<> &Set) {
203 return ND->isInStdNamespace() && ND->getDeclName().isIdentifier() &&
204 Set.contains(ND->getName());
205}
206
207static bool checkTemplatedDecl(NestedNameSpecifier NNS,
208 const llvm::StringSet<> &Set) {
209 if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
210 return false;
211 const auto *TST = NNS.getAsType()->getAs<TemplateSpecializationType>();
212 if (!TST)
213 return false;
214 if (const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) {
215 return isNamedDeclInStdTraitsSet(TD, Set);
216 }
217 return false;
218}
219
221 : ClangTidyCheck(Name, Context),
222 IgnoreMacros(Options.get("IgnoreMacros", false)) {}
223
224void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
225 auto EmitValueWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc,
226 SourceLocation EndLoc) {
227 SourceLocation TemplateNameEndLoc;
228 if (auto TSTL =
229 QualLoc.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>())
230 TemplateNameEndLoc = Lexer::getLocForEndOfToken(
231 TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
232 Result.Context->getLangOpts());
233 else
234 return;
235
236 if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() ||
237 TemplateNameEndLoc.isMacroID()) {
238 if (IgnoreMacros)
239 return;
240 diag(QualLoc.getBeginLoc(), "use c++17 style variable templates");
241 return;
242 }
243 diag(QualLoc.getBeginLoc(), "use c++17 style variable templates")
244 << FixItHint::CreateInsertion(TemplateNameEndLoc, "_v")
245 << FixItHint::CreateRemoval({QualLoc.getEndLoc(), EndLoc});
246 };
247
248 auto EmitTypeWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc,
249 SourceLocation EndLoc,
250 SourceLocation TypenameLoc) {
251 SourceLocation TemplateNameEndLoc;
252 if (auto TSTL =
253 QualLoc.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>())
254 TemplateNameEndLoc = Lexer::getLocForEndOfToken(
255 TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
256 Result.Context->getLangOpts());
257 else
258 return;
259
260 if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() ||
261 TemplateNameEndLoc.isMacroID() || TypenameLoc.isMacroID()) {
262 if (IgnoreMacros)
263 return;
264 diag(QualLoc.getBeginLoc(), "use c++14 style type templates");
265 return;
266 }
267 auto Diag = diag(QualLoc.getBeginLoc(), "use c++14 style type templates");
268
269 if (TypenameLoc.isValid())
270 Diag << FixItHint::CreateRemoval(TypenameLoc);
271 Diag << FixItHint::CreateInsertion(TemplateNameEndLoc, "_t")
272 << FixItHint::CreateRemoval({QualLoc.getEndLoc(), EndLoc});
273 };
274
275 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>(Bind)) {
276 if (!DRE->hasQualifier())
277 return;
278 if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
279 DRE->getQualifier().getAsRecordDecl())) {
281 EmitValueWarning(DRE->getQualifierLoc(), DRE->getEndLoc());
282 }
283 return;
284 }
285
286 if (const auto *TL = Result.Nodes.getNodeAs<TypedefTypeLoc>(Bind)) {
287 const NestedNameSpecifierLoc QualLoc = TL->getQualifierLoc();
288 const NestedNameSpecifier NNS = QualLoc.getNestedNameSpecifier();
289 if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
290 NNS.getAsRecordDecl())) {
292 EmitTypeWarning(TL->getQualifierLoc(), TL->getEndLoc(),
293 TL->getElaboratedKeywordLoc());
294 }
295 return;
296 }
297
298 if (const auto *DSDRE =
299 Result.Nodes.getNodeAs<DependentScopeDeclRefExpr>(Bind)) {
300 if (checkTemplatedDecl(DSDRE->getQualifier(), ValueTraits))
301 EmitValueWarning(DSDRE->getQualifierLoc(), DSDRE->getEndLoc());
302 return;
303 }
304
305 if (const auto *DNTL = Result.Nodes.getNodeAs<DependentNameTypeLoc>(Bind)) {
306 const NestedNameSpecifierLoc QualLoc = DNTL->getQualifierLoc();
307 if (checkTemplatedDecl(QualLoc.getNestedNameSpecifier(), TypeTraits))
308 EmitTypeWarning(QualLoc, DNTL->getEndLoc(),
309 DNTL->getElaboratedKeywordLoc());
310 return;
311 }
312}
313
315 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
316}
317} // namespace clang::tidy::modernize
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
TypeTraitsCheck(StringRef Name, ClangTidyContext *Context)
AST_POLYMORPHIC_MATCHER(isInAbseilFile, AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt, TypeLoc, NestedNameSpecifierLoc))
Matches AST nodes that were found within Abseil files.
static constexpr char Bind[]
static bool checkTemplatedDecl(NestedNameSpecifier NNS, const llvm::StringSet<> &Set)
static DeclarationName getName(const DependentScopeDeclRefExpr &D)
static const llvm::StringSet ValueTraits
static const llvm::StringSet TypeTraits
static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND, const llvm::StringSet<> &Set)
llvm::StringMap< ClangTidyValue > OptionMap