From: Kadir Cetinkaya Date: Fri, 22 Nov 2019 12:56:02 +0000 (+0100) Subject: [clangd] Define out-of-line qualify function name X-Git-Tag: llvmorg-11-init~3009 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ddcce0f3d665b66a8232a80ca918d349c8485fe4;p=platform%2Fupstream%2Fllvm.git [clangd] Define out-of-line qualify function name Summary: When moving function definitions to a different context, the function name might need a different spelling, for example in the header it might be: ``` namespace a { void foo() {} } ``` And we might want to move it into a context which doesn't have `namespace a` as a parent, then we must re-spell the function name, i.e: ``` void a::foo() {} ``` This patch implements a version of this which ignores using namespace declarations in the source file. Reviewers: hokein Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D70656 --- diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp index 1bd70b72..6b44edb 100644 --- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp @@ -136,7 +136,6 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD, // Contains function signature, body and template parameters if applicable. // No need to qualify parameters, as they are looked up in the context // containing the function/method. -// FIXME: Qualify function name depending on the target context. llvm::Expected getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace) { auto &SM = FD->getASTContext().getSourceManager(); @@ -149,16 +148,17 @@ getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace) { llvm::Error Errors = llvm::Error::success(); tooling::Replacements QualifierInsertions; - // Finds the first unqualified name in function return type and qualifies it - // to be valid in TargetContext. + // Finds the first unqualified name in function return type and name, then + // qualifies those to be valid in TargetContext. findExplicitReferences(FD, [&](ReferenceLoc Ref) { // It is enough to qualify the first qualifier, so skip references with a // qualifier. Also we can't do much if there are no targets or name is // inside a macro body. if (Ref.Qualifier || Ref.Targets.empty() || Ref.NameLoc.isMacroID()) return; - // Qualify return type - if (Ref.NameLoc != FD->getReturnTypeSourceRange().getBegin()) + // Only qualify return type and function name. + if (Ref.NameLoc != FD->getReturnTypeSourceRange().getBegin() && + Ref.NameLoc != FD->getLocation()) return; for (const NamedDecl *ND : Ref.Targets) { @@ -293,9 +293,7 @@ public: auto FuncDef = getFunctionSourceCode(Source, InsertionPoint->EnclosingNamespace); if (!FuncDef) - return llvm::createStringError( - llvm::inconvertibleErrorCode(), - "Couldn't get full source for function definition."); + return FuncDef.takeError(); SourceManagerForFile SMFF(*CCFile, Contents); const tooling::Replacement InsertFunctionDef( diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp index 9710465..1149a0e 100644 --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -2004,7 +2004,7 @@ TEST_F(DefineOutlineTest, QualifyReturnValue) { Bar foo() ; }; })cpp", - "a::Foo::Bar foo() { return {}; }\n"}, + "a::Foo::Bar a::Foo::foo() { return {}; }\n"}, {R"cpp( class Foo; Foo fo^o() { return; })cpp", @@ -2022,6 +2022,58 @@ TEST_F(DefineOutlineTest, QualifyReturnValue) { } } +TEST_F(DefineOutlineTest, QualifyFunctionName) { + FileName = "Test.hpp"; + struct { + llvm::StringRef TestHeader; + llvm::StringRef TestSource; + llvm::StringRef ExpectedHeader; + llvm::StringRef ExpectedSource; + } Cases[] = { + { + R"cpp( + namespace a { + namespace b { + class Foo { + void fo^o() {} + }; + } + })cpp", + "", + R"cpp( + namespace a { + namespace b { + class Foo { + void foo() ; + }; + } + })cpp", + "void a::b::Foo::foo() {}\n", + }, + { + "namespace a { namespace b { void f^oo() {} } }", + "namespace a{}", + "namespace a { namespace b { void foo() ; } }", + "namespace a{void b::foo() {} }", + }, + { + "namespace a { namespace b { void f^oo() {} } }", + "using namespace a;", + "namespace a { namespace b { void foo() ; } }", + // FIXME: Take using namespace directives in the source file into + // account. This can be spelled as b::foo instead. + "using namespace a;void a::b::foo() {} ", + }, + }; + llvm::StringMap EditedFiles; + for (auto &Case : Cases) { + ExtraFiles["Test.cpp"] = Case.TestSource; + EXPECT_EQ(apply(Case.TestHeader, &EditedFiles), Case.ExpectedHeader); + EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents( + testPath("Test.cpp"), Case.ExpectedSource))) + << Case.TestHeader; + } +} } // namespace } // namespace clangd } // namespace clang