blob: 6078013166d46152c343103cf6cac9c3f766c07e [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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 {
// FIXME: Add chrono::treat_as_floating_point_v and chrono::is_clock_v.
// This will require restructuring the code to handle type traits not
// defined directly in std.
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_bind_expression",
"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_error_code_enum",
"is_error_condition_enum",
"is_execution_policy",
"is_final",
"is_floating_point",
"is_function",
"is_fundamental",
"is_implicit_lifetime",
"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_relocatable",
"is_nothrow_swappable",
"is_nothrow_swappable_with",
"is_null_pointer",
"is_object",
"is_placeholder",
"is_pointer",
"is_pointer_interconvertible_base_of",
"is_polymorphic",
"is_reference",
"is_replaceable",
"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_trivially_relocatable",
"is_unbounded_array",
"is_union",
"is_unsigned",
"is_virtual_base_of",
"is_void",
"is_volatile",
"negation",
"rank",
"ratio_equal",
"ratio_greater_equal",
"ratio_greater",
"ratio_less_equal",
"ratio_less",
"ratio_not_equal",
"reference_constructs_from_temporary",
"reference_converts_from_temporary",
"tuple_size",
"uses_allocator",
"variant_size",
};
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",
"compare_three_way_result",
"common_comparison_category",
"unwrap_ref_decay",
"unwrap_reference",
"tuple_element",
"variant_alternative",
};
static DeclarationName getName(const DependentScopeDeclRefExpr &D) {
return D.getDeclName();
}
static DeclarationName getName(const DeclRefExpr &D) {
return D.getDecl()->getDeclName();
}
namespace {
AST_POLYMORPHIC_MATCHER(isValue, AST_POLYMORPHIC_SUPPORTED_TYPES(
DeclRefExpr, DependentScopeDeclRefExpr)) {
const IdentifierInfo *Ident = getName(Node).getAsIdentifierInfo();
return Ident && Ident->isStr("value");
}
AST_MATCHER(TypeLoc, isType) {
if (auto TL = Node.getAs<TypedefTypeLoc>()) {
const auto *TD = TL.getDecl();
return TD->getDeclName().isIdentifier() && TD->getName() == "type";
}
if (auto TL = Node.getAs<DependentNameTypeLoc>())
return TL.getTypePtr()->getIdentifier()->getName() == "type";
return false;
}
} // namespace
static constexpr char Bind[] = "";
void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) {
// 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(typeLoc(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(NestedNameSpecifier NNS,
const llvm::StringSet<> &Set) {
if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
return false;
const auto *TST = NNS.getAsType()->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.get("IgnoreMacros", false)) {}
void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) {
auto EmitValueWarning = [this, &Result](const NestedNameSpecifierLoc &QualLoc,
SourceLocation EndLoc) {
SourceLocation TemplateNameEndLoc;
if (auto TSTL =
QualLoc.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>())
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.getAsTypeLoc().getAs<TemplateSpecializationTypeLoc>())
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 *TL = Result.Nodes.getNodeAs<TypedefTypeLoc>(Bind)) {
const NestedNameSpecifierLoc QualLoc = TL->getQualifierLoc();
NestedNameSpecifier NNS = QualLoc.getNestedNameSpecifier();
if (const auto *CTSD = dyn_cast_if_present<ClassTemplateSpecializationDecl>(
NNS.getAsRecordDecl())) {
if (isNamedDeclInStdTraitsSet(CTSD, TypeTraits))
EmitTypeWarning(TL->getQualifierLoc(), TL->getEndLoc(),
TL->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