diff --git a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp --- a/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseTrailingReturnTypeCheck.cpp @@ -404,15 +404,18 @@ const auto *Fr = Result.Nodes.getNodeAs("Friend"); assert(F && "Matcher is expected to find only FunctionDecls"); - if (F->getLocation().isInvalid()) + // operator<=> generates also the AST of e.g. operator==, which is implicit + if (F->getLocation().isInvalid() || F->isImplicit()) return; - // Skip functions which return just 'auto'. - const auto *AT = F->getDeclaredReturnType()->getAs(); - if (AT != nullptr && !AT->isConstrained() && - AT->getKeyword() == AutoTypeKeyword::Auto && - !hasAnyNestedLocalQualifiers(F->getDeclaredReturnType())) - return; + { // Skip functions which return just 'auto' or are also defaulted operators. + const auto *AT = F->getDeclaredReturnType()->getAs(); + if (AT != nullptr && + ((!AT->isConstrained() && AT->getKeyword() == AutoTypeKeyword::Auto && + !hasAnyNestedLocalQualifiers(F->getDeclaredReturnType())) || + F->isDefaulted())) + return; + } // TODO: implement those if (F->getDeclaredReturnType()->isFunctionPointerType() || diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type-cxx20.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type-cxx20.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize-use-trailing-return-type-cxx20.cpp @@ -52,3 +52,50 @@ T req2(T t) requires requires { t + t; }; // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a trailing return type for this function [modernize-use-trailing-return-type] // CHECK-FIXES: {{^}}auto req2(T t) -> T requires requires { t + t; };{{$}} + +// +// Operator c++20 defaulted comparison operators +// +// Requires + +namespace std { + struct strong_ordering { + using value_type = signed char; + static strong_ordering const less; + static strong_ordering const equal; + static strong_ordering const equivalent; + static strong_ordering const greater; + + constexpr strong_ordering(value_type v) : val(v) {} + template + requires(T{0}) // + friend constexpr auto + operator==(strong_ordering v, T u) noexcept -> bool { + return v.val == u; + } + friend constexpr auto operator==(strong_ordering v, strong_ordering w) noexcept -> bool = default; + + value_type val{}; + }; + inline constexpr strong_ordering strong_ordering::less{-1}; + inline constexpr strong_ordering strong_ordering::equal{0}; + inline constexpr strong_ordering strong_ordering::equivalent{0}; + inline constexpr strong_ordering strong_ordering::greater{1}; + +} // namespace std + +struct TestDefaultOperatorA { + int a{}; + int b{}; + + friend auto operator<=>(const TestDefaultOperatorA &, const TestDefaultOperatorA &) noexcept = default; +}; + +struct TestDefaultOperatorB { + int a{}; + int b{}; + friend auto operator==(const TestDefaultOperatorB &, const TestDefaultOperatorB &) noexcept -> bool = default; + friend bool operator<(const TestDefaultOperatorB &, const TestDefaultOperatorB &) noexcept = default; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use a trailing return type for this function [modernize-use-trailing-return-type] + // CHECK-FIXES: {{^}} friend auto operator<(const TestDefaultOperatorB &, const TestDefaultOperatorB &) noexcept -> bool = default;{{$}} +};