diff options
author | Kadir Cetinkaya <kadircet@google.com> | 2019-11-22 13:56:02 +0100 |
---|---|---|
committer | Kadir Cetinkaya <kadircet@google.com> | 2019-12-04 08:21:09 +0100 |
commit | ddcce0f3d665b66a8232a80ca918d349c8485fe4 (patch) | |
tree | 67b5a95aff09a4dab967eed40bc287f3d7d8bbf6 | |
parent | e4609ec0e8cfddf697ffc3eccf9bef6a830bc6f0 (diff) | |
download | bcm5719-llvm-ddcce0f3d665b66a8232a80ca918d349c8485fe4.tar.gz bcm5719-llvm-ddcce0f3d665b66a8232a80ca918d349c8485fe4.zip |
[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
-rw-r--r-- | clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp | 14 | ||||
-rw-r--r-- | clang-tools-extra/clangd/unittests/TweakTests.cpp | 54 |
2 files changed, 59 insertions, 9 deletions
diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp index 1bd70b72aa1..6b44edb8cd7 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<std::string> 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 9710465335d..1149a0e6497 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<std::string> 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 |