[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 #56221

Differential Revision: https://reviews.llvm.org/D128715
This commit is contained in:
serge-sans-paille 2022-06-28 10:34:46 +02:00
parent f27672924e
commit 7a550212e8
2 changed files with 152 additions and 16 deletions

View File

@ -93,22 +93,70 @@ std::string ConfusableIdentifierCheck::skeleton(StringRef Name) {
return Skeleton;
}
static bool mayShadowImpl(const NamedDecl *ND0, const NamedDecl *ND1) {
const DeclContext *DC0 = ND0->getDeclContext()->getPrimaryContext();
const DeclContext *DC1 = ND1->getDeclContext()->getPrimaryContext();
if (isa<TemplateTypeParmDecl>(ND0) || isa<TemplateTypeParmDecl>(ND0))
return true;
while (DC0->isTransparentContext())
DC0 = DC0->getParent();
while (DC1->isTransparentContext())
DC1 = DC1->getParent();
if (DC0->Equals(DC1))
return true;
return false;
}
static bool isMemberOf(const NamedDecl *ND, const CXXRecordDecl *RD) {
const DeclContext *NDParent = ND->getDeclContext();
if (!NDParent || !isa<CXXRecordDecl>(NDParent))
return false;
if (NDParent == RD)
return true;
return !RD->forallBases(
[NDParent](const CXXRecordDecl *Base) { return NDParent != Base; });
}
static bool mayShadow(const NamedDecl *ND0, const NamedDecl *ND1) {
const DeclContext *DC0 = ND0->getDeclContext()->getPrimaryContext();
const DeclContext *DC1 = ND1->getDeclContext()->getPrimaryContext();
if (const CXXRecordDecl *RD0 = dyn_cast<CXXRecordDecl>(DC0)) {
if (ND1->getAccess() != AS_private && isMemberOf(ND1, RD0))
return true;
}
if (const CXXRecordDecl *RD1 = dyn_cast<CXXRecordDecl>(DC1)) {
if (ND0->getAccess() != AS_private && isMemberOf(ND0, RD1))
return true;
}
if (DC0->Encloses(DC1))
return mayShadowImpl(ND0, ND1);
if (DC1->Encloses(DC0))
return mayShadowImpl(ND1, ND0);
return false;
}
void ConfusableIdentifierCheck::check(
const ast_matchers::MatchFinder::MatchResult &Result) {
if (const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("nameddecl")) {
if (IdentifierInfo *II = ND->getIdentifier()) {
StringRef NDName = II->getName();
if (IdentifierInfo *NDII = ND->getIdentifier()) {
StringRef NDName = NDII->getName();
llvm::SmallVector<const NamedDecl *> &Mapped = Mapper[skeleton(NDName)];
const DeclContext *NDDecl = ND->getDeclContext();
for (const NamedDecl *OND : Mapped) {
if (!NDDecl->isDeclInLexicalTraversal(OND) &&
!OND->getDeclContext()->isDeclInLexicalTraversal(ND))
continue;
if (OND->getIdentifier()->getName() != NDName) {
diag(OND->getLocation(), "%0 is confusable with %1")
<< OND->getName() << NDName;
diag(ND->getLocation(), "other declaration found here",
DiagnosticIDs::Note);
const IdentifierInfo *ONDII = OND->getIdentifier();
if (mayShadow(ND, OND)) {
StringRef ONDName = ONDII->getName();
if (ONDName != NDName) {
diag(ND->getLocation(), "%0 is confusable with %1") << ND << OND;
diag(OND->getLocation(), "other declaration found here",
DiagnosticIDs::Note);
}
}
}
Mapped.push_back(ND);

View File

@ -1,9 +1,9 @@
// RUN: %check_clang_tidy %s misc-confusable-identifiers %t
int fo;
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: fo is confusable with 𝐟o [misc-confusable-identifiers]
int 𝐟o;
// CHECK-MESSAGES: :[[#@LINE-1]]:5: note: other declaration found here
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: '𝐟o' is confusable with 'fo' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-3]]:5: note: other declaration found here
void no() {
int 𝐟oo;
@ -12,14 +12,102 @@ void no() {
void worry() {
int foo;
}
int 𝐟i;
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: 𝐟i is confusable with fi [misc-confusable-identifiers]
int fi;
// CHECK-MESSAGES: :[[#@LINE-1]]:5: note: other declaration found here
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: 'fi' is confusable with '𝐟i' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-3]]:5: note: other declaration found here
bool f0(const char *q1, const char *ql) {
// CHECK-MESSAGES: :[[#@LINE-1]]:37: warning: 'ql' is confusable with 'q1' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-2]]:21: note: other declaration found here
return q1 < ql;
}
// should not print anything
namespace ns {
struct Foo {};
} // namespace ns
auto f = ns::Foo();
struct Test {
void f1(const char *pl);
};
bool f2(const char *p1, const char *ql) {
return p1 < ql;
}
bool f3(const char *q0, const char *q1) {
return q0 < q1;
}
template <typename i1>
struct S {
template <typename il>
void f4() {}
// CHECK-MESSAGES: :[[#@LINE-2]]:22: warning: 'il' is confusable with 'i1' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-5]]:20: note: other declaration found here
};
template <typename i1>
void f5(int il) {
// CHECK-MESSAGES: :[[#@LINE-1]]:13: warning: 'il' is confusable with 'i1' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-3]]:20: note: other declaration found here
}
template <typename O0>
void f6() {
int OO = 0;
// CHECK-MESSAGES: :[[#@LINE-1]]:7: warning: 'OO' is confusable with 'O0' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-4]]:20: note: other declaration found here
}
int OO = 0; // no warning, not same scope as f6
namespace f7 {
int i1;
}
namespace f8 {
int il; // no warning, different namespace
}
namespace f7 {
int il;
// CHECK-MESSAGES: :[[#@LINE-1]]:5: warning: 'il' is confusable with 'i1' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-10]]:5: note: other declaration found here
} // namespace f7
template <typename t1, typename tl>
// CHECK-MESSAGES: :[[#@LINE-1]]:33: warning: 'tl' is confusable with 't1' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-2]]:20: note: other declaration found here
void f9();
struct Base0 {
virtual void mO0();
private:
void mII();
};
struct Derived0 : Base0 {
void mOO();
// CHECK-MESSAGES: :[[#@LINE-1]]:8: warning: 'mOO' is confusable with 'mO0' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-9]]:16: note: other declaration found here
void mI1(); // no warning: mII is private
};
struct Base1 {
long mO0;
private:
long mII;
};
struct Derived1 : Base1 {
long mOO;
// CHECK-MESSAGES: :[[#@LINE-1]]:8: warning: 'mOO' is confusable with 'mO0' [misc-confusable-identifiers]
// CHECK-MESSAGES: :[[#@LINE-9]]:8: note: other declaration found here
long mI1(); // no warning: mII is private
};