[clang-tidy] Fix false positives involving type aliases in `misc-unconventional-assign-operator` check

clang-tidy currently reports false positives even for simple cases such as:
```
struct S {
    using X = S;
    X &operator=(const X&) { return *this; }
};
```
This is due to the fact that the `misc-unconventional-assign-operator` check fails to look at the //canonical// types. This patch fixes this behavior.

Reviewed By: aaron.ballman, mizvekov

Differential Revision: https://reviews.llvm.org/D114197
This commit is contained in:
Fabian Wolff 2022-01-17 21:10:22 +01:00
parent c10cbb243c
commit 2cd2accc61
3 changed files with 47 additions and 8 deletions

View File

@ -18,13 +18,14 @@ namespace misc {
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 @@ void UnconventionalAssignOperatorCheck::registerMatchers(
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,

View File

@ -8,6 +8,8 @@ Finds declarations of assign operators with the wrong return and/or argument
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, 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``.

View File

@ -127,3 +127,39 @@ struct AssignmentCallAtReturn {
// 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 with typedef instead of using
struct TypeAliasTypedef {
typedef TypeAliasTypedef Alias;
Alias &operator=(const Alias &) { return *this; }
typedef Alias &AliasRef;
AliasRef operator=(int) { return *this; }
};
// Same check as above for a template class
template <typename T>
struct TemplateTypeAlias {
using Alias1 = TemplateTypeAlias &;
using Alias2 = TemplateTypeAlias const &;
Alias1 operator=(Alias2) { return *this; }
template <typename U>
using Alias3 = TemplateTypeAlias<U>;
Alias3<T> &operator=(int) { return *this; }
// Using a different type parameter in the return type should give a warning
Alias3<TypeAlias::Alias> &operator=(double) { return *this; }
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'TemplateTypeAlias&' [misc-unconventional-assign-operator]
};