diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -25,6 +25,7 @@ ReplaceRandomShuffleCheck.cpp ReturnBracedInitListCheck.cpp ShrinkToFitCheck.cpp + TypeTraitsCheck.cpp UnaryStaticAssertCheck.cpp UseAutoCheck.cpp UseBoolLiteralsCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -26,6 +26,7 @@ #include "ReplaceRandomShuffleCheck.h" #include "ReturnBracedInitListCheck.h" #include "ShrinkToFitCheck.h" +#include "TypeTraitsCheck.h" #include "UnaryStaticAssertCheck.h" #include "UseAutoCheck.h" #include "UseBoolLiteralsCheck.h" @@ -77,6 +78,7 @@ CheckFactories.registerCheck( "modernize-return-braced-init-list"); CheckFactories.registerCheck("modernize-shrink-to-fit"); + CheckFactories.registerCheck("modernize-type-traits"); CheckFactories.registerCheck( "modernize-unary-static-assert"); CheckFactories.registerCheck("modernize-use-auto"); diff --git a/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.h b/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.h @@ -0,0 +1,41 @@ +//===--- TypeTraitsCheck.h - clang-tidy -------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_TYPETRAITSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_TYPETRAITSCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// Convert type traits from the form `trait<...>::value` into `trait_v<...>` +/// The template variable traits from C++17 can improve compile times, as in +/// modern library implementations they don't instantiate structs. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/type-traits.html +class TypeTraitsCheck : public ClangTidyCheck { +public: + TypeTraitsCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus17; + } + +private: + llvm::DenseSet ValueTypeTraits; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_TYPETRAITSCHECK_H diff --git a/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp b/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.cpp @@ -0,0 +1,101 @@ +//===--- 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 "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/FixIt.h" +#include "llvm/Support/Regex.h" +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +const auto DefaultValueTypeTraits = + "std::alignment_of;std::conjunction;std::disjunction;std::extent;std::has_" + "unique_object_representations;std::has_virtual_destructor;std::is_" + "abstract;std::is_aggregate;std::is_arithmetic;std::is_array;std::is_" + "assignable;std::is_base_of;std::is_bounded_array;std::is_class;std::is_" + "compound;std::is_const;std::is_constructible;std::is_convertible;std::is_" + "copy_assignable;std::is_copy_constructible;std::is_default_constructible;" + "std::is_destructible;std::is_empty;std::is_enum;std::is_final;std::is_" + "floating_point;std::is_function;std::is_fundamental;std::is_integral;std::" + "is_invocable;std::is_invocable_r;std::is_layout_compatible;std::is_lvalue_" + "reference;std::is_member_function_pointer;std::is_member_object_pointer;" + "std::is_member_pointer;std::is_move_assignable;std::is_move_constructible;" + "std::is_nothrow_assignable;std::is_nothrow_constructible;std::is_nothrow_" + "convertible;std::is_nothrow_copy_assignable;std::is_nothrow_copy_" + "constructible;std::is_nothrow_default_constructible;std::is_nothrow_" + "destructible;std::is_nothrow_invocable;std::is_nothrow_invocable_r;std::" + "is_nothrow_move_assignable;std::is_nothrow_move_constructible;std::is_" + "nothrow_swappable;std::is_nothrow_swappable_with;std::is_null_pointer;std:" + ":is_object;std::is_pointer;std::is_pointer_interconvertible_base_of;std::" + "is_polymorphic;std::is_reference;std::is_rvalue_reference;std::is_same;" + "std::is_scalar;std::is_scoped_enum;std::is_signed;std::is_standard_layout;" + "std::is_swappable;std::is_swappable_with;std::is_trivial;std::is_" + "trivially_assignable;std::is_trivially_constructible;std::is_trivially_" + "copy_assignable;std::is_trivially_copy_constructible;std::is_trivially_" + "copyable;std::is_trivially_default_constructible;std::is_trivially_" + "destructible;std::is_trivially_move_assignable;std::is_trivially_move_" + "constructible;std::is_unbounded_array;std::is_union;std::is_unsigned;std::" + "is_void;std::is_volatile;std::negation;std::rank;std::reference_" + "constructs_from_temporary;std::reference_converts_from_temporary"; + +TypeTraitsCheck::TypeTraitsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) { + auto VectorValueTypeTraits = utils::options::parseStringList( + Options.get("ValueTypeTraits", DefaultValueTypeTraits)); + ValueTypeTraits = llvm::DenseSet(VectorValueTypeTraits.begin(), + VectorValueTypeTraits.end()); +} + +void TypeTraitsCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + declRefExpr(to(varDecl(hasName("value")))).bind("memberExpr"), this); +} + +void TypeTraitsCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs("memberExpr"); + if (MatchedDecl) { + const auto *Qualifier = MatchedDecl->getQualifier(); + if (!Qualifier) + return; + const auto *RecordQualifier = + dyn_cast_if_present( + Qualifier->getAsRecordDecl()); + if (!RecordQualifier) + return; + auto TraitName = RecordQualifier->getQualifiedNameAsString(); + if (ValueTypeTraits.find(TraitName) == ValueTypeTraits.end()) + return; + + SourceLocation ValueLoc = MatchedDecl->getSourceRange().getEnd(); + auto IssuedDiag = diag(ValueLoc, "use c++17 style variable templates"); + + if (ValueLoc.isMacroID()) + return; + + IssuedDiag << FixItHint::CreateInsertion( + MatchedDecl->getQualifierLoc() + .getTypeLoc() + .getAs() + .getLAngleLoc(), + "_v") + << FixItHint::CreateRemoval( + {MatchedDecl->getQualifierLoc().getEndLoc(), + MatchedDecl->getNameInfo().getSourceRange().getEnd()}); + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -115,6 +115,13 @@ Warns when using ``do-while`` loops. +- New :doc:`modernize-type-traits + ` check. + + Finds usages of C++11 style type traits like ``std::is_same<...>::value`` and replaces + them with the C++17 variable template ``std::is_same_v<...>``. This transformation can + help reduce compile times. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -276,6 +276,7 @@ `modernize-replace-random-shuffle `_, "Yes" `modernize-return-braced-init-list `_, "Yes" `modernize-shrink-to-fit `_, "Yes" + `modernize-type-traits `_, "Yes" `modernize-unary-static-assert `_, "Yes" `modernize-use-auto `_, "Yes" `modernize-use-bool-literals `_, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/type-traits.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/type-traits.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/type-traits.rst @@ -0,0 +1,33 @@ +.. title:: clang-tidy - modernize-type-traits + +modernize-type-traits +===================== + + +Convert type traits from the form ``trait<...>::value`` into ``trait_v<...>`` +The template variable traits from C++17 can improve compile times, as in +modern library implementations they don't instantiate structs. + +For example, this: + +.. code-block:: c++ + + std::is_same::value + std::is_integral::value + +is replaced by: + +.. code-block:: c++ + + std::is_same_v + std::is_integral_v + +Options +------- + +.. option:: ValueTypeTraits + + Specifies a list of type traits which are used with a static ``::value`` + member for metaprogramming instead of the regular one of traits available + in the STL. This check assumes that a matching version exists with the + same name and a ``_v`` suffix. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/type-traits.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/type-traits.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/type-traits.cpp @@ -0,0 +1,73 @@ +// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-type-traits %t + +namespace std { + +template +struct integral_constant { + static constexpr inline T value = x; +}; + +template +struct is_same : integral_constant {}; + +template +struct is_trivially_default_constructible : integral_constant {}; + +template +struct alignment_of : integral_constant {}; + +} + + +template +void f() { + auto x = std::is_same::value; + auto y = std::is_trivially_default_constructible::value; + auto z = std::alignment_of::value; + +// CHECK-FIXES: auto x = std::is_same_v; +// CHECK-FIXES-NEXT: auto y = std::is_trivially_default_constructible_v; +// CHECK-FIXES-NEXT: auto z = std::alignment_of_v; +// Test that we don't get this twice or something weird like that because +// we're in a dependant context. +// CHECK-FIXES-NOT: auto x = std::is_same_v; +} + +void g() { + f(); + f(); +} + +void h() { + auto x = std::is_same::value; + auto y = std::is_trivially_default_constructible::value; + auto z = std::alignment_of::value; +// CHECK-FIXES: auto x = std::is_same_v; +// CHECK-FIXES-NEXT: auto y = std::is_trivially_default_constructible_v; +// CHECK-FIXES-NEXT: auto z = std::alignment_of_v; +} + + +template +struct NonDiagnosableTemplate : std::integral_constant {}; + +struct NonDiagnosable1 : std::integral_constant {}; +struct NonDiagnosable2 { + using value = int; +}; + +#define IN_A_MACRO std::is_same::value +void macro_use() { + bool a = IN_A_MACRO; +} +// Don't fix-it if we use it inside a macro. +// CHECK-FIXES: #define IN_A_MACRO std::is_same::value + +void test_no_diagnostic() { + auto x = NonDiagnosableTemplate::value; + auto y = NonDiagnosable1::value; + auto z = NonDiagnosable2::value(4); +// CHECK-FIXES: auto x = NonDiagnosableTemplate::value; +// CHECK-FIXES-NEXT: auto y = NonDiagnosable1::value; +// CHECK-FIXES-NEXT: auto z = NonDiagnosable2::value(4); +}