Skip to content

Commit 7a55021

Browse files
author
serge-sans-paille
committed
[clang-tidy] Fix confusable identifiers interaction with DeclContext
Properly checks enclosing DeclContext, and add the related test case. It would be great to be able to use Sema to check conflicting scopes, but that's not something clang-tidy seems to be able to do :-/ Fix llvm#56221 Differential Revision: https://reviews.llvm.org/D128715
1 parent f276729 commit 7a55021

File tree

2 files changed

+152
-16
lines changed

2 files changed

+152
-16
lines changed

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

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,22 +93,70 @@ std::string ConfusableIdentifierCheck::skeleton(StringRef Name) {
9393
return Skeleton;
9494
}
9595

96+
static bool mayShadowImpl(const NamedDecl *ND0, const NamedDecl *ND1) {
97+
const DeclContext *DC0 = ND0->getDeclContext()->getPrimaryContext();
98+
const DeclContext *DC1 = ND1->getDeclContext()->getPrimaryContext();
99+
100+
if (isa<TemplateTypeParmDecl>(ND0) || isa<TemplateTypeParmDecl>(ND0))
101+
return true;
102+
103+
while (DC0->isTransparentContext())
104+
DC0 = DC0->getParent();
105+
while (DC1->isTransparentContext())
106+
DC1 = DC1->getParent();
107+
108+
if (DC0->Equals(DC1))
109+
return true;
110+
111+
return false;
112+
}
113+
114+
static bool isMemberOf(const NamedDecl *ND, const CXXRecordDecl *RD) {
115+
const DeclContext *NDParent = ND->getDeclContext();
116+
if (!NDParent || !isa<CXXRecordDecl>(NDParent))
117+
return false;
118+
if (NDParent == RD)
119+
return true;
120+
return !RD->forallBases(
121+
[NDParent](const CXXRecordDecl *Base) { return NDParent != Base; });
122+
}
123+
124+
static bool mayShadow(const NamedDecl *ND0, const NamedDecl *ND1) {
125+
126+
const DeclContext *DC0 = ND0->getDeclContext()->getPrimaryContext();
127+
const DeclContext *DC1 = ND1->getDeclContext()->getPrimaryContext();
128+
129+
if (const CXXRecordDecl *RD0 = dyn_cast<CXXRecordDecl>(DC0)) {
130+
if (ND1->getAccess() != AS_private && isMemberOf(ND1, RD0))
131+
return true;
132+
}
133+
if (const CXXRecordDecl *RD1 = dyn_cast<CXXRecordDecl>(DC1)) {
134+
if (ND0->getAccess() != AS_private && isMemberOf(ND0, RD1))
135+
return true;
136+
}
137+
138+
if (DC0->Encloses(DC1))
139+
return mayShadowImpl(ND0, ND1);
140+
if (DC1->Encloses(DC0))
141+
return mayShadowImpl(ND1, ND0);
142+
return false;
143+
}
144+
96145
void ConfusableIdentifierCheck::check(
97146
const ast_matchers::MatchFinder::MatchResult &Result) {
98147
if (const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("nameddecl")) {
99-
if (IdentifierInfo *II = ND->getIdentifier()) {
100-
StringRef NDName = II->getName();
148+
if (IdentifierInfo *NDII = ND->getIdentifier()) {
149+
StringRef NDName = NDII->getName();
101150
llvm::SmallVector<const NamedDecl *> &Mapped = Mapper[skeleton(NDName)];
102-
const DeclContext *NDDecl = ND->getDeclContext();
103151
for (const NamedDecl *OND : Mapped) {
104-
if (!NDDecl->isDeclInLexicalTraversal(OND) &&
105-
!OND->getDeclContext()->isDeclInLexicalTraversal(ND))
106-
continue;
107-
if (OND->getIdentifier()->getName() != NDName) {
108-
diag(OND->getLocation(), "%0 is confusable with %1")
109-
<< OND->getName() << NDName;
110-
diag(ND->getLocation(), "other declaration found here",
111-
DiagnosticIDs::Note);
152+
const IdentifierInfo *ONDII = OND->getIdentifier();
153+
if (mayShadow(ND, OND)) {
154+
StringRef ONDName = ONDII->getName();
155+
if (ONDName != NDName) {
156+
diag(ND->getLocation(), "%0 is confusable with %1") << ND << OND;
157+
diag(OND->getLocation(), "other declaration found here",
158+
DiagnosticIDs::Note);
159+
}
112160
}
113161
}
114162
Mapped.push_back(ND);
Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// RUN: %check_clang_tidy %s misc-confusable-identifiers %t
22

33
int fo;
4-
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: fo is confusable with 𝐟o [misc-confusable-identifiers]
54
int 𝐟o;
6-
// CHECK-MESSAGES: :[[#@LINE-1]]:5: note: other declaration found here
5+
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: '𝐟o' is confusable with 'fo' [misc-confusable-identifiers]
6+
// CHECK-MESSAGES: :[[#@LINE-3]]:5: note: other declaration found here
77

88
void no() {
99
int 𝐟oo;
@@ -12,14 +12,102 @@ void no() {
1212
void worry() {
1313
int foo;
1414
}
15-
1615
int 𝐟i;
17-
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: 𝐟i is confusable with fi [misc-confusable-identifiers]
1816
int fi;
19-
// CHECK-MESSAGES: :[[#@LINE-1]]:5: note: other declaration found here
17+
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: 'fi' is confusable with '𝐟i' [misc-confusable-identifiers]
18+
// CHECK-MESSAGES: :[[#@LINE-3]]:5: note: other declaration found here
19+
20+
bool f0(const char *q1, const char *ql) {
21+
// CHECK-MESSAGES: :[[#@LINE-1]]:37: warning: 'ql' is confusable with 'q1' [misc-confusable-identifiers]
22+
// CHECK-MESSAGES: :[[#@LINE-2]]:21: note: other declaration found here
23+
return q1 < ql;
24+
}
2025

2126
// should not print anything
2227
namespace ns {
2328
struct Foo {};
2429
} // namespace ns
2530
auto f = ns::Foo();
31+
32+
struct Test {
33+
void f1(const char *pl);
34+
};
35+
36+
bool f2(const char *p1, const char *ql) {
37+
return p1 < ql;
38+
}
39+
40+
bool f3(const char *q0, const char *q1) {
41+
return q0 < q1;
42+
}
43+
44+
template <typename i1>
45+
struct S {
46+
template <typename il>
47+
void f4() {}
48+
// CHECK-MESSAGES: :[[#@LINE-2]]:22: warning: 'il' is confusable with 'i1' [misc-confusable-identifiers]
49+
// CHECK-MESSAGES: :[[#@LINE-5]]:20: note: other declaration found here
50+
};
51+
52+
template <typename i1>
53+
void f5(int il) {
54+
// CHECK-MESSAGES: :[[#@LINE-1]]:13: warning: 'il' is confusable with 'i1' [misc-confusable-identifiers]
55+
// CHECK-MESSAGES: :[[#@LINE-3]]:20: note: other declaration found here
56+
}
57+
58+
template <typename O0>
59+
void f6() {
60+
int OO = 0;
61+
// CHECK-MESSAGES: :[[#@LINE-1]]:7: warning: 'OO' is confusable with 'O0' [misc-confusable-identifiers]
62+
// CHECK-MESSAGES: :[[#@LINE-4]]:20: note: other declaration found here
63+
}
64+
int OO = 0; // no warning, not same scope as f6
65+
66+
namespace f7 {
67+
int i1;
68+
}
69+
70+
namespace f8 {
71+
int il; // no warning, different namespace
72+
}
73+
74+
namespace f7 {
75+
int il;
76+
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: 'il' is confusable with 'i1' [misc-confusable-identifiers]
77+
// CHECK-MESSAGES: :[[#@LINE-10]]:5: note: other declaration found here
78+
} // namespace f7
79+
80+
template <typename t1, typename tl>
81+
// CHECK-MESSAGES: :[[#@LINE-1]]:33: warning: 'tl' is confusable with 't1' [misc-confusable-identifiers]
82+
// CHECK-MESSAGES: :[[#@LINE-2]]:20: note: other declaration found here
83+
void f9();
84+
85+
struct Base0 {
86+
virtual void mO0();
87+
88+
private:
89+
void mII();
90+
};
91+
92+
struct Derived0 : Base0 {
93+
void mOO();
94+
// CHECK-MESSAGES: :[[#@LINE-1]]:8: warning: 'mOO' is confusable with 'mO0' [misc-confusable-identifiers]
95+
// CHECK-MESSAGES: :[[#@LINE-9]]:16: note: other declaration found here
96+
97+
void mI1(); // no warning: mII is private
98+
};
99+
100+
struct Base1 {
101+
long mO0;
102+
103+
private:
104+
long mII;
105+
};
106+
107+
struct Derived1 : Base1 {
108+
long mOO;
109+
// CHECK-MESSAGES: :[[#@LINE-1]]:8: warning: 'mOO' is confusable with 'mO0' [misc-confusable-identifiers]
110+
// CHECK-MESSAGES: :[[#@LINE-9]]:8: note: other declaration found here
111+
112+
long mI1(); // no warning: mII is private
113+
};

0 commit comments

Comments
 (0)