Skip to content

Commit 2cd2acc

Browse files
committed
[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
1 parent c10cbb2 commit 2cd2acc

File tree

3 files changed

+47
-8
lines changed

3 files changed

+47
-8
lines changed

clang-tools-extra/clang-tidy/misc/UnconventionalAssignOperatorCheck.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ namespace misc {
1818

1919
void UnconventionalAssignOperatorCheck::registerMatchers(
2020
ast_matchers::MatchFinder *Finder) {
21-
const auto HasGoodReturnType = cxxMethodDecl(returns(lValueReferenceType(
22-
pointee(unless(isConstQualified()),
23-
anyOf(autoType(), hasDeclaration(equalsBoundNode("class")))))));
21+
const auto HasGoodReturnType =
22+
cxxMethodDecl(returns(hasCanonicalType(lValueReferenceType(pointee(
23+
unless(isConstQualified()),
24+
anyOf(autoType(), hasDeclaration(equalsBoundNode("class"))))))));
2425

25-
const auto IsSelf = qualType(
26+
const auto IsSelf = qualType(hasCanonicalType(
2627
anyOf(hasDeclaration(equalsBoundNode("class")),
27-
referenceType(pointee(hasDeclaration(equalsBoundNode("class"))))));
28+
referenceType(pointee(hasDeclaration(equalsBoundNode("class")))))));
2829
const auto IsAssign =
2930
cxxMethodDecl(unless(anyOf(isDeleted(), isPrivate(), isImplicit())),
3031
hasName("operator="), ofClass(recordDecl().bind("class")))
@@ -37,9 +38,9 @@ void UnconventionalAssignOperatorCheck::registerMatchers(
3738
cxxMethodDecl(IsAssign, unless(HasGoodReturnType)).bind("ReturnType"),
3839
this);
3940

40-
const auto BadSelf = referenceType(
41+
const auto BadSelf = qualType(hasCanonicalType(referenceType(
4142
anyOf(lValueReferenceType(pointee(unless(isConstQualified()))),
42-
rValueReferenceType(pointee(isConstQualified()))));
43+
rValueReferenceType(pointee(isConstQualified()))))));
4344

4445
Finder->addMatcher(
4546
cxxMethodDecl(IsSelfAssign,

clang-tools-extra/docs/clang-tidy/checks/misc-unconventional-assign-operator.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Finds declarations of assign operators with the wrong return and/or argument
88
types and definitions with good return type but wrong ``return`` statements.
99

1010
* The return type must be ``Class&``.
11-
* Works with move-assign and assign by value.
11+
* The assignment may be from the class type by value, const lvalue
12+
reference, non-const rvalue reference, or from a completely different
13+
type (e.g. ``int``).
1214
* Private and deleted operators are ignored.
1315
* The operator must always return ``*this``.

clang-tools-extra/test/clang-tidy/checkers/misc-unconventional-assign-operator.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,39 @@ struct AssignmentCallAtReturn {
127127
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: operator=() should always return '*this'
128128
}
129129
};
130+
131+
// Check that no false positives are issued when using type aliases.
132+
struct TypeAlias {
133+
using Alias = TypeAlias;
134+
// This is correct and should not produce any warnings:
135+
Alias &operator=(const Alias &) { return *this; }
136+
137+
using AliasRef = Alias &;
138+
// So is this (assignments from other types are fine):
139+
AliasRef operator=(int) { return *this; }
140+
};
141+
142+
// Same check as above with typedef instead of using
143+
struct TypeAliasTypedef {
144+
typedef TypeAliasTypedef Alias;
145+
Alias &operator=(const Alias &) { return *this; }
146+
147+
typedef Alias &AliasRef;
148+
AliasRef operator=(int) { return *this; }
149+
};
150+
151+
// Same check as above for a template class
152+
template <typename T>
153+
struct TemplateTypeAlias {
154+
using Alias1 = TemplateTypeAlias &;
155+
using Alias2 = TemplateTypeAlias const &;
156+
Alias1 operator=(Alias2) { return *this; }
157+
158+
template <typename U>
159+
using Alias3 = TemplateTypeAlias<U>;
160+
Alias3<T> &operator=(int) { return *this; }
161+
162+
// Using a different type parameter in the return type should give a warning
163+
Alias3<TypeAlias::Alias> &operator=(double) { return *this; }
164+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator=() should return 'TemplateTypeAlias&' [misc-unconventional-assign-operator]
165+
};

0 commit comments

Comments
 (0)