Skip to content

Commit

Permalink
[clangd] Define out-of-line qualify function name
Browse files Browse the repository at this point in the history
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
  • Loading branch information
kadircet committed Dec 4, 2019
1 parent e4609ec commit ddcce0f
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 9 deletions.
14 changes: 6 additions & 8 deletions clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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) {
Expand Down Expand Up @@ -293,9 +293,7 @@ class DefineOutline : public Tweak {
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(
Expand Down
54 changes: 53 additions & 1 deletion clang-tools-extra/clangd/unittests/TweakTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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

0 comments on commit ddcce0f

Please sign in to comment.