diff --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt --- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt +++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt @@ -29,6 +29,7 @@ RemoveUsingNamespace.cpp SpecialMembers.cpp SwapIfBranches.cpp + DeclarePureVirtuals.cpp LINK_LIBS clangAST diff --git a/clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp b/clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/refactor/tweaks/DeclarePureVirtuals.cpp @@ -0,0 +1,220 @@ +//===--- DeclarePureVirtuals.cpp ---------------------------------*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Selection.h" +#include "SourceCode.h" +#include "refactor/Tweak.h" +#include "clang/AST/ASTContextAllocate.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/TypeLoc.h" +#include "clang/Basic/AttributeCommonInfo.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" +#include "clang/Tooling/Core/Replacement.h" +#include "clang/Tooling/Refactoring/ASTSelection.h" +#include "clang/Tooling/Syntax/Tokens.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { +namespace clangd { +namespace { + +// Determines if the class which provided the final overrider map has a non-pure +// virtual override of Method. +bool hasNonPureVirtOverrideFor(const CXXFinalOverriderMap &Map, + const CXXMethodDecl *Method) { + auto const It = Map.find(Method); + if (It == Map.end()) { + return false; + } + + for (const auto &Overrider : It->second) { + for (const auto &WhatIsThis : + Overrider.second) { // TODO: why is there a second level???? + return !WhatIsThis.Method->isPure(); + } + } + + assert(false && "override was found but overrider list was empty"); +} + +// Appends a declaration to To which overrides Method. +void appendDeclForOverride(std::string &To, const CXXMethodDecl *Method, + const SourceManager &SM, + const syntax::TokenBuffer &TokBuf) { + const SourceRange MethodDeclRange{Method->getBeginLoc(), Method->getEndLoc()}; + + const llvm::ArrayRef Tokens = + TokBuf.expandedTokens(MethodDeclRange); + const auto EqTok = + llvm::find_if(llvm::reverse(Tokens), [](const syntax::Token &Tok) { + return Tok.kind() == tok::equal; + }); + assert(EqTok != Tokens.rend()); + + To.append(SM.getCharacterData(MethodDeclRange.getBegin()), + SM.getCharacterData(EqTok->location())); + To.append("override;\n"); +} + +// Returns a string with override declarations for all virtual functions in the +// inheritance hierarchy of Start (including Start itself) which are still pure +// virtual in Target. Start can be the same class as Target. Target must be the +// same as or derived from Start. +std::string collectPureVirtualFuncOverrideDecls( + const CXXRecordDecl &Target, const CXXRecordDecl &Start, + const SourceManager &SM, const syntax::TokenBuffer &TokBuf) { + std::string Additions; + + assert(&Target == &Start || Target.isDerivedFrom(&Start)); + + CXXFinalOverriderMap FinalOverriderMapOfStart; + Start.getFinalOverriders(FinalOverriderMapOfStart); + + CXXFinalOverriderMap FinalOverriderMapOfTarget; + // If &Target == &Start then Target doesn't already have any + // overrides for functions that are pure in Start. The map remains empty, + // which means hasOverrideFor will return false below, which is OK since the + // function is then counted as pure virtual. + if (&Target != &Start) { + Target.getFinalOverriders(FinalOverriderMapOfTarget); + } + + for (const auto &bar : FinalOverriderMapOfStart) { // TODO: see line 44 + // const CXXMethodDecl *Method = bar.first; + const OverridingMethods &Overrides = bar.second; + + for (const std::pair> + &meow : // TODO + Overrides) { + const auto &IdontKnowWhatThisIs = meow.second; // TODO + for (const UniqueVirtualMethod &Override : IdontKnowWhatThisIs) { + // TODO: conjunction of all overrides? or rely on there's only one? + if (Override.Method->isPure() && + !hasNonPureVirtOverrideFor(FinalOverriderMapOfTarget, + Override.Method)) { + appendDeclForOverride(Additions, Override.Method, SM, TokBuf); + } + } + } + } + + return Additions; +} + +// Finds the CXXBaseSpecifier in/under the selection, if any. +const CXXBaseSpecifier *findBaseSpecifier(const SelectionTree::Node *Node) { + if (!Node) + return nullptr; + + const DynTypedNode &ASTNode = Node->ASTNode; + const CXXBaseSpecifier *BaseSpec = ASTNode.get(); + if (BaseSpec) + return BaseSpec; + + const SelectionTree::Node *const Parent = Node->Parent; + if (Parent) + return Parent->ASTNode.get(); + + return nullptr; +} + +/// Declares overrides for all pure virtual function in a class hierarchy, +/// starting with a base class specifier. +/// +/// Before: +/// class Base { virtual void foo() = 0; }; +/// class Derived {}; +/// +/// After: +/// class Base { virtual void foo() = 0; }; +/// class Derived { virtual void foo() override; }; +class DeclarePureVirtuals : public Tweak { +public: + const char *id() const override; + + std::string title() const override { + return "Override pure virtual functions"; + } + + llvm::StringLiteral kind() const override { + return CodeAction::QUICKFIX_KIND; + } + + bool prepare(const Selection &Sel) override { + const SelectionTree::Node *const CommonAncestor = + Sel.ASTSelection.commonAncestor(); + if (!CommonAncestor) + return false; + + // maybe selected a class, in which case override functions of all bases + SelectedDerivedClass = CommonAncestor->ASTNode.get(); + if (SelectedDerivedClass) { + return !SelectedDerivedClass->bases().empty(); + } + + // maybe selected a base class specifier, in which case only override those + // bases's functions + const DeclContext &DC = CommonAncestor->getDeclContext(); + SelectedDerivedClass = dyn_cast(&DC); + if (SelectedDerivedClass) { + SelectedBaseSpecifier = + findBaseSpecifier(Sel.ASTSelection.commonAncestor()); + return true; + } + + return false; + } + + const CXXRecordDecl *SelectedDerivedClass = nullptr; + const CXXBaseSpecifier *SelectedBaseSpecifier = nullptr; + + Expected apply(const Selection &Sel) override { + assert(SelectedDerivedClass); // prepare must have been called and returned + // true + + std::string Additions; + SourceManager &SM = Sel.AST->getSourceManager(); + + if (SelectedBaseSpecifier) { + // TODO: can getType return null? + auto const *Start = + SelectedBaseSpecifier->getType()->getAsCXXRecordDecl(); + if (!Start) { + return llvm::createStringError( + llvm::inconvertibleErrorCode(), + "selected base class specifier does not refer to a C++ class"); + } + Additions = collectPureVirtualFuncOverrideDecls( + *SelectedDerivedClass, *Start, SM, Sel.AST->getTokens()); + } else { + Additions += collectPureVirtualFuncOverrideDecls( + *SelectedDerivedClass, *SelectedDerivedClass, SM, + Sel.AST->getTokens()); + } + + // TODO: can we apply this tweak in a header if we apply the effect to the + // "main file"? + return Effect::mainFileEdit( + SM, tooling::Replacements(tooling::Replacement( + SM, SelectedDerivedClass->getEndLoc(), 0, Additions))); + } +}; + +REGISTER_TWEAK(DeclarePureVirtuals) + +} // namespace +} // namespace clangd +} // namespace clang diff --git a/clang-tools-extra/clangd/unittests/CMakeLists.txt b/clang-tools-extra/clangd/unittests/CMakeLists.txt --- a/clang-tools-extra/clangd/unittests/CMakeLists.txt +++ b/clang-tools-extra/clangd/unittests/CMakeLists.txt @@ -118,6 +118,7 @@ tweaks/AnnotateHighlightingsTests.cpp tweaks/DefineInlineTests.cpp tweaks/DefineOutlineTests.cpp + tweaks/DeclarePureVirtualsTests.cpp tweaks/DumpASTTests.cpp tweaks/DumpRecordLayoutTests.cpp tweaks/DumpSymbolTests.cpp diff --git a/clang-tools-extra/clangd/unittests/tweaks/DeclarePureVirtualsTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/DeclarePureVirtualsTests.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/tweaks/DeclarePureVirtualsTests.cpp @@ -0,0 +1,665 @@ +//===-- DeclarePureVirtualsTests.cpp ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TestTU.h" +#include "TweakTesting.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +TWEAK_TEST(DeclarePureVirtuals); + +TEST_F(DeclarePureVirtualsTest, AvailabilityTriggerOnBaseSpecifier) { + EXPECT_AVAILABLE(R"cpp( + class MyBase { + virtual void myFunction() = 0; + }; + + class MyDerived : ^p^u^b^l^i^c^ ^M^y^B^a^s^e { + }; + + class MyDerived2 : ^M^y^B^a^s^e { + }; + )cpp"); +} + +TEST_F(DeclarePureVirtualsTest, AvailabilityTriggerOnClass) { + EXPECT_AVAILABLE(R"cpp( + class MyBase { + virtual void myFunction() = 0; + }; + + class ^M^y^D^e^r^i^v^e^d: public MyBase {^ + // but not here, see AvailabilityTriggerInsideClass + ^}; + )cpp"); +} + +TEST_F(DeclarePureVirtualsTest, AvailabilityTriggerInsideClass) { + // TODO: this should actually be available but I don't know how to implement + // it: the common node of the selection returns the TU, so I get no + // information about which class we're in. + EXPECT_UNAVAILABLE(R"cpp( + class MyBase { + virtual void myFunction() = 0; + }; + + class MyDerived : public MyBase { + ^ + }; + )cpp"); +} + +TEST_F(DeclarePureVirtualsTest, UnavailabilityNoBases) { + EXPECT_UNAVAILABLE(R"cpp( + class ^N^o^D^e^r^i^v^e^d^ ^{^ + ^}; + )cpp"); +} + +// TODO: should the tweak available if there are no pure virtual functions and +// do nothing? or should it be unavailable? + +TEST_F(DeclarePureVirtualsTest, SinglePureVirtualFunction) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction() = 0; +}; + +class MyDerived : pub^lic MyBase { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction() = 0; +}; + +class MyDerived : public MyBase { +virtual void myFunction() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, MultipleInheritanceFirstClass) { + const char *Test = R"cpp( +class MyBase1 { + virtual void myFunction1() = 0; +}; + +class MyBase2 { + virtual void myFunction2() = 0; +}; + +class MyDerived : pub^lic MyBase1, public MyBase2 { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase1 { + virtual void myFunction1() = 0; +}; + +class MyBase2 { + virtual void myFunction2() = 0; +}; + +class MyDerived : public MyBase1, public MyBase2 { +virtual void myFunction1() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, MultipleInheritanceSecondClass) { + const char *Test = R"cpp( +class MyBase1 { + virtual void myFunction1() = 0; +}; + +class MyBase2 { + virtual void myFunction2() = 0; +}; + +class MyDerived : public MyBase1, pub^lic MyBase2 { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase1 { + virtual void myFunction1() = 0; +}; + +class MyBase2 { + virtual void myFunction2() = 0; +}; + +class MyDerived : public MyBase1, public MyBase2 { +virtual void myFunction2() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, SingleInheritanceMultiplePureVirtualFunctions) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction1() = 0; + virtual void myFunction2() = 0; +}; + +class MyDerived : pub^lic MyBase { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction1() = 0; + virtual void myFunction2() = 0; +}; + +class MyDerived : public MyBase { +virtual void myFunction1() override; +virtual void myFunction2() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, SingleInheritanceMixedVirtualFunctions) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction1() = 0; + virtual void myFunction2(); +}; + +class MyDerived : pub^lic MyBase { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction1() = 0; + virtual void myFunction2(); +}; + +class MyDerived : public MyBase { +virtual void myFunction1() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, + TwoLevelsInheritanceOnePureVirtualFunctionInTopBase) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction1() = 0; +}; + +class MyIntermediate : public MyBase { +}; + +class MyDerived : pub^lic MyIntermediate { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction1() = 0; +}; + +class MyIntermediate : public MyBase { +}; + +class MyDerived : public MyIntermediate { +virtual void myFunction1() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, + TwoLevelsInheritancePureVirtualFunctionsInBothBases) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction1() = 0; +}; + +class MyIntermediate : public MyBase { + virtual void myFunction2() = 0; +}; + +class MyDerived : pub^lic MyIntermediate { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction1() = 0; +}; + +class MyIntermediate : public MyBase { + virtual void myFunction2() = 0; +}; + +class MyDerived : public MyIntermediate { +virtual void myFunction1() override; +virtual void myFunction2() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, + TwoLevelInheritanceFunctionNoLongerPureVirtual) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction1() = 0; +}; + +class MyIntermediate : public MyBase { + virtual void myFunction1() override {} +}; + +class MyDerived : pub^lic MyIntermediate { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction1() = 0; +}; + +class MyIntermediate : public MyBase { + virtual void myFunction1() override {} +}; + +class MyDerived : public MyIntermediate { +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, VirtualFunctionWithDefaultParameters) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction(int x, int y = 42) = 0; +}; + +class MyDerived : pub^lic MyBase { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction(int x, int y = 42) = 0; +}; + +class MyDerived : public MyBase { +virtual void myFunction(int x, int y = 42) override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, FunctionOverloading) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction(int x) = 0; + virtual void myFunction(float x) = 0; +}; + +class MyDerived : pub^lic MyBase { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction(int x) = 0; + virtual void myFunction(float x) = 0; +}; + +class MyDerived : public MyBase { +virtual void myFunction(int x) override; +virtual void myFunction(float x) override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, OverrideAlreadyExisting) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction() = 0; +}; + +class MyDerived : pub^lic MyBase { + virtual void myFunction() override; +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction() = 0; +}; + +class MyDerived : public MyBase { + virtual void myFunction() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, TwoOverloadsOnlyOnePureVirtual) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction(int x) = 0; + virtual void myFunction(float x); +}; + +class MyDerived : pub^lic MyBase { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction(int x) = 0; + virtual void myFunction(float x); +}; + +class MyDerived : public MyBase { +virtual void myFunction(int x) override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, DerivedClassHasNonOverridingFunction) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction(int x) = 0; +}; + +class MyDerived : pub^lic MyBase { + void myFunction(float x); +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction(int x) = 0; +}; + +class MyDerived : public MyBase { + void myFunction(float x); +virtual void myFunction(int x) override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, PureVirtualFunctionWithNoexcept) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction() noexcept = 0; +}; + +class MyDerived : pub^lic MyBase { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction() noexcept = 0; +}; + +class MyDerived : public MyBase { +virtual void myFunction() noexcept override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, MixOfVirtualAndNonVirtualMemberFunctions) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction1() = 0; + void myFunction2(); +}; + +class MyDerived : pub^lic MyBase { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction1() = 0; + void myFunction2(); +}; + +class MyDerived : public MyBase { +virtual void myFunction1() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, PureVirtualFunctionWithBody) { + const char *Test = R"cpp( +class MyBase { + virtual void myFunction1() = 0; +}; + +void MyBase::myFunction1() { +} + +class MyDerived : pub^lic MyBase { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase { + virtual void myFunction1() = 0; +}; + +void MyBase::myFunction1() { +} + +class MyDerived : public MyBase { +virtual void myFunction1() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, TraversalOrder) { + const char *Test = R"cpp( +class MyBase0 { + virtual void myFunction0() = 0; +}; + +class MyBase1 { + virtual void myFunction1() = 0; +}; + +class MyBase2 : MyBase0, MyBase1 { + virtual void myFunction2() = 0; +}; + +class MyBase3 { + virtual void myFunction3() = 0; +}; + +class MyBase4 : MyBase2, MyBase3 { + virtual void myFunction4() = 0; +}; + +class MyDerived : pub^lic MyBase4 { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase0 { + virtual void myFunction0() = 0; +}; + +class MyBase1 { + virtual void myFunction1() = 0; +}; + +class MyBase2 : MyBase0, MyBase1 { + virtual void myFunction2() = 0; +}; + +class MyBase3 { + virtual void myFunction3() = 0; +}; + +class MyBase4 : MyBase2, MyBase3 { + virtual void myFunction4() = 0; +}; + +class MyDerived : public MyBase4 { +virtual void myFunction0() override; +virtual void myFunction1() override; +virtual void myFunction2() override; +virtual void myFunction3() override; +virtual void myFunction4() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +TEST_F(DeclarePureVirtualsTest, AllBaseClassSpecifiers) { + const char *Test = R"cpp( +class MyBase0 { + virtual void myFunction0() = 0; +}; + +class MyBase1 { + virtual void myFunction1() = 0; +}; + +class My^Derived : MyBase0, MyBase1 { +}; + )cpp"; + + const char *Expected = R"cpp( +class MyBase0 { + virtual void myFunction0() = 0; +}; + +class MyBase1 { + virtual void myFunction1() = 0; +}; + +class MyDerived : MyBase0, MyBase1 { +virtual void myFunction0() override; +virtual void myFunction1() override; +}; + )cpp"; + + EXPECT_EQ(apply(Test), Expected); +} + +// This test should fail since MyBase is incomplete. No idea how to test that. +// TEST_F(DeclarePureVirtualsTest, IncompleteBassClass) { +// const char *Test = R"cpp( +// class MyBase; +// +// class MyDerived : My^Base { +// }; +// )cpp"; +// +// const char *Expected = R"cpp( +// class MyBase; +// +// class MyDerived : MyBase { +// }; +// )cpp"; +// +// EXPECT_EQ(apply(Test), Expected); +// } + +// This test should fail since MyBase is incomplete. No idea how to test that. +// This is a different failure mode since it probably fails to apply but not to +// prepare. +// TEST_F(DeclarePureVirtualsTest, IncompleteBassClass) { +// const char *Test = R"cpp( +// class MyBase; +// +// class My^Derived : MyBase { +// }; +// )cpp"; +// +// const char *Expected = R"cpp( +// class MyBase; +// +// class MyDerived : MyBase { +// }; +// )cpp"; +// +// EXPECT_EQ(apply(Test), Expected); +// } + +// This test should fail since MyBase is unknown. No idea how to test that. +// TEST_F(DeclarePureVirtualsTest, UnknownBaseSpecifier) { +// const char *Test = R"cpp( +// class MyDerived : My^Base { +// }; +// )cpp"; +// +// const char *Expected = R"cpp( +// class MyDerived : MyBase { +// }; +// )cpp"; +// +// EXPECT_EQ(apply(Test), Expected); +// } + +// This test should fail since MyBase is unknown. No idea how to test that. +// It's a different failure mode since it will probably fail to apply but not to +// prepare. +// TEST_F(DeclarePureVirtualsTest, UnknownBaseSpecifier) { +// const char *Test = R"cpp( +// class My^Derived : MyBase { +// }; +// )cpp"; +// +// const char *Expected = R"cpp( +// class MyDerived : MyBase { +// }; +// )cpp"; +// +// EXPECT_EQ(apply(Test), Expected); +// } + +} // namespace +} // namespace clangd +} // namespace clang