319 lines · cpp
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-exception6//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 not20// 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} // namespace188 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 221TypeTraitsCheck::TypeTraitsCheck(StringRef Name, ClangTidyContext *Context)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 else235 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 else259 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())) {281 if (isNamedDeclInStdTraitsSet(CTSD, ValueTraits))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 const NestedNameSpecifier NNS = QualLoc.getNestedNameSpecifier();290 if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(291 NNS.getAsRecordDecl())) {292 if (isNamedDeclInStdTraitsSet(CTSD, TypeTraits))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 const NestedNameSpecifierLoc QualLoc = DNTL->getQualifierLoc();308 if (checkTemplatedDecl(QualLoc.getNestedNameSpecifier(), TypeTraits))309 EmitTypeWarning(QualLoc, DNTL->getEndLoc(),310 DNTL->getElaboratedKeywordLoc());311 return;312 }313}314 315void TypeTraitsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {316 Options.store(Opts, "IgnoreMacros", IgnoreMacros);317}318} // namespace clang::tidy::modernize319