Skip to content

Commit 0cfa6e2

Browse files
authored
[clangd] Let DefineOutline tweak handle member functions (#95235)
... of class templates.
1 parent 828d72b commit 0cfa6e2

File tree

4 files changed

+94
-20
lines changed

4 files changed

+94
-20
lines changed

clang-tools-extra/clangd/AST.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,13 @@ getQualification(ASTContext &Context, const DeclContext *DestContext,
144144
// since we stored inner-most parent first.
145145
std::string Result;
146146
llvm::raw_string_ostream OS(Result);
147-
for (const auto *Parent : llvm::reverse(Parents))
147+
for (const auto *Parent : llvm::reverse(Parents)) {
148+
if (Parent != *Parents.rbegin() && Parent->isDependent() &&
149+
Parent->getAsRecordDecl() &&
150+
Parent->getAsRecordDecl()->getDescribedClassTemplate())
151+
OS << "template ";
148152
Parent->print(OS, Context.getPrintingPolicy());
153+
}
149154
return OS.str();
150155
}
151156

clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,28 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD,
128128
SM.getBufferData(SM.getMainFileID()), Replacements);
129129
if (!QualifiedFunc)
130130
return QualifiedFunc.takeError();
131-
return QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);
131+
132+
std::string TemplatePrefix;
133+
if (auto *MD = llvm::dyn_cast<CXXMethodDecl>(FD)) {
134+
for (const CXXRecordDecl *Parent = MD->getParent(); Parent;
135+
Parent =
136+
llvm::dyn_cast_or_null<const CXXRecordDecl>(Parent->getParent())) {
137+
if (const TemplateParameterList *Params =
138+
Parent->getDescribedTemplateParams()) {
139+
std::string S;
140+
llvm::raw_string_ostream Stream(S);
141+
Params->print(Stream, FD->getASTContext());
142+
if (!S.empty())
143+
*S.rbegin() = '\n'; // Replace space with newline
144+
TemplatePrefix.insert(0, S);
145+
}
146+
}
147+
}
148+
149+
auto Source = QualifiedFunc->substr(FuncBegin, FuncEnd - FuncBegin + 1);
150+
if (!TemplatePrefix.empty())
151+
Source.insert(0, TemplatePrefix);
152+
return Source;
132153
}
133154

134155
// Returns replacements to delete tokens with kind `Kind` in the range
@@ -212,9 +233,13 @@ getFunctionSourceCode(const FunctionDecl *FD, const DeclContext *TargetContext,
212233
}
213234
}
214235
const NamedDecl *ND = Ref.Targets.front();
215-
const std::string Qualifier =
236+
std::string Qualifier =
216237
getQualification(AST, TargetContext,
217238
SM.getLocForStartOfFile(SM.getMainFileID()), ND);
239+
if (ND->getDeclContext()->isDependentContext() &&
240+
llvm::isa<TypeDecl>(ND)) {
241+
Qualifier.insert(0, "typename ");
242+
}
218243
if (auto Err = DeclarationCleanups.add(
219244
tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier)))
220245
Errors = llvm::joinErrors(std::move(Errors), std::move(Err));
@@ -407,10 +432,23 @@ class DefineOutline : public Tweak {
407432
return !SameFile;
408433
}
409434

410-
// Bail out in templated classes, as it is hard to spell the class name,
411-
// i.e if the template parameter is unnamed.
412-
if (MD->getParent()->isTemplated())
413-
return false;
435+
for (const CXXRecordDecl *Parent = MD->getParent(); Parent;
436+
Parent =
437+
llvm::dyn_cast_or_null<const CXXRecordDecl>(Parent->getParent())) {
438+
if (const TemplateParameterList *Params =
439+
Parent->getDescribedTemplateParams()) {
440+
441+
// Class template member functions must be defined in the
442+
// same file.
443+
SameFile = true;
444+
445+
// Bail out if the template parameter is unnamed.
446+
for (NamedDecl *P : *Params) {
447+
if (!P->getIdentifier())
448+
return false;
449+
}
450+
}
451+
}
414452

415453
// The refactoring is meaningless for unnamed classes and namespaces,
416454
// unless we're outlining in the same file

