This tweak searches the current or a selected base class for functions
which are still pure virtual in the current class, and adds function
declarations (overriders) for them.
Details
- Reviewers
sammccall
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
This change is not done, I need some advice:
- I don't understand why there are two levels of data structures in an entry of the final overriders map, hence I can't properly name the variables nor do I fully understand what I'm doing there.
- It's not clear if the tweak should be available if there are no pure virtual functions.
- It's not clear if we need to handle the case that there are multiple final overriders, or what we should do in that case.
- There are some test cases at the end of the file which I don't know how to implement.
- It's not clear if this tweak can apply inside a header.
- It's not clear how to prepare the tweak when the cursor is inside the class body (see tests).
Thanks for working on this!
FWIW I had an old draft of this feature that may have interesting ideas: https://reviews.llvm.org/D122827
clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp | ||
---|---|---|
45 | see the long comment on CXXFinalOverriderMap | |
59 | copying the tokens directly seems brittle, for example we shouldn't be emitting "virtual" but its's hard to avoid doing so. D122827 has a reprint() function that seems to work | |
76 | this probably needs to be restructured somewhat as need to enumerate these in prepare() to be sure there are any, but don't want to pay to produce the strings | |
87 | FWIW, I'm not actually sure that the case of multiple bases classes each providing pure-virtual-methods of which you want to override only one set is common enough to be worth this extra complexity. | |
140 | please use ^^ markers underneath the characters where you expect the tweak to trigger | |
144 | no "virtual" | |
150 | what do you think about listing the functions we'll define? Especially in the case where there's only one. | |
154 | I don't think this is a quick-fix, which should address a diagnostic or so. Really there doesn't seem to be a standard kind that fits here. Maybe we should add a new constant "generate"? | |
157 | it's important that we only offer the code action (i.e. prepare returns true) if there are actually methods to override. This means actually enumerating the functions needs to happen in prepare rather than apply. (Which means it needs to be fast...) | |
164 | this should only trigger on the class definition, not forward declarations | |
171 | this seems confusing: if you're going to walk up looking for a CXXBaseSpecifier, why not just do that in the first place? | |
197 | this doesn't seem like a condition we should be surfacing to the user. | |
213 | inserting at the end of the class isn't the most appropriate place. we have InsertionPoint.h for this, see usage in D122827 | |
clang-tools-extra/clangd/unittests/tweaks/DeclarePureVirtualsTests.cpp | ||
69 | it should be unavailable | |
592 | EXPECT_UNAVAILABLE, we shouldn't be offering the tweak |
I'm working on the comments, but it will take a while.
clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp | ||
---|---|---|
59 | My original idea was to take the AST node, clone it, remove the pure-specifier, and print that. To me, that seemed like the obvious (object-oriented) way how a tweak would manipulate or generate text. I've got the idea of manipulating the tokens from the "extract function" tweak (DefineOutline.cpp / getFunctionSourceCode). I've tried the reprint function and it does not copy noexcept. That is simple to fix but my guess is there are more issues. The idea of token manipulation was to rather copy too much than too little. I have no intuition how many issues could arise from that :) When I try to naively remove the virtual I currently print (virtual void foo() override;) in the token manipulation approach by skipping the first tokens, I end up skipping any attributes that precede the virtual as well. So I wrote a test for that, and reprint also doesn't copy attributes at the moment. It is not obvious to me if attributes should be copied, e.g. [[noreturn]]. I have a slight preference for my original approach but it's probably too complicated if this is the only use case. Therefore I'll just implement whatever looks most promising to you. | |
154 | For reference: |
clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp | ||
---|---|---|
59 | Yeah, I think extract function copies tokens because there could be anything in the selected code, and there are certain constructs clang doesn't print well, it's unclear what to do in the presence of macros etc. Those concerns don't apply here I think.
Yes, unfortunately this approach doesn't work because the AST isn't designed to be mutated, and carries not just syntactic information but also semantic invariants.
True. It looks like the noexcept is part of the functionprototype, so you could replace the logic that prints the return type + arg list with printType(functype, class, methodname) (from AST.h). That should pick up noexcept etc.
No, I don't think attributes should be copied. They're not required to override, and whether they semantically belong there depends on the particular attribute - we can leave this up to the user. |
clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp | ||
---|---|---|
59 |
Sorry, wrong tweak name (but right file/function). I meant "define outline". It also needs to massage the declaration since it needs to remove those parts which are not allowed in an outlined function definition, like virtual and static. Anyway, I'll try adjusting reprint() next week. |
so you could replace the logic that prints the return type + arg list with printType(functype, class, methodname) (from AST.h). That should pick up noexcept etc.
I've tried it and it's not obvious to me how to do it properly: Since printType wants to print the _function type_, it will parenthesize the placeholder (methodname):
void (foo)()
I've tried adding a flag to PrintingPolicy to avoid parenthesizing the name, but I wonder if adjusting a "type printer" to print a function declaration is a clean design. Next problem I ran into is parameters: A type printer does not need to concern itself with parameter names nor default arguments:
void foo(int x = 0) = 0; void foo(int) override;
Shall I continue to pursue that path? Shall I adjust the type printer to be able to print parameter names and default arguments?
Sorry, it does sound like I sent you down a bad path.
You *could* (no need for an option for parens, here we can detect that the placeholder isValidAsciiIdentifier), but entirely possible more things are broken still.
If you prefer the approach with copying the tokens, adding heuristics to patch them up as needed is maybe no worse than the alternatives.
(BTW, I don't think we want to copy default arguments - it's certainly not necessary, and not helpful in common cases where the function is only called through the base)
WIP - several improvements
I thought it might be a good idea to give an update even though this is
still work in progress.
- Don't print virtual for added declarations
- Some rework of the declaration generation
- Add title with preview
- Make tweak only available if it adds anything
- Mostly fix multiple inheritance
- Disable tweak on forward declarations
I've pushed some WIP change to inform that I'm still working on this topic :) Not sure if I also need to submit a comment to publish what I've marked as "done" via checkboxes.
clang-format not found in user’s local PATH; not linting file.