16 #include "clang/AST/ASTContext.h"
17 #include "clang/AST/ASTTypeTraits.h"
18 #include "clang/AST/Decl.h"
19 #include "clang/AST/DeclBase.h"
20 #include "clang/AST/DeclCXX.h"
21 #include "clang/AST/DeclTemplate.h"
22 #include "clang/AST/Expr.h"
23 #include "clang/AST/ExprCXX.h"
24 #include "clang/AST/NestedNameSpecifier.h"
25 #include "clang/AST/PrettyPrinter.h"
26 #include "clang/AST/RecursiveASTVisitor.h"
27 #include "clang/AST/Stmt.h"
28 #include "clang/AST/TemplateBase.h"
29 #include "clang/AST/Type.h"
30 #include "clang/AST/TypeLoc.h"
31 #include "clang/Basic/LangOptions.h"
32 #include "clang/Basic/SourceLocation.h"
33 #include "clang/Basic/SourceManager.h"
34 #include "clang/Basic/TokenKinds.h"
35 #include "clang/Driver/Types.h"
36 #include "clang/Index/IndexDataConsumer.h"
37 #include "clang/Index/IndexSymbol.h"
38 #include "clang/Index/IndexingAction.h"
39 #include "clang/Lex/Lexer.h"
40 #include "clang/Lex/Preprocessor.h"
41 #include "clang/Lex/Token.h"
42 #include "clang/Sema/Lookup.h"
43 #include "clang/Sema/Sema.h"
44 #include "clang/Tooling/Core/Replacement.h"
45 #include "llvm/ADT/DenseMap.h"
46 #include "llvm/ADT/DenseSet.h"
47 #include "llvm/ADT/None.h"
48 #include "llvm/ADT/Optional.h"
49 #include "llvm/ADT/STLExtras.h"
50 #include "llvm/ADT/SmallVector.h"
51 #include "llvm/ADT/StringRef.h"
52 #include "llvm/Support/Casting.h"
53 #include "llvm/Support/Error.h"
54 #include "llvm/Support/FormatAdapters.h"
55 #include "llvm/Support/FormatVariadic.h"
56 #include "llvm/Support/Signals.h"
57 #include "llvm/Support/raw_ostream.h"
61 #include <unordered_map>
72 llvm::Optional<SourceLocation> getSemicolonForDecl(
const FunctionDecl *FD) {
73 const SourceManager &SM = FD->getASTContext().getSourceManager();
74 const LangOptions &LangOpts = FD->getASTContext().getLangOpts();
76 SourceLocation CurLoc = FD->getEndLoc();
77 auto NextTok = Lexer::findNextToken(CurLoc, SM, LangOpts);
78 if (!NextTok || !NextTok->is(tok::semi))
80 return NextTok->getLocation();
86 const FunctionDecl *getSelectedFunction(
const SelectionTree::Node *SelNode) {
87 const ast_type_traits::DynTypedNode &AstNode = SelNode->ASTNode;
88 if (
const FunctionDecl *FD = AstNode.get<FunctionDecl>())
90 if (AstNode.get<CompoundStmt>() &&
92 if (
const SelectionTree::Node *P = SelNode->Parent)
93 return P->ASTNode.get<FunctionDecl>();
101 bool checkDeclsAreVisible(
const llvm::DenseSet<const Decl *> &DeclRefs,
102 const FunctionDecl *Target,
const SourceManager &SM) {
103 SourceLocation TargetLoc = Target->getLocation();
106 const RecordDecl *
Class =
nullptr;
107 if (
const auto *
MD = llvm::dyn_cast<CXXMethodDecl>(Target))
110 for (
const auto *DR : DeclRefs) {
112 const Decl *D = DR->getCanonicalDecl();
115 SourceLocation DeclLoc = D->getLocation();
118 if (!SM.isWrittenInSameFile(DeclLoc, TargetLoc))
122 if (SM.isBeforeInTranslationUnit(DeclLoc, TargetLoc))
128 const RecordDecl *
Parent =
nullptr;
129 if (
const auto *
MD = llvm::dyn_cast<CXXMethodDecl>(D))
131 else if (
const auto *FD = llvm::dyn_cast<FieldDecl>(D))
141 llvm::Expected<std::string> qualifyAllDecls(
const FunctionDecl *FD,
142 const FunctionDecl *Target) {
160 auto *TargetContext = Target->getLexicalDeclContext();
161 const SourceManager &SM = FD->getASTContext().getSourceManager();
163 tooling::Replacements Replacements;
164 bool HadErrors =
false;
172 if (Ref.Targets.empty())
175 if (Ref.NameLoc.isMacroID())
178 for (
const NamedDecl *ND : Ref.Targets) {
179 if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) {
180 elog(
"define inline: Targets from multiple contexts: {0}, {1}",
187 const NamedDecl *ND = Ref.Targets.front();
196 if (!ND->getDeclContext()->isNamespace())
200 FD->getASTContext(), TargetContext, Target->getBeginLoc(), ND);
201 if (
auto Err = Replacements.add(
202 tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier))) {
204 elog(
"define inline: Failed to add quals: {0}", std::move(Err));
209 return llvm::createStringError(
210 llvm::inconvertibleErrorCode(),
211 "define inline: Failed to compute qualifiers see logs for details.");
216 SM, FD->getASTContext().getLangOpts(), FD->getBody()->getSourceRange());
218 return llvm::createStringError(llvm::inconvertibleErrorCode(),
219 "Couldn't get range func body.");
221 unsigned BodyBegin = SM.getFileOffset(OrigBodyRange->getBegin());
222 unsigned BodyEnd = Replacements.getShiftedCodePosition(
223 SM.getFileOffset(OrigBodyRange->getEnd()));
226 auto QualifiedFunc = tooling::applyAllReplacements(
227 SM.getBufferData(SM.getFileID(OrigBodyRange->getBegin())), Replacements);
229 return QualifiedFunc.takeError();
230 return QualifiedFunc->substr(BodyBegin, BodyEnd - BodyBegin + 1);
235 llvm::Expected<tooling::Replacements>
236 renameParameters(
const FunctionDecl *Dest,
const FunctionDecl *Source) {
237 llvm::DenseMap<const Decl *, std::string> ParamToNewName;
238 llvm::DenseMap<const NamedDecl *, std::vector<SourceLocation>> RefLocs;
239 auto HandleParam = [&](
const NamedDecl *DestParam,
240 const NamedDecl *SourceParam) {
242 if (DestParam->getName() == SourceParam->getName())
247 if (DestParam->getName().empty()) {
248 RefLocs[DestParam].push_back(DestParam->getLocation());
253 NewName.append(std::string(SourceParam->getName()));
254 ParamToNewName[DestParam->getCanonicalDecl()] = std::move(NewName);
258 auto *DestTempl = Dest->getDescribedFunctionTemplate();
259 auto *SourceTempl = Source->getDescribedFunctionTemplate();
260 assert(
bool(DestTempl) ==
bool(SourceTempl));
262 const auto *DestTPL = DestTempl->getTemplateParameters();
263 const auto *SourceTPL = SourceTempl->getTemplateParameters();
264 assert(DestTPL->size() == SourceTPL->size());
266 for (
size_t I = 0, EP = DestTPL->size(); I != EP; ++I)
267 HandleParam(DestTPL->getParam(I), SourceTPL->getParam(I));
271 assert(Dest->param_size() == Source->param_size());
272 for (
size_t I = 0,
E = Dest->param_size(); I !=
E; ++I)
273 HandleParam(Dest->getParamDecl(I), Source->getParamDecl(I));
275 const SourceManager &SM = Dest->getASTContext().getSourceManager();
276 const LangOptions &LangOpts = Dest->getASTContext().getLangOpts();
282 DestTempl ? llvm::dyn_cast<Decl>(DestTempl) : llvm::dyn_cast<Decl>(Dest),
283 [&](ReferenceLoc Ref) {
284 if (Ref.Targets.size() != 1)
287 llvm::cast<NamedDecl>(Ref.Targets.front()->getCanonicalDecl());
288 auto It = ParamToNewName.find(Target);
289 if (It == ParamToNewName.end())
291 RefLocs[Target].push_back(Ref.NameLoc);
295 tooling::Replacements Replacements;
296 for (
auto &
Entry : RefLocs) {
297 const auto *OldDecl =
Entry.first;
298 llvm::StringRef OldName = OldDecl->getName();
299 llvm::StringRef NewName = ParamToNewName[OldDecl];
300 for (SourceLocation RefLoc :
Entry.second) {
301 CharSourceRange ReplaceRange;
305 ReplaceRange = CharSourceRange::getCharRange(RefLoc, RefLoc);
307 ReplaceRange = CharSourceRange::getTokenRange(RefLoc, RefLoc);
310 if (RefLoc.isMacroID()) {
311 ReplaceRange = Lexer::makeFileCharRange(ReplaceRange, SM, LangOpts);
313 if (ReplaceRange.isInvalid()) {
314 auto Err = llvm::createStringError(
315 llvm::inconvertibleErrorCode(),
316 "Cant rename parameter inside macro body.");
317 elog(
"define inline: {0}", Err);
318 return std::move(Err);
322 if (
auto Err = Replacements.add(
323 tooling::Replacement(SM, ReplaceRange, NewName))) {
324 elog(
"define inline: Couldn't replace parameter name for {0} to {1}: "
326 OldName, NewName, Err);
327 return std::move(Err);
340 const FunctionDecl *findTarget(
const FunctionDecl *FD) {
341 auto CanonDecl = FD->getCanonicalDecl();
342 if (!FD->isFunctionTemplateSpecialization())
348 while (PrevDecl->getPreviousDecl() != CanonDecl) {
349 PrevDecl = PrevDecl->getPreviousDecl();
350 assert(PrevDecl &&
"Found specialization without template decl");
357 const SourceLocation getBeginLoc(
const FunctionDecl *FD) {
359 if (
auto *FTD = FD->getDescribedFunctionTemplate())
360 return FTD->getBeginLoc();
361 return FD->getBeginLoc();
364 llvm::Optional<tooling::Replacement>
365 addInlineIfInHeader(
const FunctionDecl *FD) {
367 if (FD->isInlined() || llvm::isa<CXXMethodDecl>(FD))
370 if (FD->isTemplated() && !FD->isFunctionTemplateSpecialization())
373 const SourceManager &SM = FD->getASTContext().getSourceManager();
374 llvm::StringRef
FileName = SM.getFilename(FD->getLocation());
380 return tooling::Replacement(SM, FD->getInnerLocStart(), 0,
"inline ");
398 class DefineInline :
public Tweak {
400 const char *id() const override final;
402 Intent intent()
const override {
return Intent::Refactor; }
403 std::string title()
const override {
404 return "Move function body to declaration";
409 bool prepare(
const Selection &Sel)
override {
410 const SelectionTree::Node *SelNode = Sel.ASTSelection.commonAncestor();
413 Source = getSelectedFunction(SelNode);
414 if (!Source || !Source->hasBody())
419 if (
auto *
MD = llvm::dyn_cast<CXXMethodDecl>(Source)) {
420 if (
MD->getParent()->isTemplated())
425 if (Source->getBody()->getBeginLoc().isMacroID() ||
426 Source->getBody()->getEndLoc().isMacroID())
429 Target = findTarget(Source);
430 if (Target == Source) {
442 Sel.AST->getSourceManager()))
448 Expected<Effect> apply(
const Selection &Sel)
override {
449 const auto &
AST = Sel.AST->getASTContext();
450 const auto &SM =
AST.getSourceManager();
452 auto Semicolon = getSemicolonForDecl(Target);
454 return llvm::createStringError(
455 llvm::inconvertibleErrorCode(),
456 "Couldn't find semicolon for target declaration.");
459 auto AddInlineIfNecessary = addInlineIfInHeader(Target);
460 auto ParamReplacements = renameParameters(Target, Source);
461 if (!ParamReplacements)
462 return ParamReplacements.takeError();
464 auto QualifiedBody = qualifyAllDecls(Source, Target);
466 return QualifiedBody.takeError();
468 const tooling::Replacement SemicolonToFuncBody(SM, *Semicolon, 1,
470 tooling::Replacements TargetFileReplacements(SemicolonToFuncBody);
471 TargetFileReplacements = TargetFileReplacements.merge(*ParamReplacements);
472 if (AddInlineIfNecessary) {
473 if (
auto Err = TargetFileReplacements.add(*AddInlineIfNecessary))
474 return std::move(Err);
478 SM,
AST.getLangOpts(),
479 SM.getExpansionRange(CharSourceRange::getCharRange(getBeginLoc(Source),
480 Source->getEndLoc()))
483 return llvm::createStringError(llvm::inconvertibleErrorCode(),
484 "Couldn't get range for the source.");
486 unsigned int SourceLen = SM.getFileOffset(DefRange->getEnd()) -
487 SM.getFileOffset(DefRange->getBegin());
488 const tooling::Replacement DeleteFuncBody(SM, DefRange->getBegin(),
491 llvm::SmallVector<std::pair<std::string, Edit>, 2> Edits;
493 auto FE = Effect::fileEdit(SM, SM.getFileID(*Semicolon),
494 std::move(TargetFileReplacements));
496 return FE.takeError();
497 Edits.push_back(std::move(*FE));
500 if (!SM.isWrittenInSameFile(DefRange->getBegin(),
501 SM.getExpansionLoc(Target->getBeginLoc()))) {
503 auto FE = Effect::fileEdit(SM, SM.getFileID(Sel.Cursor),
504 tooling::Replacements(DeleteFuncBody));
506 return FE.takeError();
507 Edits.push_back(std::move(*FE));
510 if (
auto Err = Edits.front().second.Replacements.add(DeleteFuncBody))
511 return std::move(Err);
515 for (
auto &Pair : Edits)
516 E.ApplyEdits.try_emplace(std::move(Pair.first), std::move(Pair.second));
521 const FunctionDecl *Source =
nullptr;
522 const FunctionDecl *Target =
nullptr;