|  | //===--- TypeTraitsCheck.cpp - clang-tidy ---------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "TypeTraitsCheck.h" | 
|  | #include "clang/ASTMatchers/ASTMatchFinder.h" | 
|  | #include "clang/ASTMatchers/ASTMatchers.h" | 
|  | #include "clang/Lex/Lexer.h" | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  |  | 
|  | namespace clang::tidy::modernize { | 
|  |  | 
|  | static const llvm::StringSet<> ValueTraits = { | 
|  | "alignment_of", | 
|  | "conjunction", | 
|  | "disjunction", | 
|  | "extent", | 
|  | "has_unique_object_representations", | 
|  | "has_virtual_destructor", | 
|  | "is_abstract", | 
|  | "is_aggregate", | 
|  | "is_arithmetic", | 
|  | "is_array", | 
|  | "is_assignable", | 
|  | "is_base_of", | 
|  | "is_bounded_array", | 
|  | "is_class", | 
|  | "is_compound", | 
|  | "is_const", | 
|  | "is_constructible", | 
|  | "is_convertible", | 
|  | "is_copy_assignable", | 
|  | "is_copy_constructible", | 
|  | "is_default_constructible", | 
|  | "is_destructible", | 
|  | "is_empty", | 
|  | "is_enum", | 
|  | "is_final", | 
|  | "is_floating_point", | 
|  | "is_function", | 
|  | "is_fundamental", | 
|  | "is_integral", | 
|  | "is_invocable", | 
|  | "is_invocable_r", | 
|  | "is_layout_compatible", | 
|  | "is_lvalue_reference", | 
|  | "is_member_function_pointer", | 
|  | "is_member_object_pointer", | 
|  | "is_member_pointer", | 
|  | "is_move_assignable", | 
|  | "is_move_constructible", | 
|  | "is_nothrow_assignable", | 
|  | "is_nothrow_constructible", | 
|  | "is_nothrow_convertible", | 
|  | "is_nothrow_copy_assignable", | 
|  | "is_nothrow_copy_constructible", | 
|  | "is_nothrow_default_constructible", | 
|  | "is_nothrow_destructible", | 
|  | "is_nothrow_invocable", | 
|  | "is_nothrow_invocable_r", | 
|  | "is_nothrow_move_assignable", | 
|  | "is_nothrow_move_constructible", | 
|  | "is_nothrow_swappable", | 
|  | "is_nothrow_swappable_with", | 
|  | "is_null_pointer", | 
|  | "is_object", | 
|  | "is_pointer", | 
|  | "is_pointer_interconvertible_base_of", | 
|  | "is_polymorphic", | 
|  | "is_reference", | 
|  | "is_rvalue_reference", | 
|  | "is_same", | 
|  | "is_scalar", | 
|  | "is_scoped_enum", | 
|  | "is_signed", | 
|  | "is_standard_layout", | 
|  | "is_swappable", | 
|  | "is_swappable_with", | 
|  | "is_trivial", | 
|  | "is_trivially_assignable", | 
|  | "is_trivially_constructible", | 
|  | "is_trivially_copy_assignable", | 
|  | "is_trivially_copy_constructible", | 
|  | "is_trivially_copyable", | 
|  | "is_trivially_default_constructible", | 
|  | "is_trivially_destructible", | 
|  | "is_trivially_move_assignable", | 
|  | "is_trivially_move_constructible", | 
|  | "is_unbounded_array", | 
|  | "is_union", | 
|  | "is_unsigned", | 
|  | "is_void", | 
|  | "is_volatile", | 
|  | "negation", | 
|  | "rank", | 
|  | "reference_constructs_from_temporary", | 
|  | "reference_converts_from_temporary", | 
|  | }; | 
|  |  | 
|  | static const llvm::StringSet<> TypeTraits = { | 
|  | "remove_cv", | 
|  | "remove_const", | 
|  | "remove_volatile", | 
|  | "add_cv", | 
|  | "add_const", | 
|  | "add_volatile", | 
|  | "remove_reference", | 
|  | "add_lvalue_reference", | 
|  | "add_rvalue_reference", | 
|  | "remove_pointer", | 
|  | "add_pointer", | 
|  | "make_signed", | 
|  | "make_unsigned", | 
|  | "remove_extent", | 
|  | "remove_all_extents", | 
|  | "aligned_storage", | 
|  | "aligned_union", | 
|  | "decay", | 
|  | "remove_cvref", | 
|  | "enable_if", | 
|  | "conditional", | 
|  | "common_type", | 
|  | "common_reference", | 
|  | "underlying_type", | 
|  | "result_of", | 
|  | "invoke_result", | 
|  | "type_identity", | 
|  | }; | 
|  |  | 
|  | static DeclarationName getName(const DependentScopeDeclRefExpr &D) { | 
|  | return D.getDeclName(); | 
|  | } | 
|  |  | 
|  | static DeclarationName getName(const DeclRefExpr &D) { | 
|  | return D.getDecl()->getDeclName(); | 
|  | } | 
|  |  | 
|  | static bool isNamedType(const ElaboratedTypeLoc &ETL) { | 
|  | if (const auto *TFT = | 
|  | ETL.getNamedTypeLoc().getTypePtr()->getAs<TypedefType>()) { | 
|  | const TypedefNameDecl *Decl = TFT->getDecl(); | 
|  | return Decl->getDeclName().isIdentifier() && Decl->getName() == "type"; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool isNamedType(const DependentNameTypeLoc &DTL) { | 
|  | return DTL.getTypePtr()->getIdentifier()->getName() == "type"; | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES( | 
|  | DeclRefExpr, DependentScopeDeclRefExpr)) { | 
|  | const IdentifierInfo *Ident = getName(Node).getAsIdentifierInfo(); | 
|  | return Ident && Ident->isStr("value"); | 
|  | } | 
|  |  | 
|  | AST_POLYMORPHIC_MATCHER(isType, | 
|  | AST_POLYMORPHIC_SUPPORTED_TYPES(ElaboratedTypeLoc, | 
|  | DependentNameTypeLoc)) { | 
|  | return Node.getBeginLoc().isValid() && isNamedType(Node); | 
|  | } | 
|  | } // namespace | 
|  |  | 
|  | static constexpr char Bind[] = ""; | 
|  |  | 
|  | void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) { | 
|  | const ast_matchers::internal::VariadicDynCastAllOfMatcher< | 
|  | Stmt, | 
|  | DependentScopeDeclRefExpr> | 
|  | dependentScopeDeclRefExpr; // NOLINT(readability-identifier-naming) | 
|  | const ast_matchers::internal::VariadicDynCastAllOfMatcher< | 
|  | TypeLoc, | 
|  | DependentNameTypeLoc> | 
|  | dependentNameTypeLoc; // NOLINT(readability-identifier-naming) | 
|  |  | 
|  | // Only register matchers for trait<...>::value in c++17 mode. | 
|  | if (getLangOpts().CPlusPlus17) { | 
|  | Finder->addMatcher(mapAnyOf(declRefExpr, dependentScopeDeclRefExpr) | 
|  | .with(isValue()) | 
|  | .bind(Bind), | 
|  | this); | 
|  | } | 
|  | Finder->addMatcher(mapAnyOf(elaboratedTypeLoc, dependentNameTypeLoc) | 
|  | .with(isType()) | 
|  | .bind(Bind), | 
|  | this); | 
|  | } | 
|  |  | 
|  | static bool isNamedDeclInStdTraitsSet(const NamedDecl *ND, | 
|  | const llvm::StringSet<> &Set) { | 
|  | return ND->isInStdNamespace() && ND->getDeclName().isIdentifier() && | 
|  | Set.contains(ND->getName()); | 
|  | } | 
|  |  | 
|  | static bool checkTemplatedDecl(const NestedNameSpecifier *NNS, | 
|  | const llvm::StringSet<> &Set) { | 
|  | if (!NNS) | 
|  | return false; | 
|  | const Type *NNST = NNS->getAsType(); | 
|  | if (!NNST) | 
|  | return false; | 
|  | const auto *TST = NNST->getAs<TemplateSpecializationType>(); | 
|  | if (!TST) | 
|  | return false; | 
|  | if (const TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl()) { | 
|  | return isNamedDeclInStdTraitsSet(TD, Set); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | TypeTraitsCheck::TypeTraitsCheck(StringRef Name, ClangTidyContext *Context) | 
|  | : ClangTidyCheck(Name, Context), | 
|  | IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", false)) {} | 
|  |  | 
|  | void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) { | 
|  | auto EmitValueWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc, | 
|  | SourceLocation EndLoc) { | 
|  | SourceLocation TemplateNameEndLoc; | 
|  | if (auto TSTL = QualLoc.getTypeLoc().getAs<TemplateSpecializationTypeLoc>(); | 
|  | !TSTL.isNull()) | 
|  | TemplateNameEndLoc = Lexer::getLocForEndOfToken( | 
|  | TSTL.getTemplateNameLoc(), 0, *Result.SourceManager, | 
|  | Result.Context->getLangOpts()); | 
|  | else | 
|  | return; | 
|  |  | 
|  | if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() || | 
|  | TemplateNameEndLoc.isMacroID()) { | 
|  | if (IgnoreMacros) | 
|  | return; | 
|  | diag(QualLoc.getBeginLoc(), "use c++17 style variable templates"); | 
|  | return; | 
|  | } | 
|  | diag(QualLoc.getBeginLoc(), "use c++17 style variable templates") | 
|  | << FixItHint::CreateInsertion(TemplateNameEndLoc, "_v") | 
|  | << FixItHint::CreateRemoval({QualLoc.getEndLoc(), EndLoc}); | 
|  | }; | 
|  |  | 
|  | auto EmitTypeWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc, | 
|  | SourceLocation EndLoc, | 
|  | SourceLocation TypenameLoc) { | 
|  | SourceLocation TemplateNameEndLoc; | 
|  | if (auto TSTL = QualLoc.getTypeLoc().getAs<TemplateSpecializationTypeLoc>(); | 
|  | !TSTL.isNull()) | 
|  | TemplateNameEndLoc = Lexer::getLocForEndOfToken( | 
|  | TSTL.getTemplateNameLoc(), 0, *Result.SourceManager, | 
|  | Result.Context->getLangOpts()); | 
|  | else | 
|  | return; | 
|  |  | 
|  | if (EndLoc.isMacroID() || QualLoc.getEndLoc().isMacroID() || | 
|  | TemplateNameEndLoc.isMacroID() || TypenameLoc.isMacroID()) { | 
|  | if (IgnoreMacros) | 
|  | return; | 
|  | diag(QualLoc.getBeginLoc(), "use c++14 style type templates"); | 
|  | return; | 
|  | } | 
|  | auto Diag = diag(QualLoc.getBeginLoc(), "use c++14 style type templates"); | 
|  |  | 
|  | if (TypenameLoc.isValid()) | 
|  | Diag << FixItHint::CreateRemoval(TypenameLoc); | 
|  | Diag << FixItHint::CreateInsertion(TemplateNameEndLoc, "_t") | 
|  | << FixItHint::CreateRemoval({QualLoc.getEndLoc(), EndLoc}); | 
|  | }; | 
|  |  | 
|  | if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>(Bind)) { | 
|  | if (!DRE->hasQualifier()) | 
|  | return; | 
|  | if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>( | 
|  | DRE->getQualifier()->getAsRecordDecl())) { | 
|  | if (isNamedDeclInStdTraitsSet(CTSD, ValueTraits)) | 
|  | EmitValueWarning(DRE->getQualifierLoc(), DRE->getEndLoc()); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (const auto *ETL = Result.Nodes.getNodeAs<ElaboratedTypeLoc>(Bind)) { | 
|  | const NestedNameSpecifierLoc QualLoc = ETL->getQualifierLoc(); | 
|  | const auto *NNS = QualLoc.getNestedNameSpecifier(); | 
|  | if (!NNS) | 
|  | return; | 
|  | if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>( | 
|  | NNS->getAsRecordDecl())) { | 
|  | if (isNamedDeclInStdTraitsSet(CTSD, TypeTraits)) | 
|  | EmitTypeWarning(ETL->getQualifierLoc(), ETL->getEndLoc(), | 
|  | ETL->getElaboratedKeywordLoc()); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (const auto *DSDRE = | 
|  | Result.Nodes.getNodeAs<DependentScopeDeclRefExpr>(Bind)) { | 
|  | if (checkTemplatedDecl(DSDRE->getQualifier(), ValueTraits)) | 
|  | EmitValueWarning(DSDRE->getQualifierLoc(), DSDRE->getEndLoc()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (const auto *DNTL = Result.Nodes.getNodeAs<DependentNameTypeLoc>(Bind)) { | 
|  | NestedNameSpecifierLoc QualLoc = DNTL->getQualifierLoc(); | 
|  | if (checkTemplatedDecl(QualLoc.getNestedNameSpecifier(), TypeTraits)) | 
|  | EmitTypeWarning(QualLoc, DNTL->getEndLoc(), | 
|  | DNTL->getElaboratedKeywordLoc()); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TypeTraitsCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { | 
|  | Options.store(Opts, "IgnoreMacros", IgnoreMacros); | 
|  | } | 
|  | } // namespace clang::tidy::modernize |