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_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
171namespace {
172AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES(
173 DeclRefExpr, DependentScopeDeclRefExpr)) {
174 const IdentifierInfo *Ident = getName(Node).getAsIdentifierInfo();
175 return Ident && Ident->isStr("value");
176}
177
178AST_MATCHER(TypeLoc, isType) {
179 if (auto TL = Node.getAs<TypedefTypeLoc>()) {
180 const auto *TD = TL.getDecl();
181 return TD->getDeclName().isIdentifier() && TD->getName() == "type";
182 }
183 if (auto TL = Node.getAs<DependentNameTypeLoc>())
184 return TL.getTypePtr()->getIdentifier()->getName() == "type";
185 return false;
186}
187} // namespace
188
189static constexpr char Bind[] = "";
190
191void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) {
192 // Only register matchers for trait<...>::value in c++17 mode.
193 if (getLangOpts().CPlusPlus17) {
194 Finder->addMatcher(mapAnyOf(declRefExpr, dependentScopeDeclRefExpr)
195 .with(isValue())
196 .bind(Bind),
197 this);
198 }
199 Finder->addMatcher(typeLoc(isType()).bind(Bind), this);
200}
201
202static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND,
203 const llvm::StringSet<> &Set) {
204 return ND->isInStdNamespace() && ND->getDeclName().isIdentifier() &&
205 Set.contains(ND->getName());
206}
207
208static bool checkTemplatedDecl(NestedNameSpecifier NNS,
209 const llvm::StringSet<> &Set) {
210 if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
211 return false;
212 const auto *TST = NNS.getAsType()->getAs<TemplateSpecializationType>();
213 if (!TST)
214 return false;
215 if (const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) {
216 return isNamedDeclInStdTraitsSet(TD, Set);
217 }
218 return false;
219}
220
222 : ClangTidyCheck(Name, Context),
223 IgnoreMacros(Options.get("IgnoreMacros", false)) {}
224
225void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
226 auto EmitValueWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc,
227 SourceLocation EndLoc) {
228 SourceLocation TemplateNameEndLoc;
229 if (auto TSTL =
230 QualLoc.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>())
231 TemplateNameEndLoc = Lexer::getLocForEndOfToken(
232 TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
233 Result.Context->getLangOpts());
234 else
235 return;
236
237 if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() ||
238 TemplateNameEndLoc.isMacroID()) {
239 if (IgnoreMacros)
240 return;
241 diag(QualLoc.getBeginLoc(), "use c++17 style variable templates");
242 return;
243 }
244 diag(QualLoc.getBeginLoc(), "use c++17 style variable templates")
245 << FixItHint::CreateInsertion(TemplateNameEndLoc, "_v")
246 << FixItHint::CreateRemoval({QualLoc.getEndLoc(), EndLoc});
247 };
248
249 auto EmitTypeWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc,
250 SourceLocation EndLoc,
251 SourceLocation TypenameLoc) {
252 SourceLocation TemplateNameEndLoc;
253 if (auto TSTL =
254 QualLoc.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>())
255 TemplateNameEndLoc = Lexer::getLocForEndOfToken(
256 TSTL.getTemplateNameLoc(), 0, *Result.SourceManager,
257 Result.Context->getLangOpts());
258 else
259 return;
260
261 if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() ||
262 TemplateNameEndLoc.isMacroID() || TypenameLoc.isMacroID()) {
263 if (IgnoreMacros)
264 return;
265 diag(QualLoc.getBeginLoc(), "use c++14 style type templates");
266 return;
267 }
268 auto Diag = diag(QualLoc.getBeginLoc(), "use c++14 style type templates");
269
270 if (TypenameLoc.isValid())
271 Diag << FixItHint::CreateRemoval(TypenameLoc);
272 Diag << FixItHint::CreateInsertion(TemplateNameEndLoc, "_t")
273 << FixItHint::CreateRemoval({QualLoc.getEndLoc(), EndLoc});
274 };
275
276 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>(Bind)) {
277 if (!DRE->hasQualifier())
278 return;
279 if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
280 DRE->getQualifier().getAsRecordDecl())) {
282 EmitValueWarning(DRE->getQualifierLoc(), DRE->getEndLoc());
283 }
284 return;
285 }
286
287 if (const auto *TL = Result.Nodes.getNodeAs<TypedefTypeLoc>(Bind)) {
288 const NestedNameSpecifierLoc QualLoc = TL->getQualifierLoc();
289 NestedNameSpecifier NNS = QualLoc.getNestedNameSpecifier();
290 if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
291 NNS.getAsRecordDecl())) {
293 EmitTypeWarning(TL->getQualifierLoc(), TL->getEndLoc(),
294 TL->getElaboratedKeywordLoc());
295 }
296 return;
297 }
298
299 if (const auto *DSDRE =
300 Result.Nodes.getNodeAs<DependentScopeDeclRefExpr>(Bind)) {
301 if (checkTemplatedDecl(DSDRE->getQualifier(), ValueTraits))
302 EmitValueWarning(DSDRE->getQualifierLoc(), DSDRE->getEndLoc());
303 return;
304 }
305
306 if (const auto *DNTL = Result.Nodes.getNodeAs<DependentNameTypeLoc>(Bind)) {
307 NestedNameSpecifierLoc QualLoc = DNTL->getQualifierLoc();
308 if (checkTemplatedDecl(QualLoc.getNestedNameSpecifier(), TypeTraits))
309 EmitTypeWarning(QualLoc, DNTL->getEndLoc(),
310 DNTL->getElaboratedKeywordLoc());
311 return;
312 }
313}
314
316 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
317}
318} // 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