This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Introduce "add subclass" tweak
AbandonedPublic

Authored by avogelsgesang on Mar 20 2022, 7:49 AM.

Details

Summary

This commit adds a new "add subclass" tweak which facilitates quick
scaffolding of inheritance hierarchies. The tweak can be triggered
on any class with virtual methods. It then inserts a new subclass
which overrides all virtual methods with dummy implementations.

There are two variations of this tweak:

  1. A variant which overrides all virtual functions
  2. A variant which overrides only pure virtual functions

This tweak also supports deeper inheritance hierarchies, and collects
the methods to be overriden not only from the immediate base class but
from the complete inheritance tree.

Diff Detail

Event Timeline

avogelsgesang created this revision.Mar 20 2022, 7:49 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 20 2022, 7:49 AM
avogelsgesang requested review of this revision.Mar 20 2022, 7:49 AM
adamcz added a subscriber: adamcz.Mar 21 2022, 11:39 AM

Thanks, this looks really good! I always wanted a code action like this.

I always envisioned this as a code action on a class to implement missing virtual methods though. The idea would be:

class Foo : public B^ar { };

expands into:
class Foo : public Bar {

int baz() overrides { return Bar::baz(); }

};

This has several advantages:

  • no need to rename (the user already provided the name)
  • no need to move the code (it adds the code in appropriate place)
  • in situations like this change, where you implement a subclass of Tweak, there's no need to navigate to the Tweak.h header file (which could be outside of your project, maybe even read-only, or could just #include[_next] something else) and you don't need to wait for the AST build for the header file when you're just going to move it to another file anyway.

Have you considered an approach like this? Obviously it's a bit more complicated, but I think it would make this tweak even better. The way it's triggered now has it's use cases too, of course. I wonder if we could share the code between two use cases like this.

Ideally I'd like to see this code action show up as code completion option as well. Something like:

class Foo : public Bar {^

could result in "add virtual methods" as #1 option, greatly increasing discoverability, but that's a whole different problem and not specific to this tweak (PopulateSwitch would be great candidate for this too).

clang-tools-extra/clangd/refactor/tweaks/AddSubclass.cpp
1

Wrong file name in the comment.

In its current incarnation, I struggle to see the use case for this.
Most of the time Base classes live in header files are pretty much every time this tweak is applied you will then want to move the class.
The idea about just implementing the interface I had started in D94942 but its been stale a while. However IMO that has more potential value.

Thank for your comments, @adamcz and @njames93!

I agree that a "Implement interface" tweak is probably more useful, and I can imagine to evolve this tweak in that direction.

The main reason, I first went for the "create a new class from scratch" approach, was because I was not sure how to merge the newly inserted methods with the potentially existing methods to get an intuitive order.

How do you think we should further proceed here?

  • Do we see value in both an "Implement methods" and an "Add subclass" tweak?
  • Are you, @njames93, planning to follow-up on your existing review?
  • Would you like me to evolve this patch into an "Implement methods" patch, drawing inspiration from your existing review & the comments on it?

@adamcz

Ideally I'd like to see this code action show up as code completion option as well.

I think in the long-term such a "Implement methods" code action should also apply as a quickfix for

class Base {
   virtual void foo() = 0;
};

class Derived final : public Base {}; // as a quickfix for "Abstract class is marked 'final'"

auto foo = make_unique<Derived>(); // as a quickfix for "allocating an object of abstract class type 'Derived'"
Derived d; // as a quickfix for "Variable type 'Derived' is an abstract class"

But that's probably for a future commit. Let's first see that we get the basic functionality into clangd :)

avogelsgesang abandoned this revision.Aug 2 2022, 5:31 AM