blob: c0766395ec5ccc86546cda6433379bc7252f3d14 [file] [log] [blame]
//===--- 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