diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp --- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp @@ -16,6 +16,7 @@ #include "SourceCode.h" #include "refactor/Tweak.h" #include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Attr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" @@ -211,6 +212,60 @@ } } + // Remove the virtual, override and final specifiers. + if (FD->hasAttrs()) { + for (auto *Attr : FD->getAttrs()) { + if (isa(Attr) || isa(Attr)) { + assert(Attr->getLocation().isValid()); + if (Attr->getLocation().isMacroID()) { + Errors = llvm::joinErrors( + std::move(Errors), + llvm::createStringError(llvm::inconvertibleErrorCode(), + isa(Attr) + ? "define outline: Can't move out of " + "line as function has " + "a macro `override` specifier" + : "define outline: Can't move out of " + "line as function has " + "a macro `final` specifier")); + continue; + } + CharSourceRange DelRange = + CharSourceRange::getTokenRange(Attr->getLocation()); + if (auto Err = + QualifierInsertions.add(tooling::Replacement(SM, DelRange, ""))) + Errors = llvm::joinErrors(std::move(Errors), std::move(Err)); + } + } + } + if (FD->isVirtualAsWritten()) { + SourceRange SpecRange{FD->getBeginLoc(), FD->getLocation()}; + bool Any = false; + // Clang allows duplicating virtual specifiers so check for multiple + // occurances. + for (const syntax::Token &Tok : TokBuf.expandedTokens(SpecRange)) { + if (Tok.kind() == tok::kw_virtual) { + assert(Tok.location().isValid()); + if (Tok.location().isMacroID()) { + Errors = + llvm::joinErrors(std::move(Errors), + llvm::createStringError( + llvm::inconvertibleErrorCode(), + "define outline: Can't move out of line as " + "function has a macro `virtual` specifier")); + continue; + } + Any = true; + CharSourceRange DelRange = + CharSourceRange::getTokenRange(Tok.location()); + if (auto Err = + QualifierInsertions.add(tooling::Replacement(SM, DelRange, ""))) + Errors = llvm::joinErrors(std::move(Errors), std::move(Err)); + } + } + assert(Any); // Ensure at least one virtual was found + } + if (Errors) return std::move(Errors); return getFunctionSourceAfterReplacements(FD, QualifierInsertions); diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -2068,6 +2068,52 @@ };)cpp", "Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n", }, + // Overridden Methods + { + R"cpp( + struct A { + virtual void f^oo() {} + };)cpp", + R"cpp( + struct A { + virtual void foo() ; + };)cpp", + " void A::foo() {}\n", + }, + { + R"cpp( + struct A { + virtual void foo() = 0; + }; + struct B : A { + void fo^o() override {} + };)cpp", + R"cpp( + struct A { + virtual void foo() = 0; + }; + struct B : A { + void foo() override ; + };)cpp", + "void B::foo() {}\n", + }, + { + R"cpp( + struct A { + virtual void foo() = 0; + }; + struct B : A { + void fo^o() final {} + };)cpp", + R"cpp( + struct A { + virtual void foo() = 0; + }; + struct B : A { + void foo() final ; + };)cpp", + "void B::foo() {}\n", + }, }; for (const auto &Case : Cases) { SCOPED_TRACE(Case.Test);