Details
- Reviewers
nridge sammccall - Commits
- rG8019b46bc70b: [clangd] Support renaming virtual methods
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Sorry for the delay, this patch is really neat!
The only serious thing is it uncovers an existing bug which asserts. As a potential new crash I think we should fix that - LMK whether you feel comfortable adding that fix in here or you'd like me to do it as a separate review.
clang-tools-extra/clangd/refactor/Rename.cpp | ||
---|---|---|
245 | Unfortunately we've uncovered a bug: this assertion fires (after this patch) on the following code: template <typename> class Foo { virtual void m(); }; class Bar : Foo<int> { void ^m() override; }; canonicalRenameDecl is supposed to be idempotent. However:canonicalRenameDecl(Bar::m) is Foo<int>::m, but canonicalRenameDecl(Foo<int>::m) is Foo<T>::m. I think this is because the getInstantiatedFromMemberFunction check comes before the overridden_methods check, so if the override points to a member of an instantiation then it's too late to map it back to the member of the template. Probably the best fix is to change line 103 to a recursive return canonicalRenameDecl(...), as is done elsewhere in the function. (Can you please add this case to the tests?) | |
547 | we're already in an anon namespace here | |
548 | The fact that we only need to walk down wasn't obvious to me at first, maybe add a comment on this function? e.g. Walk down from a virtual method to overriding methods, we rename them as a group. Note that canonicalRenameDecl() ensures we're starting from the base method. |
Thanks for the patch! A drive-by comment from me, hopefully useful.
clang-tools-extra/clangd/refactor/Rename.cpp | ||
---|---|---|
554 | Should we put a limit on the number of requests we send during recursion here? I see a few obvious failure modes:
We could address these in some other manner, this just seems to be the simplest option to protect against catastrophic outcomes (running the request indefinitely, crashing due to infinite recursion, etc). |
clang-tools-extra/clangd/refactor/Rename.cpp | ||
---|---|---|
548 | nit: can pass SymbolID by value, it's 8 bytes | |
554 |
It seems with little loss of readability we could provide some useful bounds: DenseSet<SymbolID> Pending = {Base}; while (!Pending.empty()) { Req = {.Subjects = Pending}; Pending.clear(); Index.relations(Req, { IDs.insert(ID); Pending.insert(ID) }); } in this case the #requests is clearly bounded by the length of the shortest chain to the most distant SymbolID, and is IMO safe with no numeric cap. whereas the current version could potentially get the same ID in multiple branches and so the bound on #requests is harder to determine. |
clang-tools-extra/clangd/refactor/Rename.cpp | ||
---|---|---|
554 | This looks good! Also avoids infinite recursion. To solve it, we could allow recursive requests and implement the recursion inside the index, but this could be done with a follow-up when we actually hit this issue. |
clang-tools-extra/clangd/refactor/Rename.cpp | ||
---|---|---|
245 | I added the test case for this. However, your suggested fix doesn't seem to stop the assertion from being triggered. I'd be grateful if you can take a closer look at this in a separate review - Should I leave the test case in anyway? Is it okay if the test fails for a while until you found the fix? | |
554 | Done, hope I interpreted your sketch correctly |
clang-tools-extra/clangd/refactor/Rename.cpp | ||
---|---|---|
554 | Nope, I totally misunderstood, should be better now |
A few more NITs from me, but please wait for @sammccall for another round of reviews.
clang-tools-extra/clangd/refactor/Rename.cpp | ||
---|---|---|
550 | NIT: maybe rename to insertTransitiveOverrides? | |
561–562 | This should protect against infinite loop in case of broken/stale index. |
LG
IIRC Tom doesn't have commit access, so I'll apply the last trivial changes and commit to avoid another round trip.
(Please LMK if i'm wrong about this!)
clang-tools-extra/clangd/refactor/Rename.cpp | ||
---|---|---|
245 | Thanks for adding the test, and sorry about the bad guess at a fix. We can't turn the buildbots red, so I'll comment out the test and fix+enable it separately. | |
561–562 | Good catch! |
Unfortunately we've uncovered a bug: this assertion fires (after this patch) on the following code:
canonicalRenameDecl is supposed to be idempotent. However:canonicalRenameDecl(Bar::m) is Foo<int>::m, but canonicalRenameDecl(Foo<int>::m) is Foo<T>::m.
I think this is because the getInstantiatedFromMemberFunction check comes before the overridden_methods check, so if the override points to a member of an instantiation then it's too late to map it back to the member of the template.
Probably the best fix is to change line 103 to a recursive return canonicalRenameDecl(...), as is done elsewhere in the function.
(Can you please add this case to the tests?)