Index: clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp =================================================================== --- clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp +++ clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp @@ -18,13 +18,14 @@ void UnconventionalAssignOperatorCheck::registerMatchers( ast_matchers::MatchFinder *Finder) { - const auto HasGoodReturnType = cxxMethodDecl(returns(lValueReferenceType( - pointee(unless(isConstQualified()), - anyOf(autoType(), hasDeclaration(equalsBoundNode("class"))))))); + const auto HasGoodReturnType = + cxxMethodDecl(returns(hasCanonicalType(lValueReferenceType(pointee( + unless(isConstQualified()), + anyOf(autoType(), hasDeclaration(equalsBoundNode("class")))))))); - const auto IsSelf = qualType( + const auto IsSelf = qualType(hasCanonicalType( anyOf(hasDeclaration(equalsBoundNode("class")), - referenceType(pointee(hasDeclaration(equalsBoundNode("class")))))); + referenceType(pointee(hasDeclaration(equalsBoundNode("class"))))))); const auto IsAssign = cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())), hasName("operator="), ofClass(recordDecl().bind("class"))) @@ -37,9 +38,9 @@ cxxMethodDecl(IsAssign, unless(HasGoodReturnType)).bind("ReturnType"), this); - const auto BadSelf = referenceType( + const auto BadSelf = qualType(hasCanonicalType(referenceType( anyOf(lValueReferenceType(pointee(unless(isConstQualified()))), - rValueReferenceType(pointee(isConstQualified())))); + rValueReferenceType(pointee(isConstQualified())))))); Finder->addMatcher( cxxMethodDecl(IsSelfAssign, Index: clang-tools-extra/docs/clang-tidy/checks/misc-unconventional-assign-operator.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/misc-unconventional-assign-operator.rst +++ clang-tools-extra/docs/clang-tidy/checks/misc-unconventional-assign-operator.rst @@ -8,6 +8,8 @@ types and definitions with good return type but wrong ``return`` statements. * The return type must be ``Class&``. - * Works with move-assign and assign by value. + * The assignment may be from the class type by value, const lvalue + reference, or non-const rvalue reference, or from a completely different + type (e.g. ``int``). * Private and deleted operators are ignored. * The operator must always return ``*this``. Index: clang-tools-extra/test/clang-tidy/checkers/misc-unconventional-assign-operator.cpp =================================================================== --- clang-tools-extra/test/clang-tidy/checkers/misc-unconventional-assign-operator.cpp +++ clang-tools-extra/test/clang-tidy/checkers/misc-unconventional-assign-operator.cpp @@ -127,3 +127,30 @@ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: operator=() should always return '*this' } }; + +// Check that no false positives are issued when using type aliases +struct TypeAlias { + using Alias = TypeAlias; + // This is correct and should not produce any warnings: + Alias &operator=(const Alias &) { return *this; } + + using AliasRef = Alias &; + // So is this (assignments from other types are fine): + AliasRef operator=(int) { return *this; } +}; + +// Same check as above for a template class +template +struct TemplateTypeAlias { + using Alias1 = TemplateTypeAlias &; + using Alias2 = TemplateTypeAlias const &; + Alias1 operator=(Alias2) { return *this; } + + template + using Alias3 = TemplateTypeAlias; + Alias3 &operator=(int) { return *this; } + + // Using a different type parameter in the return type should give a warning + Alias3& operator=(double) { return *this; } + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'TemplateTypeAlias&' [misc-unconventional-assign-operator] +};