diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp index bbb35228ce47fb..fd5dc70f6d0fbc 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp @@ -25,9 +25,11 @@ AST_MATCHER_P(QualType, possiblyPackExpansionOf, return InnerMatcher.matches(Node.getNonPackExpansionType(), Finder, Builder); } -AST_MATCHER(ParmVarDecl, isTemplateTypeParameter) { +AST_MATCHER_P(ParmVarDecl, isForwardingReferenceType, + ast_matchers::internal::Matcher, + InnerMatcher) { ast_matchers::internal::Matcher Inner = possiblyPackExpansionOf( - qualType(rValueReferenceType(), + qualType(rValueReferenceType(InnerMatcher), references(templateTypeParmType( hasDeclaration(templateTypeParmDecl()))), unless(references(qualType(isConstQualified()))))); @@ -86,6 +88,15 @@ AST_MATCHER(VarDecl, hasIdentifier) { } // namespace +MissingStdForwardCheck::MissingStdForwardCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IgnoreStaticCasts(Options.get("IgnoreStaticCasts", false)) {} + +void MissingStdForwardCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoreStaticCasts", IgnoreStaticCasts); +} + void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) { auto RefToParmImplicit = allOf( equalsBoundNode("var"), hasInitializer(ignoringParenImpCasts( @@ -129,15 +140,25 @@ void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) { unless(anyOf(hasAncestor(typeLoc()), hasAncestor(expr(hasUnevaluatedContext()))))); + auto StaticCast = + IgnoreStaticCasts + ? cxxStaticCastExpr( + hasDestinationType(lValueReferenceType( + pointee(type(equalsBoundNode("qtype"))))), + hasSourceExpression(declRefExpr(to(equalsBoundNode("param"))))) + : cxxStaticCastExpr(unless(anything())); + Finder->addMatcher( parmVarDecl( parmVarDecl().bind("param"), hasIdentifier(), - unless(hasAttr(attr::Kind::Unused)), isTemplateTypeParameter(), + unless(hasAttr(attr::Kind::Unused)), + isForwardingReferenceType(pointee(type().bind("qtype"))), hasAncestor(functionDecl().bind("func")), hasAncestor(functionDecl( isDefinition(), equalsBoundNode("func"), ToParam, - unless(anyOf(isDeleted(), - hasDescendant(std::move(ForwardCallMatcher))))))), + unless(anyOf(isDeleted(), hasDescendant(expr( + anyOf(std::move(ForwardCallMatcher), + std::move(StaticCast))))))))), this); } diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.h index 5995ad588855ed..1989730c985310 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.h @@ -21,8 +21,9 @@ namespace clang::tidy::cppcoreguidelines { /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/missing-std-forward.html class MissingStdForwardCheck : public ClangTidyCheck { public: - MissingStdForwardCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + MissingStdForwardCheck(StringRef Name, ClangTidyContext *Context); + + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { @@ -31,6 +32,9 @@ class MissingStdForwardCheck : public ClangTidyCheck { std::optional getCheckTraversalKind() const override { return TK_IgnoreUnlessSpelledInSource; } + +private: + const bool IgnoreStaticCasts; }; } // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index b001a6ad446695..3250e083157aaa 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -104,6 +104,11 @@ New check aliases Changes in existing checks ^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Improved :doc:`cppcoreguidelines-missing-std-forward + ` check to allow + using ``static_cast`` to explicitly convey the intention of using a + forwarding reference as an lvalue reference.Removed checks + - Improved :doc:`modernize-use-std-format ` check to support replacing member function calls too. diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/missing-std-forward.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/missing-std-forward.rst index 0c311b59a5d5a4..72126ed7f0ad41 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/missing-std-forward.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/missing-std-forward.rst @@ -38,3 +38,12 @@ Example: This check implements `F.19 `_ from the C++ Core Guidelines. + +Options +------- + +.. option:: IgnoreStaticCasts + + Boolean flag to allow users who want to use the forwarding reference as an + lvalue reference to convey he intention by using ``static_cast(t)`` to + disable warning. Default value is `false`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward-casts.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward-casts.cpp new file mode 100644 index 00000000000000..1dfcb7cd9f7a52 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward-casts.cpp @@ -0,0 +1,76 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-missing-std-forward %t -- \ +// RUN: -config="{CheckOptions: \ +// RUN: {cppcoreguidelines-missing-std-forward.IgnoreStaticCasts: true }}" \ +// RUN: -- -fno-delayed-template-parsing + +// NOLINTBEGIN +namespace std { + +template struct remove_reference { using type = T; }; +template struct remove_reference { using type = T; }; +template struct remove_reference { using type = T; }; + +template using remove_reference_t = typename remove_reference::type; + +template constexpr T &&forward(remove_reference_t &t) noexcept; +template constexpr T &&forward(remove_reference_t &&t) noexcept; + +template using add_lvalue_reference_t = __add_lvalue_reference(T); + +} // namespace std +// NOLINTEND + +namespace in_static_cast { + +template +void to_lvalue_ref(T&& t) { + static_cast(t); +} + +template +void to_const_lvalue_ref(T&& t) { + static_cast(t); +} + +template +void to_rvalue_ref(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_value(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_const_float_lvalue_ref(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_float(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_dependent(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast>(t); +} + +template +void to_float_expanded(T&&... t) { + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + (static_cast(t), ...); +} + +template +void to_lvalue_ref_expanded(T&&... t) { + (static_cast(t), ...); +} + +} // namespace in_static_cast diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp index 8116db58c937d4..b61cfa0afa7b5f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp @@ -13,6 +13,8 @@ template constexpr T &&forward(remove_reference_t &t) noexcept; template constexpr T &&forward(remove_reference_t &&t) noexcept; template constexpr remove_reference_t &&move(T &&x); +template using add_lvalue_reference_t = __add_lvalue_reference(T); + } // namespace std // NOLINTEND @@ -211,3 +213,61 @@ template void unused_argument3(F&& _) {} } // namespace unused_arguments + +namespace in_static_cast { + +template +void to_lvalue_ref(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_const_lvalue_ref(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_rvalue_ref(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_value(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_const_float_lvalue_ref(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_float(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast(t); +} + +template +void to_dependent(T&& t) { + // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + static_cast>(t); +} + +template +void to_float_expanded(T&&... t) { + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + (static_cast(t), ...); +} + +template +void to_lvalue_ref_expanded(T&&... t) { + // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + (static_cast(t), ...); +} + +} // namespace in_static_cast