Skip to content

Commit a209a80

Browse files
committed
[clangd] Delete ctor initializers while moving functions out-of-line
Summary: Currently we only delete function body from declaration, in addition to that we should also drop ctor initializers. Unfortunately CXXConstructorDecl doesn't store the location of `:` before initializers, therefore we make use of token buffer to figure out where to start deletion. Fixes clangd/clangd#220 Reviewers: hokein, ilya-biryukov Subscribers: MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D71188
1 parent 898d7a0 commit a209a80

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "clang/AST/ASTTypeTraits.h"
1919
#include "clang/AST/Decl.h"
2020
#include "clang/AST/DeclBase.h"
21+
#include "clang/AST/DeclCXX.h"
2122
#include "clang/AST/DeclTemplate.h"
2223
#include "clang/AST/Stmt.h"
2324
#include "clang/Basic/SourceLocation.h"
@@ -141,6 +142,7 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD,
141142
// Contains function signature, except defaulted parameter arguments, body and
142143
// template parameters if applicable. No need to qualify parameters, as they are
143144
// looked up in the context containing the function/method.
145+
// FIXME: Drop attributes in function signature.
144146
llvm::Expected<std::string>
145147
getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
146148
const syntax::TokenBuffer &TokBuf) {
@@ -238,6 +240,45 @@ getInsertionPoint(llvm::StringRef Contents, llvm::StringRef QualifiedName,
238240
return InsertionPoint{Region.EnclosingNamespace, *Offset};
239241
}
240242

243+
// Returns the range that should be deleted from declaration, which always
244+
// contains function body. In addition to that it might contain constructor
245+
// initializers.
246+
SourceRange getDeletionRange(const FunctionDecl *FD,
247+
const syntax::TokenBuffer &TokBuf) {
248+
auto DeletionRange = FD->getBody()->getSourceRange();
249+
if (auto *CD = llvm::dyn_cast<CXXConstructorDecl>(FD)) {
250+
const auto &SM = TokBuf.sourceManager();
251+
// AST doesn't contain the location for ":" in ctor initializers. Therefore
252+
// we find it by finding the first ":" before the first ctor initializer.
253+
SourceLocation InitStart;
254+
// Find the first initializer.
255+
for (const auto *CInit : CD->inits()) {
256+
// We don't care about in-class initializers.
257+
if (CInit->isInClassMemberInitializer())
258+
continue;
259+
if (InitStart.isInvalid() ||
260+
SM.isBeforeInTranslationUnit(CInit->getSourceLocation(), InitStart))
261+
InitStart = CInit->getSourceLocation();
262+
}
263+
if (InitStart.isValid()) {
264+
auto Toks = TokBuf.expandedTokens(CD->getSourceRange());
265+
// Drop any tokens after the initializer.
266+
Toks = Toks.take_while([&TokBuf, &InitStart](const syntax::Token &Tok) {
267+
return TokBuf.sourceManager().isBeforeInTranslationUnit(Tok.location(),
268+
InitStart);
269+
});
270+
// Look for the first colon.
271+
auto Tok =
272+
llvm::find_if(llvm::reverse(Toks), [](const syntax::Token &Tok) {
273+
return Tok.kind() == tok::colon;
274+
});
275+
assert(Tok != Toks.rend());
276+
DeletionRange.setBegin(Tok->location());
277+
}
278+
}
279+
return DeletionRange;
280+
}
281+
241282
/// Moves definition of a function/method to an appropriate implementation file.
242283
///
243284
/// Before:
@@ -338,7 +379,8 @@ class DefineOutline : public Tweak {
338379
const tooling::Replacement DeleteFuncBody(
339380
Sel.AST.getSourceManager(),
340381
CharSourceRange::getTokenRange(*toHalfOpenFileRange(
341-
SM, Sel.AST.getLangOpts(), Source->getBody()->getSourceRange())),
382+
SM, Sel.AST.getLangOpts(),
383+
getDeletionRange(Source, Sel.AST.getTokens()))),
342384
";");
343385
auto HeaderFE = Effect::fileEdit(SM, SM.getMainFileID(),
344386
tooling::Replacements(DeleteFuncBody));

clang-tools-extra/clangd/unittests/TweakTests.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,6 +1979,24 @@ TEST_F(DefineOutlineTest, ApplyTest) {
19791979
"void foo(int x, int y = 5, int = 2, int (*foo)(int) = nullptr) ;",
19801980
"void foo(int x, int y , int , int (*foo)(int) ) {}",
19811981
},
1982+
// Ctor initializers.
1983+
{
1984+
R"cpp(
1985+
class Foo {
1986+
int y = 2;
1987+
F^oo(int z) __attribute__((weak)) : bar(2){}
1988+
int bar;
1989+
int z = 2;
1990+
};)cpp",
1991+
R"cpp(
1992+
class Foo {
1993+
int y = 2;
1994+
Foo(int z) __attribute__((weak)) ;
1995+
int bar;
1996+
int z = 2;
1997+
};)cpp",
1998+
"Foo::Foo(int z) __attribute__((weak)) : bar(2){}\n",
1999+
},
19822000
};
19832001
for (const auto &Case : Cases) {
19842002
SCOPED_TRACE(Case.Test);

0 commit comments

Comments
 (0)