Skip to content

Commit 2a84c63

Browse files
committed
[clang-tidy] Optimize misc-confusable-identifiers
Main performance issue in this check were caused by many calls to getPrimaryContext and constant walk up to declaration contexts using getParent. Also there were issue with forallBases that is slow. Profiled with perf and tested on open-source project Cataclysm-DDA. Before changes check took 27320 seconds, after changes 3682 seconds. That's 86.5% reduction. More optimizations are still possible in this check. Reviewed By: serge-sans-paille Differential Revision: https://reviews.llvm.org/D151051
1 parent 437ec15 commit 2a84c63

File tree

2 files changed

+91
-44
lines changed

2 files changed

+91
-44
lines changed

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

+74-42
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ ConfusableIdentifierCheck::~ConfusableIdentifierCheck() = default;
4545
// We're skipping 1. and 3. for the sake of simplicity, but this can lead to
4646
// false positive.
4747

48-
std::string ConfusableIdentifierCheck::skeleton(StringRef Name) {
48+
static std::string skeleton(StringRef Name) {
4949
using namespace llvm;
5050
std::string SName = Name.str();
5151
std::string Skeleton;
@@ -89,75 +89,107 @@ std::string ConfusableIdentifierCheck::skeleton(StringRef Name) {
8989
return Skeleton;
9090
}
9191

92-
static bool mayShadowImpl(const NamedDecl *ND0, const NamedDecl *ND1) {
93-
const DeclContext *DC0 = ND0->getDeclContext()->getPrimaryContext();
94-
const DeclContext *DC1 = ND1->getDeclContext()->getPrimaryContext();
92+
static bool mayShadowImpl(const DeclContext *DC0, const DeclContext *DC1) {
93+
return DC0 && DC0 == DC1;
94+
}
9595

96-
if (isa<TemplateTypeParmDecl>(ND0) || isa<TemplateTypeParmDecl>(ND0))
97-
return true;
96+
static bool mayShadowImpl(const NamedDecl *ND0, const NamedDecl *ND1) {
97+
return isa<TemplateTypeParmDecl>(ND0) || isa<TemplateTypeParmDecl>(ND1);
98+
}
9899

99-
while (DC0->isTransparentContext())
100-
DC0 = DC0->getParent();
101-
while (DC1->isTransparentContext())
102-
DC1 = DC1->getParent();
100+
static bool isMemberOf(const ConfusableIdentifierCheck::ContextInfo *DC0,
101+
const ConfusableIdentifierCheck::ContextInfo *DC1) {
102+
if (DC0->Bases.empty())
103+
return false;
104+
return llvm::is_contained(DC1->Bases, DC0->PrimaryContext);
105+
}
103106

104-
if (DC0->Equals(DC1))
107+
static bool enclosesContext(const ConfusableIdentifierCheck::ContextInfo *DC0,
108+
const ConfusableIdentifierCheck::ContextInfo *DC1) {
109+
if (DC0->PrimaryContext == DC1->PrimaryContext)
105110
return true;
106111

107-
return false;
112+
return llvm::is_contained(DC0->PrimaryContexts, DC1->PrimaryContext) ||
113+
llvm::is_contained(DC1->PrimaryContexts, DC0->PrimaryContext);
108114
}
109115

110-
static bool isMemberOf(const NamedDecl *ND, const CXXRecordDecl *RD) {
111-
const DeclContext *NDParent = ND->getDeclContext();
112-
if (!NDParent || !isa<CXXRecordDecl>(NDParent))
113-
return false;
114-
if (NDParent == RD)
116+
static bool mayShadow(const NamedDecl *ND0,
117+
const ConfusableIdentifierCheck::ContextInfo *DC0,
118+
const NamedDecl *ND1,
119+
const ConfusableIdentifierCheck::ContextInfo *DC1) {
120+
if (!DC0->Bases.empty() && ND1->getAccess() != AS_private &&
121+
isMemberOf(DC1, DC0))
115122
return true;
116-
return !RD->forallBases(
117-
[NDParent](const CXXRecordDecl *Base) { return NDParent != Base; });
123+
if (!DC1->Bases.empty() && ND0->getAccess() != AS_private &&
124+
isMemberOf(DC0, DC1))
125+
return true;
126+
127+
return enclosesContext(DC0, DC1) &&
128+
(mayShadowImpl(ND0, ND1) || mayShadowImpl(DC0->NonTransparentContext,
129+
DC1->NonTransparentContext));
118130
}
119131

120-
static bool mayShadow(const NamedDecl *ND0, const NamedDecl *ND1) {
132+
const ConfusableIdentifierCheck::ContextInfo *
133+
ConfusableIdentifierCheck::getContextInfo(const DeclContext *DC) {
134+
const DeclContext *PrimaryContext = DC->getPrimaryContext();
135+
auto It = ContextInfos.find(PrimaryContext);
136+
if (It != ContextInfos.end())
137+
return &It->second;
138+
139+
ContextInfo &Info = ContextInfos[PrimaryContext];
140+
Info.PrimaryContext = PrimaryContext;
141+
Info.NonTransparentContext = PrimaryContext;
142+
143+
while (Info.NonTransparentContext->isTransparentContext()) {
144+
Info.NonTransparentContext = Info.NonTransparentContext->getParent();
145+
if (!Info.NonTransparentContext)
146+
break;
147+
}
121148

122-
const DeclContext *DC0 = ND0->getDeclContext()->getPrimaryContext();
123-
const DeclContext *DC1 = ND1->getDeclContext()->getPrimaryContext();
149+
if (Info.NonTransparentContext)
150+
Info.NonTransparentContext =
151+
Info.NonTransparentContext->getPrimaryContext();
124152

125-
if (const CXXRecordDecl *RD0 = dyn_cast<CXXRecordDecl>(DC0)) {
126-
RD0 = RD0->getDefinition();
127-
if (RD0 && ND1->getAccess() != AS_private && isMemberOf(ND1, RD0))
128-
return true;
153+
while (DC) {
154+
if (!isa<LinkageSpecDecl>(DC) && !isa<ExportDecl>(DC))
155+
Info.PrimaryContexts.push_back(DC->getPrimaryContext());
156+
DC = DC->getParent();
129157
}
130-
if (const CXXRecordDecl *RD1 = dyn_cast<CXXRecordDecl>(DC1)) {
131-
RD1 = RD1->getDefinition();
132-
if (RD1 && ND0->getAccess() != AS_private && isMemberOf(ND0, RD1))
133-
return true;
158+
159+
if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(PrimaryContext)) {
160+
RD = RD->getDefinition();
161+
if (RD) {
162+
Info.Bases.push_back(RD);
163+
RD->forallBases([&](const CXXRecordDecl *Base) {
164+
Info.Bases.push_back(Base);
165+
return false;
166+
});
167+
}
134168
}
135169

136-
if (DC0->Encloses(DC1))
137-
return mayShadowImpl(ND0, ND1);
138-
if (DC1->Encloses(DC0))
139-
return mayShadowImpl(ND1, ND0);
140-
return false;
170+
return &Info;
141171
}
142172

143173
void ConfusableIdentifierCheck::check(
144174
const ast_matchers::MatchFinder::MatchResult &Result) {
145175
if (const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("nameddecl")) {
146176
if (IdentifierInfo *NDII = ND->getIdentifier()) {
177+
const ContextInfo *Info = getContextInfo(ND->getDeclContext());
147178
StringRef NDName = NDII->getName();
148-
llvm::SmallVector<const NamedDecl *> &Mapped = Mapper[skeleton(NDName)];
149-
for (const NamedDecl *OND : Mapped) {
150-
const IdentifierInfo *ONDII = OND->getIdentifier();
151-
if (mayShadow(ND, OND)) {
179+
llvm::SmallVector<Entry> &Mapped = Mapper[skeleton(NDName)];
180+
for (const Entry &E : Mapped) {
181+
const IdentifierInfo *ONDII = E.Declaration->getIdentifier();
182+
if (mayShadow(ND, Info, E.Declaration, E.Info)) {
152183
StringRef ONDName = ONDII->getName();
153184
if (ONDName != NDName) {
154-
diag(ND->getLocation(), "%0 is confusable with %1") << ND << OND;
155-
diag(OND->getLocation(), "other declaration found here",
185+
diag(ND->getLocation(), "%0 is confusable with %1")
186+
<< ND << E.Declaration;
187+
diag(E.Declaration->getLocation(), "other declaration found here",
156188
DiagnosticIDs::Note);
157189
}
158190
}
159191
}
160-
Mapped.push_back(ND);
192+
Mapped.push_back({ND, Info});
161193
}
162194
}
163195
}

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

+17-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_CONFUSABLE_IDENTIFIER_CHECK_H
1212

1313
#include "../ClangTidyCheck.h"
14+
#include <unordered_map>
1415

1516
namespace clang::tidy::misc {
1617

@@ -26,9 +27,23 @@ class ConfusableIdentifierCheck : public ClangTidyCheck {
2627
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
2728
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
2829

30+
struct ContextInfo {
31+
const DeclContext *PrimaryContext;
32+
const DeclContext *NonTransparentContext;
33+
llvm::SmallVector<const DeclContext *> PrimaryContexts;
34+
llvm::SmallVector<const CXXRecordDecl *> Bases;
35+
};
36+
2937
private:
30-
std::string skeleton(StringRef);
31-
llvm::StringMap<llvm::SmallVector<const NamedDecl *>> Mapper;
38+
struct Entry {
39+
const NamedDecl *Declaration;
40+
const ContextInfo *Info;
41+
};
42+
43+
const ContextInfo *getContextInfo(const DeclContext *DC);
44+
45+
llvm::StringMap<llvm::SmallVector<Entry>> Mapper;
46+
std::unordered_map<const DeclContext *, ContextInfo> ContextInfos;
3247
};
3348

3449
} // namespace clang::tidy::misc

0 commit comments

Comments
 (0)