|
18 | 18 | #include "clang/AST/ASTTypeTraits.h"
|
19 | 19 | #include "clang/AST/Decl.h"
|
20 | 20 | #include "clang/AST/DeclBase.h"
|
| 21 | +#include "clang/AST/DeclCXX.h" |
21 | 22 | #include "clang/AST/DeclTemplate.h"
|
22 | 23 | #include "clang/AST/Stmt.h"
|
23 | 24 | #include "clang/Basic/SourceLocation.h"
|
@@ -141,6 +142,7 @@ getFunctionSourceAfterReplacements(const FunctionDecl *FD,
|
141 | 142 | // Contains function signature, except defaulted parameter arguments, body and
|
142 | 143 | // template parameters if applicable. No need to qualify parameters, as they are
|
143 | 144 | // looked up in the context containing the function/method.
|
| 145 | +// FIXME: Drop attributes in function signature. |
144 | 146 | llvm::Expected<std::string>
|
145 | 147 | getFunctionSourceCode(const FunctionDecl *FD, llvm::StringRef TargetNamespace,
|
146 | 148 | const syntax::TokenBuffer &TokBuf) {
|
@@ -238,6 +240,45 @@ getInsertionPoint(llvm::StringRef Contents, llvm::StringRef QualifiedName,
|
238 | 240 | return InsertionPoint{Region.EnclosingNamespace, *Offset};
|
239 | 241 | }
|
240 | 242 |
|
| 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 | + |
241 | 282 | /// Moves definition of a function/method to an appropriate implementation file.
|
242 | 283 | ///
|
243 | 284 | /// Before:
|
@@ -338,7 +379,8 @@ class DefineOutline : public Tweak {
|
338 | 379 | const tooling::Replacement DeleteFuncBody(
|
339 | 380 | Sel.AST.getSourceManager(),
|
340 | 381 | CharSourceRange::getTokenRange(*toHalfOpenFileRange(
|
341 |
| - SM, Sel.AST.getLangOpts(), Source->getBody()->getSourceRange())), |
| 382 | + SM, Sel.AST.getLangOpts(), |
| 383 | + getDeletionRange(Source, Sel.AST.getTokens()))), |
342 | 384 | ";");
|
343 | 385 | auto HeaderFE = Effect::fileEdit(SM, SM.getMainFileID(),
|
344 | 386 | tooling::Replacements(DeleteFuncBody));
|
|
0 commit comments