clang-tools-extra/clangd/unittests/tweaks/DefineOutlineTests.cpp

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ TEST_F(DefineOutlineTest, TriggersOnFunctionDecl) {
105105
F^oo(const Foo&) = delete;
106106
};)cpp");
107107

108-
// Not available within templated classes, as it is hard to spell class name
109-
// out-of-line in such cases.
108+
// Not available within templated classes with unnamed parameters, as it is
109+
// hard to spell class name out-of-line in such cases.
110110
EXPECT_UNAVAILABLE(R"cpp(
111111
template <typename> struct Foo { void fo^o(){} };
112112
)cpp");
@@ -154,7 +154,6 @@ TEST_F(DefineOutlineTest, FailsWithoutSource) {
154154
}
155155

156156
TEST_F(DefineOutlineTest, ApplyTest) {
157-
llvm::StringMap<std::string> EditedFiles;
158157
ExtraFiles["Test.cpp"] = "";
159158
FileName = "Test.hpp";
160159

@@ -229,17 +228,18 @@ TEST_F(DefineOutlineTest, ApplyTest) {
229228
// Ctor initializer with attribute.
230229
{
231230
R"cpp(
232-
class Foo {
233-
F^oo(int z) __attribute__((weak)) : bar(2){}
231+
template <typename T> class Foo {
232+
F^oo(T z) __attribute__((weak)) : bar(2){}
234233
int bar;
235234
};)cpp",
236235
R"cpp(
237-
class Foo {
238-
Foo(int z) __attribute__((weak)) ;
236+
template <typename T> class Foo {
237+
Foo(T z) __attribute__((weak)) ;
239238
int bar;
240-
};)cpp",
241-
"Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n",
242-
},
239+
};template <typename T>
240+
Foo<T>::Foo(T z) __attribute__((weak)) : bar(2){}
241+
)cpp",
242+
""},
243243
// Virt specifiers.
244244
{
245245
R"cpp(
@@ -369,7 +369,31 @@ TEST_F(DefineOutlineTest, ApplyTest) {
369369
};)cpp",
370370
" void A::foo(int) {}\n",
371371
},
372-
// Destrctors
372+
// Complex class template
373+
{
374+
R"cpp(
375+
template <typename T, typename ...U> struct O1 {
376+
template <class V, int A> struct O2 {
377+
enum E { E1, E2 };
378+
struct I {
379+
E f^oo(T, U..., V, E) { return E1; }
380+
};
381+
};
382+
};)cpp",
383+
R"cpp(
384+
template <typename T, typename ...U> struct O1 {
385+
template <class V, int A> struct O2 {
386+
enum E { E1, E2 };
387+
struct I {
388+
E foo(T, U..., V, E) ;
389+
};
390+
};
391+
};template <typename T, typename ...U>
392+
template <class V, int A>
393+
typename O1<T, U...>::template O2<V, A>::E O1<T, U...>::template O2<V, A>::I::foo(T, U..., V, E) { return E1; }
394+
)cpp",
395+
""},
396+
// Destructors
373397
{
374398
"class A { ~A^(){} };",
375399
"class A { ~A(); };",
@@ -378,9 +402,14 @@ TEST_F(DefineOutlineTest, ApplyTest) {
378402
};
379403
for (const auto &Case : Cases) {
380404
SCOPED_TRACE(Case.Test);
405+
llvm::StringMap<std::string> EditedFiles;
381406
EXPECT_EQ(apply(Case.Test, &EditedFiles), Case.ExpectedHeader);
382-
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
383-
testPath("Test.cpp"), Case.ExpectedSource)));
407+
if (Case.ExpectedSource.empty()) {
408+
EXPECT_TRUE(EditedFiles.empty());
409+
} else {
410+
EXPECT_THAT(EditedFiles, testing::ElementsAre(FileWithContents(
411+
testPath("Test.cpp"), Case.ExpectedSource)));
412+
}
384413
}
385414
}
386415

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ Objective-C
8888
Miscellaneous
8989
^^^^^^^^^^^^^
9090

91+
- The DefineOutline tweak now handles member functions of class templates.
92+
9193
Improvements to clang-doc
9294
-------------------------
9395

0 commit comments

Comments
 (0)