15ab9a850SKadir Cetinkaya //===--- DefineInline.cpp ----------------------------------------*- C++-*-===//
25ab9a850SKadir Cetinkaya //
35ab9a850SKadir Cetinkaya // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ab9a850SKadir Cetinkaya // See https://llvm.org/LICENSE.txt for license information.
55ab9a850SKadir Cetinkaya // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ab9a850SKadir Cetinkaya //
75ab9a850SKadir Cetinkaya //===----------------------------------------------------------------------===//
85ab9a850SKadir Cetinkaya
95ab9a850SKadir Cetinkaya #include "AST.h"
103d65def1SKadir Cetinkaya #include "FindTarget.h"
115ab9a850SKadir Cetinkaya #include "Selection.h"
125ab9a850SKadir Cetinkaya #include "SourceCode.h"
135ab9a850SKadir Cetinkaya #include "XRefs.h"
145ab9a850SKadir Cetinkaya #include "refactor/Tweak.h"
15ad97ccf6SSam McCall #include "support/Logger.h"
165ab9a850SKadir Cetinkaya #include "clang/AST/ASTContext.h"
175ab9a850SKadir Cetinkaya #include "clang/AST/ASTTypeTraits.h"
185ab9a850SKadir Cetinkaya #include "clang/AST/Decl.h"
195ab9a850SKadir Cetinkaya #include "clang/AST/DeclBase.h"
205ab9a850SKadir Cetinkaya #include "clang/AST/DeclCXX.h"
215ab9a850SKadir Cetinkaya #include "clang/AST/DeclTemplate.h"
225ab9a850SKadir Cetinkaya #include "clang/AST/NestedNameSpecifier.h"
235ab9a850SKadir Cetinkaya #include "clang/AST/Stmt.h"
245ab9a850SKadir Cetinkaya #include "clang/Basic/LangOptions.h"
255ab9a850SKadir Cetinkaya #include "clang/Basic/SourceLocation.h"
265ab9a850SKadir Cetinkaya #include "clang/Basic/SourceManager.h"
275ab9a850SKadir Cetinkaya #include "clang/Basic/TokenKinds.h"
285ab9a850SKadir Cetinkaya #include "clang/Lex/Lexer.h"
295ab9a850SKadir Cetinkaya #include "clang/Lex/Token.h"
305ab9a850SKadir Cetinkaya #include "clang/Sema/Lookup.h"
315ab9a850SKadir Cetinkaya #include "clang/Sema/Sema.h"
325ab9a850SKadir Cetinkaya #include "clang/Tooling/Core/Replacement.h"
335ab9a850SKadir Cetinkaya #include "llvm/ADT/DenseMap.h"
345ab9a850SKadir Cetinkaya #include "llvm/ADT/DenseSet.h"
353d65def1SKadir Cetinkaya #include "llvm/ADT/SmallVector.h"
365ab9a850SKadir Cetinkaya #include "llvm/ADT/StringRef.h"
375ab9a850SKadir Cetinkaya #include "llvm/Support/Casting.h"
385ab9a850SKadir Cetinkaya #include "llvm/Support/Error.h"
395ab9a850SKadir Cetinkaya #include "llvm/Support/raw_ostream.h"
405ab9a850SKadir Cetinkaya #include <cstddef>
4171f55735SKazu Hirata #include <optional>
425ab9a850SKadir Cetinkaya #include <set>
435ab9a850SKadir Cetinkaya #include <string>
445ab9a850SKadir Cetinkaya #include <unordered_map>
453d65def1SKadir Cetinkaya #include <utility>
465ab9a850SKadir Cetinkaya #include <vector>
475ab9a850SKadir Cetinkaya
485ab9a850SKadir Cetinkaya namespace clang {
495ab9a850SKadir Cetinkaya namespace clangd {
505ab9a850SKadir Cetinkaya namespace {
515ab9a850SKadir Cetinkaya
523d65def1SKadir Cetinkaya // Returns semicolon location for the given FD. Since AST doesn't contain that
533d65def1SKadir Cetinkaya // information, searches for a semicolon by lexing from end of function decl
543d65def1SKadir Cetinkaya // while skipping comments.
getSemicolonForDecl(const FunctionDecl * FD)55*f71ffd3bSKazu Hirata std::optional<SourceLocation> getSemicolonForDecl(const FunctionDecl *FD) {
563d65def1SKadir Cetinkaya const SourceManager &SM = FD->getASTContext().getSourceManager();
573d65def1SKadir Cetinkaya const LangOptions &LangOpts = FD->getASTContext().getLangOpts();
583d65def1SKadir Cetinkaya
593d65def1SKadir Cetinkaya SourceLocation CurLoc = FD->getEndLoc();
603d65def1SKadir Cetinkaya auto NextTok = Lexer::findNextToken(CurLoc, SM, LangOpts);
613d65def1SKadir Cetinkaya if (!NextTok || !NextTok->is(tok::semi))
62059a23c0SKazu Hirata return std::nullopt;
633d65def1SKadir Cetinkaya return NextTok->getLocation();
643d65def1SKadir Cetinkaya }
653d65def1SKadir Cetinkaya
665ab9a850SKadir Cetinkaya // Deduces the FunctionDecl from a selection. Requires either the function body
675ab9a850SKadir Cetinkaya // or the function decl to be selected. Returns null if none of the above
685ab9a850SKadir Cetinkaya // criteria is met.
getSelectedFunction(const SelectionTree::Node * SelNode)695ab9a850SKadir Cetinkaya const FunctionDecl *getSelectedFunction(const SelectionTree::Node *SelNode) {
70027899daSAlexander Kornienko const DynTypedNode &AstNode = SelNode->ASTNode;
715ab9a850SKadir Cetinkaya if (const FunctionDecl *FD = AstNode.get<FunctionDecl>())
725ab9a850SKadir Cetinkaya return FD;
735ab9a850SKadir Cetinkaya if (AstNode.get<CompoundStmt>() &&
745ab9a850SKadir Cetinkaya SelNode->Selected == SelectionTree::Complete) {
755ab9a850SKadir Cetinkaya if (const SelectionTree::Node *P = SelNode->Parent)
765ab9a850SKadir Cetinkaya return P->ASTNode.get<FunctionDecl>();
775ab9a850SKadir Cetinkaya }
785ab9a850SKadir Cetinkaya return nullptr;
795ab9a850SKadir Cetinkaya }
805ab9a850SKadir Cetinkaya
815ab9a850SKadir Cetinkaya // Checks the decls mentioned in Source are visible in the context of Target.
82b7ecf1c1SKazuaki Ishizaki // Achieves that by checking declarations occur before target location in
835ab9a850SKadir Cetinkaya // translation unit or declared in the same class.
checkDeclsAreVisible(const llvm::DenseSet<const Decl * > & DeclRefs,const FunctionDecl * Target,const SourceManager & SM)845ab9a850SKadir Cetinkaya bool checkDeclsAreVisible(const llvm::DenseSet<const Decl *> &DeclRefs,
855ab9a850SKadir Cetinkaya const FunctionDecl *Target, const SourceManager &SM) {
865ab9a850SKadir Cetinkaya SourceLocation TargetLoc = Target->getLocation();
875ab9a850SKadir Cetinkaya // To be used in visibility check below, decls in a class are visible
885ab9a850SKadir Cetinkaya // independent of order.
895ab9a850SKadir Cetinkaya const RecordDecl *Class = nullptr;
905ab9a850SKadir Cetinkaya if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Target))
915ab9a850SKadir Cetinkaya Class = MD->getParent();
925ab9a850SKadir Cetinkaya
935ab9a850SKadir Cetinkaya for (const auto *DR : DeclRefs) {
945ab9a850SKadir Cetinkaya // Use canonical decl, since having one decl before target is enough.
955ab9a850SKadir Cetinkaya const Decl *D = DR->getCanonicalDecl();
965ab9a850SKadir Cetinkaya if (D == Target)
975ab9a850SKadir Cetinkaya continue;
985ab9a850SKadir Cetinkaya SourceLocation DeclLoc = D->getLocation();
995ab9a850SKadir Cetinkaya
1005ab9a850SKadir Cetinkaya // FIXME: Allow declarations from different files with include insertion.
1015ab9a850SKadir Cetinkaya if (!SM.isWrittenInSameFile(DeclLoc, TargetLoc))
1025ab9a850SKadir Cetinkaya return false;
1035ab9a850SKadir Cetinkaya
1045ab9a850SKadir Cetinkaya // If declaration is before target, then it is visible.
1055ab9a850SKadir Cetinkaya if (SM.isBeforeInTranslationUnit(DeclLoc, TargetLoc))
1065ab9a850SKadir Cetinkaya continue;
1075ab9a850SKadir Cetinkaya
1085ab9a850SKadir Cetinkaya // Otherwise they need to be in same class
1095ab9a850SKadir Cetinkaya if (!Class)
1105ab9a850SKadir Cetinkaya return false;
1115ab9a850SKadir Cetinkaya const RecordDecl *Parent = nullptr;
1125ab9a850SKadir Cetinkaya if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(D))
1135ab9a850SKadir Cetinkaya Parent = MD->getParent();
1145ab9a850SKadir Cetinkaya else if (const auto *FD = llvm::dyn_cast<FieldDecl>(D))
1155ab9a850SKadir Cetinkaya Parent = FD->getParent();
1165ab9a850SKadir Cetinkaya if (Parent != Class)
1175ab9a850SKadir Cetinkaya return false;
1185ab9a850SKadir Cetinkaya }
1195ab9a850SKadir Cetinkaya return true;
1205ab9a850SKadir Cetinkaya }
1215ab9a850SKadir Cetinkaya
1225075c682SKadir Cetinkaya // Rewrites body of FD by re-spelling all of the names to make sure they are
1235075c682SKadir Cetinkaya // still valid in context of Target.
qualifyAllDecls(const FunctionDecl * FD,const FunctionDecl * Target,const HeuristicResolver * Resolver)1245075c682SKadir Cetinkaya llvm::Expected<std::string> qualifyAllDecls(const FunctionDecl *FD,
1259510b094SNathan Ridge const FunctionDecl *Target,
1269510b094SNathan Ridge const HeuristicResolver *Resolver) {
1273d65def1SKadir Cetinkaya // There are three types of spellings that needs to be qualified in a function
1283d65def1SKadir Cetinkaya // body:
1293d65def1SKadir Cetinkaya // - Types: Foo -> ns::Foo
1303d65def1SKadir Cetinkaya // - DeclRefExpr: ns2::foo() -> ns1::ns2::foo();
1313d65def1SKadir Cetinkaya // - UsingDecls:
1323d65def1SKadir Cetinkaya // using ns2::foo -> using ns1::ns2::foo
1333d65def1SKadir Cetinkaya // using namespace ns2 -> using namespace ns1::ns2
1343d65def1SKadir Cetinkaya // using ns3 = ns2 -> using ns3 = ns1::ns2
1353d65def1SKadir Cetinkaya //
1363d65def1SKadir Cetinkaya // Go over all references inside a function body to generate replacements that
1375075c682SKadir Cetinkaya // will qualify those. So that body can be moved into an arbitrary file.
138dd5571d5SKazuaki Ishizaki // We perform the qualification by qualifying the first type/decl in a
1393d65def1SKadir Cetinkaya // (un)qualified name. e.g:
1403d65def1SKadir Cetinkaya // namespace a { namespace b { class Bar{}; void foo(); } }
1413d65def1SKadir Cetinkaya // b::Bar x; -> a::b::Bar x;
1423d65def1SKadir Cetinkaya // foo(); -> a::b::foo();
1433d65def1SKadir Cetinkaya
1445075c682SKadir Cetinkaya auto *TargetContext = Target->getLexicalDeclContext();
1453d65def1SKadir Cetinkaya const SourceManager &SM = FD->getASTContext().getSourceManager();
1465075c682SKadir Cetinkaya
1473d65def1SKadir Cetinkaya tooling::Replacements Replacements;
1483d65def1SKadir Cetinkaya bool HadErrors = false;
1499510b094SNathan Ridge findExplicitReferences(
1509510b094SNathan Ridge FD->getBody(),
1519510b094SNathan Ridge [&](ReferenceLoc Ref) {
1523d65def1SKadir Cetinkaya // Since we want to qualify only the first qualifier, skip names with a
1533d65def1SKadir Cetinkaya // qualifier.
1543d65def1SKadir Cetinkaya if (Ref.Qualifier)
1553d65def1SKadir Cetinkaya return;
1569510b094SNathan Ridge // There might be no decl in dependent contexts, there's nothing much we
1579510b094SNathan Ridge // can do in such cases.
1583d65def1SKadir Cetinkaya if (Ref.Targets.empty())
1593d65def1SKadir Cetinkaya return;
1603d65def1SKadir Cetinkaya // Do not qualify names introduced by macro expansions.
1613d65def1SKadir Cetinkaya if (Ref.NameLoc.isMacroID())
1623d65def1SKadir Cetinkaya return;
1633d65def1SKadir Cetinkaya
1643d65def1SKadir Cetinkaya for (const NamedDecl *ND : Ref.Targets) {
1653d65def1SKadir Cetinkaya if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) {
1663d65def1SKadir Cetinkaya elog("define inline: Targets from multiple contexts: {0}, {1}",
1679510b094SNathan Ridge printQualifiedName(*Ref.Targets.front()),
1689510b094SNathan Ridge printQualifiedName(*ND));
1693d65def1SKadir Cetinkaya HadErrors = true;
1703d65def1SKadir Cetinkaya return;
1713d65def1SKadir Cetinkaya }
1723d65def1SKadir Cetinkaya }
1733d65def1SKadir Cetinkaya // All Targets are in the same scope, so we can safely chose first one.
1743d65def1SKadir Cetinkaya const NamedDecl *ND = Ref.Targets.front();
1753d65def1SKadir Cetinkaya // Skip anything from a non-namespace scope, these can be:
1769510b094SNathan Ridge // - Function or Method scopes, which means decl is local and doesn't
1779510b094SNathan Ridge // need
1783d65def1SKadir Cetinkaya // qualification.
1799510b094SNathan Ridge // - From Class/Struct/Union scope, which again doesn't need any
1809510b094SNathan Ridge // qualifiers,
1813d65def1SKadir Cetinkaya // rather the left side of it requires qualification, like:
1823d65def1SKadir Cetinkaya // namespace a { class Bar { public: static int x; } }
1833d65def1SKadir Cetinkaya // void foo() { Bar::x; }
1843d65def1SKadir Cetinkaya // ~~~~~ -> we need to qualify Bar not x.
1853d65def1SKadir Cetinkaya if (!ND->getDeclContext()->isNamespace())
1863d65def1SKadir Cetinkaya return;
1873d65def1SKadir Cetinkaya
1885075c682SKadir Cetinkaya const std::string Qualifier = getQualification(
1895075c682SKadir Cetinkaya FD->getASTContext(), TargetContext, Target->getBeginLoc(), ND);
1903d65def1SKadir Cetinkaya if (auto Err = Replacements.add(
1913d65def1SKadir Cetinkaya tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier))) {
1923d65def1SKadir Cetinkaya HadErrors = true;
1933d65def1SKadir Cetinkaya elog("define inline: Failed to add quals: {0}", std::move(Err));
1943d65def1SKadir Cetinkaya }
1959510b094SNathan Ridge },
1969510b094SNathan Ridge Resolver);
1973d65def1SKadir Cetinkaya
198687e1d71SSam McCall if (HadErrors)
199687e1d71SSam McCall return error(
200687e1d71SSam McCall "define inline: Failed to compute qualifiers. See logs for details.");
2013d65def1SKadir Cetinkaya
2023d65def1SKadir Cetinkaya // Get new begin and end positions for the qualified body.
2033d65def1SKadir Cetinkaya auto OrigBodyRange = toHalfOpenFileRange(
2043d65def1SKadir Cetinkaya SM, FD->getASTContext().getLangOpts(), FD->getBody()->getSourceRange());
2053d65def1SKadir Cetinkaya if (!OrigBodyRange)
206687e1d71SSam McCall return error("Couldn't get range func body.");
2073d65def1SKadir Cetinkaya
2083d65def1SKadir Cetinkaya unsigned BodyBegin = SM.getFileOffset(OrigBodyRange->getBegin());
2093d65def1SKadir Cetinkaya unsigned BodyEnd = Replacements.getShiftedCodePosition(
2103d65def1SKadir Cetinkaya SM.getFileOffset(OrigBodyRange->getEnd()));
2113d65def1SKadir Cetinkaya
2123d65def1SKadir Cetinkaya // Trim the result to function body.
2133d65def1SKadir Cetinkaya auto QualifiedFunc = tooling::applyAllReplacements(
2143d65def1SKadir Cetinkaya SM.getBufferData(SM.getFileID(OrigBodyRange->getBegin())), Replacements);
2153d65def1SKadir Cetinkaya if (!QualifiedFunc)
2163d65def1SKadir Cetinkaya return QualifiedFunc.takeError();
2173d65def1SKadir Cetinkaya return QualifiedFunc->substr(BodyBegin, BodyEnd - BodyBegin + 1);
2183d65def1SKadir Cetinkaya }
2193d65def1SKadir Cetinkaya
22071aa3f7bSKadir Cetinkaya /// Generates Replacements for changing template and function parameter names in
22171aa3f7bSKadir Cetinkaya /// \p Dest to be the same as in \p Source.
22271aa3f7bSKadir Cetinkaya llvm::Expected<tooling::Replacements>
renameParameters(const FunctionDecl * Dest,const FunctionDecl * Source,const HeuristicResolver * Resolver)2239510b094SNathan Ridge renameParameters(const FunctionDecl *Dest, const FunctionDecl *Source,
2249510b094SNathan Ridge const HeuristicResolver *Resolver) {
22571aa3f7bSKadir Cetinkaya llvm::DenseMap<const Decl *, std::string> ParamToNewName;
22671aa3f7bSKadir Cetinkaya llvm::DenseMap<const NamedDecl *, std::vector<SourceLocation>> RefLocs;
22771aa3f7bSKadir Cetinkaya auto HandleParam = [&](const NamedDecl *DestParam,
22871aa3f7bSKadir Cetinkaya const NamedDecl *SourceParam) {
22971aa3f7bSKadir Cetinkaya // No need to rename if parameters already have the same name.
23071aa3f7bSKadir Cetinkaya if (DestParam->getName() == SourceParam->getName())
23171aa3f7bSKadir Cetinkaya return;
23271aa3f7bSKadir Cetinkaya std::string NewName;
23371aa3f7bSKadir Cetinkaya // Unnamed parameters won't be visited in findExplicitReferences. So add
23471aa3f7bSKadir Cetinkaya // them here.
23571aa3f7bSKadir Cetinkaya if (DestParam->getName().empty()) {
23671aa3f7bSKadir Cetinkaya RefLocs[DestParam].push_back(DestParam->getLocation());
23771aa3f7bSKadir Cetinkaya // If decl is unnamed in destination we pad the new name to avoid gluing
23871aa3f7bSKadir Cetinkaya // with previous token, e.g. foo(int^) shouldn't turn into foo(intx).
23971aa3f7bSKadir Cetinkaya NewName = " ";
24071aa3f7bSKadir Cetinkaya }
241adcd0268SBenjamin Kramer NewName.append(std::string(SourceParam->getName()));
24271aa3f7bSKadir Cetinkaya ParamToNewName[DestParam->getCanonicalDecl()] = std::move(NewName);
24371aa3f7bSKadir Cetinkaya };
24471aa3f7bSKadir Cetinkaya
24571aa3f7bSKadir Cetinkaya // Populate mapping for template parameters.
24671aa3f7bSKadir Cetinkaya auto *DestTempl = Dest->getDescribedFunctionTemplate();
24771aa3f7bSKadir Cetinkaya auto *SourceTempl = Source->getDescribedFunctionTemplate();
24871aa3f7bSKadir Cetinkaya assert(bool(DestTempl) == bool(SourceTempl));
24971aa3f7bSKadir Cetinkaya if (DestTempl) {
25071aa3f7bSKadir Cetinkaya const auto *DestTPL = DestTempl->getTemplateParameters();
25171aa3f7bSKadir Cetinkaya const auto *SourceTPL = SourceTempl->getTemplateParameters();
25271aa3f7bSKadir Cetinkaya assert(DestTPL->size() == SourceTPL->size());
25371aa3f7bSKadir Cetinkaya
25471aa3f7bSKadir Cetinkaya for (size_t I = 0, EP = DestTPL->size(); I != EP; ++I)
25571aa3f7bSKadir Cetinkaya HandleParam(DestTPL->getParam(I), SourceTPL->getParam(I));
25671aa3f7bSKadir Cetinkaya }
25771aa3f7bSKadir Cetinkaya
25871aa3f7bSKadir Cetinkaya // Populate mapping for function params.
25971aa3f7bSKadir Cetinkaya assert(Dest->param_size() == Source->param_size());
26071aa3f7bSKadir Cetinkaya for (size_t I = 0, E = Dest->param_size(); I != E; ++I)
26171aa3f7bSKadir Cetinkaya HandleParam(Dest->getParamDecl(I), Source->getParamDecl(I));
26271aa3f7bSKadir Cetinkaya
26371aa3f7bSKadir Cetinkaya const SourceManager &SM = Dest->getASTContext().getSourceManager();
26471aa3f7bSKadir Cetinkaya const LangOptions &LangOpts = Dest->getASTContext().getLangOpts();
26571aa3f7bSKadir Cetinkaya // Collect other references in function signature, i.e parameter types and
26671aa3f7bSKadir Cetinkaya // default arguments.
26771aa3f7bSKadir Cetinkaya findExplicitReferences(
26871aa3f7bSKadir Cetinkaya // Use function template in case of templated functions to visit template
26971aa3f7bSKadir Cetinkaya // parameters.
27071aa3f7bSKadir Cetinkaya DestTempl ? llvm::dyn_cast<Decl>(DestTempl) : llvm::dyn_cast<Decl>(Dest),
27171aa3f7bSKadir Cetinkaya [&](ReferenceLoc Ref) {
27271aa3f7bSKadir Cetinkaya if (Ref.Targets.size() != 1)
27371aa3f7bSKadir Cetinkaya return;
27471aa3f7bSKadir Cetinkaya const auto *Target =
27571aa3f7bSKadir Cetinkaya llvm::cast<NamedDecl>(Ref.Targets.front()->getCanonicalDecl());
27671aa3f7bSKadir Cetinkaya auto It = ParamToNewName.find(Target);
27771aa3f7bSKadir Cetinkaya if (It == ParamToNewName.end())
27871aa3f7bSKadir Cetinkaya return;
27971aa3f7bSKadir Cetinkaya RefLocs[Target].push_back(Ref.NameLoc);
2809510b094SNathan Ridge },
2819510b094SNathan Ridge Resolver);
28271aa3f7bSKadir Cetinkaya
28371aa3f7bSKadir Cetinkaya // Now try to generate edits for all the refs.
28471aa3f7bSKadir Cetinkaya tooling::Replacements Replacements;
28571aa3f7bSKadir Cetinkaya for (auto &Entry : RefLocs) {
28671aa3f7bSKadir Cetinkaya const auto *OldDecl = Entry.first;
28771aa3f7bSKadir Cetinkaya llvm::StringRef OldName = OldDecl->getName();
28871aa3f7bSKadir Cetinkaya llvm::StringRef NewName = ParamToNewName[OldDecl];
28971aa3f7bSKadir Cetinkaya for (SourceLocation RefLoc : Entry.second) {
29071aa3f7bSKadir Cetinkaya CharSourceRange ReplaceRange;
29171aa3f7bSKadir Cetinkaya // In case of unnamed parameters, we have an empty char range, whereas we
29271aa3f7bSKadir Cetinkaya // have a tokenrange at RefLoc with named parameters.
29371aa3f7bSKadir Cetinkaya if (OldName.empty())
29471aa3f7bSKadir Cetinkaya ReplaceRange = CharSourceRange::getCharRange(RefLoc, RefLoc);
29571aa3f7bSKadir Cetinkaya else
29671aa3f7bSKadir Cetinkaya ReplaceRange = CharSourceRange::getTokenRange(RefLoc, RefLoc);
297dd5571d5SKazuaki Ishizaki // If occurrence is coming from a macro expansion, try to get back to the
29871aa3f7bSKadir Cetinkaya // file range.
29971aa3f7bSKadir Cetinkaya if (RefLoc.isMacroID()) {
30071aa3f7bSKadir Cetinkaya ReplaceRange = Lexer::makeFileCharRange(ReplaceRange, SM, LangOpts);
30171aa3f7bSKadir Cetinkaya // Bail out if we need to replace macro bodies.
30271aa3f7bSKadir Cetinkaya if (ReplaceRange.isInvalid()) {
303687e1d71SSam McCall auto Err = error("Cant rename parameter inside macro body.");
30471aa3f7bSKadir Cetinkaya elog("define inline: {0}", Err);
30571aa3f7bSKadir Cetinkaya return std::move(Err);
30671aa3f7bSKadir Cetinkaya }
30771aa3f7bSKadir Cetinkaya }
30871aa3f7bSKadir Cetinkaya
30971aa3f7bSKadir Cetinkaya if (auto Err = Replacements.add(
31071aa3f7bSKadir Cetinkaya tooling::Replacement(SM, ReplaceRange, NewName))) {
31171aa3f7bSKadir Cetinkaya elog("define inline: Couldn't replace parameter name for {0} to {1}: "
31271aa3f7bSKadir Cetinkaya "{2}",
31371aa3f7bSKadir Cetinkaya OldName, NewName, Err);
31471aa3f7bSKadir Cetinkaya return std::move(Err);
31571aa3f7bSKadir Cetinkaya }
31671aa3f7bSKadir Cetinkaya }
31771aa3f7bSKadir Cetinkaya }
31871aa3f7bSKadir Cetinkaya return Replacements;
31971aa3f7bSKadir Cetinkaya }
32071aa3f7bSKadir Cetinkaya
3215ab9a850SKadir Cetinkaya // Returns the canonical declaration for the given FunctionDecl. This will
3225ab9a850SKadir Cetinkaya // usually be the first declaration in current translation unit with the
3235ab9a850SKadir Cetinkaya // exception of template specialization.
3245ab9a850SKadir Cetinkaya // For those we return first declaration different than the canonical one.
3255ab9a850SKadir Cetinkaya // Because canonical declaration points to template decl instead of
3265ab9a850SKadir Cetinkaya // specialization.
findTarget(const FunctionDecl * FD)3275ab9a850SKadir Cetinkaya const FunctionDecl *findTarget(const FunctionDecl *FD) {
328c0e3c893SChristian Kühnel auto *CanonDecl = FD->getCanonicalDecl();
32901173285SKadir Cetinkaya if (!FD->isFunctionTemplateSpecialization() || CanonDecl == FD)
3305ab9a850SKadir Cetinkaya return CanonDecl;
3315ab9a850SKadir Cetinkaya // For specializations CanonicalDecl is the TemplatedDecl, which is not the
3325ab9a850SKadir Cetinkaya // target we want to inline into. Instead we traverse previous decls to find
3335ab9a850SKadir Cetinkaya // the first forward decl for this specialization.
334c0e3c893SChristian Kühnel auto *PrevDecl = FD;
3355ab9a850SKadir Cetinkaya while (PrevDecl->getPreviousDecl() != CanonDecl) {
3365ab9a850SKadir Cetinkaya PrevDecl = PrevDecl->getPreviousDecl();
3375ab9a850SKadir Cetinkaya assert(PrevDecl && "Found specialization without template decl");
3385ab9a850SKadir Cetinkaya }
3395ab9a850SKadir Cetinkaya return PrevDecl;
3405ab9a850SKadir Cetinkaya }
3415ab9a850SKadir Cetinkaya
342dd5571d5SKazuaki Ishizaki // Returns the beginning location for a FunctionDecl. Returns location of
3433d65def1SKadir Cetinkaya // template keyword for templated functions.
getBeginLoc(const FunctionDecl * FD)3443d65def1SKadir Cetinkaya const SourceLocation getBeginLoc(const FunctionDecl *FD) {
3453d65def1SKadir Cetinkaya // Include template parameter list.
3463d65def1SKadir Cetinkaya if (auto *FTD = FD->getDescribedFunctionTemplate())
3473d65def1SKadir Cetinkaya return FTD->getBeginLoc();
3483d65def1SKadir Cetinkaya return FD->getBeginLoc();
3493d65def1SKadir Cetinkaya }
3503d65def1SKadir Cetinkaya
351*f71ffd3bSKazu Hirata std::optional<tooling::Replacement>
addInlineIfInHeader(const FunctionDecl * FD)352087528a3SKadir Cetinkaya addInlineIfInHeader(const FunctionDecl *FD) {
353087528a3SKadir Cetinkaya // This includes inline functions and constexpr functions.
354087528a3SKadir Cetinkaya if (FD->isInlined() || llvm::isa<CXXMethodDecl>(FD))
355059a23c0SKazu Hirata return std::nullopt;
356087528a3SKadir Cetinkaya // Primary template doesn't need inline.
357087528a3SKadir Cetinkaya if (FD->isTemplated() && !FD->isFunctionTemplateSpecialization())
358059a23c0SKazu Hirata return std::nullopt;
359087528a3SKadir Cetinkaya
360087528a3SKadir Cetinkaya const SourceManager &SM = FD->getASTContext().getSourceManager();
361087528a3SKadir Cetinkaya llvm::StringRef FileName = SM.getFilename(FD->getLocation());
362087528a3SKadir Cetinkaya
363087528a3SKadir Cetinkaya // If it is not a header we don't need to mark function as "inline".
364087528a3SKadir Cetinkaya if (!isHeaderFile(FileName, FD->getASTContext().getLangOpts()))
365059a23c0SKazu Hirata return std::nullopt;
366087528a3SKadir Cetinkaya
367087528a3SKadir Cetinkaya return tooling::Replacement(SM, FD->getInnerLocStart(), 0, "inline ");
368087528a3SKadir Cetinkaya }
369087528a3SKadir Cetinkaya
3705ab9a850SKadir Cetinkaya /// Moves definition of a function/method to its declaration location.
3715ab9a850SKadir Cetinkaya /// Before:
3725ab9a850SKadir Cetinkaya /// a.h:
3735ab9a850SKadir Cetinkaya /// void foo();
3745ab9a850SKadir Cetinkaya ///
3755ab9a850SKadir Cetinkaya /// a.cc:
3765ab9a850SKadir Cetinkaya /// void foo() { return; }
3775ab9a850SKadir Cetinkaya ///
3785ab9a850SKadir Cetinkaya /// ------------------------
3795ab9a850SKadir Cetinkaya /// After:
3805ab9a850SKadir Cetinkaya /// a.h:
3815ab9a850SKadir Cetinkaya /// void foo() { return; }
3825ab9a850SKadir Cetinkaya ///
3835ab9a850SKadir Cetinkaya /// a.cc:
3845ab9a850SKadir Cetinkaya ///
3855ab9a850SKadir Cetinkaya class DefineInline : public Tweak {
3865ab9a850SKadir Cetinkaya public:
38795a932fbSKazu Hirata const char *id() const final;
3885ab9a850SKadir Cetinkaya
kind() const38917747d2eSSam McCall llvm::StringLiteral kind() const override {
39017747d2eSSam McCall return CodeAction::REFACTOR_KIND;
39117747d2eSSam McCall }
title() const3925ab9a850SKadir Cetinkaya std::string title() const override {
3935ab9a850SKadir Cetinkaya return "Move function body to declaration";
3945ab9a850SKadir Cetinkaya }
3955ab9a850SKadir Cetinkaya
3965ab9a850SKadir Cetinkaya // Returns true when selection is on a function definition that does not
3975ab9a850SKadir Cetinkaya // make use of any internal symbols.
prepare(const Selection & Sel)3985ab9a850SKadir Cetinkaya bool prepare(const Selection &Sel) override {
3995ab9a850SKadir Cetinkaya const SelectionTree::Node *SelNode = Sel.ASTSelection.commonAncestor();
4005ab9a850SKadir Cetinkaya if (!SelNode)
4015ab9a850SKadir Cetinkaya return false;
4025ab9a850SKadir Cetinkaya Source = getSelectedFunction(SelNode);
403b9213dfeSSam McCall if (!Source || !Source->hasBody())
4045ab9a850SKadir Cetinkaya return false;
4055ab9a850SKadir Cetinkaya // Only the last level of template parameter locations are not kept in AST,
4065ab9a850SKadir Cetinkaya // so if we are inlining a method that is in a templated class, there is no
4075ab9a850SKadir Cetinkaya // way to verify template parameter names. Therefore we bail out.
4085ab9a850SKadir Cetinkaya if (auto *MD = llvm::dyn_cast<CXXMethodDecl>(Source)) {
4095ab9a850SKadir Cetinkaya if (MD->getParent()->isTemplated())
4105ab9a850SKadir Cetinkaya return false;
4115ab9a850SKadir Cetinkaya }
4123d65def1SKadir Cetinkaya // If function body starts or ends inside a macro, we refuse to move it into
4133d65def1SKadir Cetinkaya // declaration location.
4143d65def1SKadir Cetinkaya if (Source->getBody()->getBeginLoc().isMacroID() ||
4153d65def1SKadir Cetinkaya Source->getBody()->getEndLoc().isMacroID())
4163d65def1SKadir Cetinkaya return false;
4175ab9a850SKadir Cetinkaya
4185ab9a850SKadir Cetinkaya Target = findTarget(Source);
4195ab9a850SKadir Cetinkaya if (Target == Source) {
4205ab9a850SKadir Cetinkaya // The only declaration is Source. No other declaration to move function
4215ab9a850SKadir Cetinkaya // body.
4225ab9a850SKadir Cetinkaya // FIXME: If we are in an implementation file, figure out a suitable
4235ab9a850SKadir Cetinkaya // location to put declaration. Possibly using other declarations in the
4245ab9a850SKadir Cetinkaya // AST.
4255ab9a850SKadir Cetinkaya return false;
4265ab9a850SKadir Cetinkaya }
4275ab9a850SKadir Cetinkaya
4285ab9a850SKadir Cetinkaya // Check if the decls referenced in function body are visible in the
4295ab9a850SKadir Cetinkaya // declaration location.
4307dc388bdSSam McCall if (!checkDeclsAreVisible(getNonLocalDeclRefs(*Sel.AST, Source), Target,
4317dc388bdSSam McCall Sel.AST->getSourceManager()))
4325ab9a850SKadir Cetinkaya return false;
4335ab9a850SKadir Cetinkaya
4345ab9a850SKadir Cetinkaya return true;
4355ab9a850SKadir Cetinkaya }
4365ab9a850SKadir Cetinkaya
apply(const Selection & Sel)4375ab9a850SKadir Cetinkaya Expected<Effect> apply(const Selection &Sel) override {
4387dc388bdSSam McCall const auto &AST = Sel.AST->getASTContext();
4393d65def1SKadir Cetinkaya const auto &SM = AST.getSourceManager();
4403d65def1SKadir Cetinkaya
4413d65def1SKadir Cetinkaya auto Semicolon = getSemicolonForDecl(Target);
442687e1d71SSam McCall if (!Semicolon)
443687e1d71SSam McCall return error("Couldn't find semicolon for target declaration.");
4443d65def1SKadir Cetinkaya
445087528a3SKadir Cetinkaya auto AddInlineIfNecessary = addInlineIfInHeader(Target);
4469510b094SNathan Ridge auto ParamReplacements =
4479510b094SNathan Ridge renameParameters(Target, Source, Sel.AST->getHeuristicResolver());
44871aa3f7bSKadir Cetinkaya if (!ParamReplacements)
44971aa3f7bSKadir Cetinkaya return ParamReplacements.takeError();
45071aa3f7bSKadir Cetinkaya
4519510b094SNathan Ridge auto QualifiedBody =
4529510b094SNathan Ridge qualifyAllDecls(Source, Target, Sel.AST->getHeuristicResolver());
4533d65def1SKadir Cetinkaya if (!QualifiedBody)
4543d65def1SKadir Cetinkaya return QualifiedBody.takeError();
4553d65def1SKadir Cetinkaya
4563d65def1SKadir Cetinkaya const tooling::Replacement SemicolonToFuncBody(SM, *Semicolon, 1,
4573d65def1SKadir Cetinkaya *QualifiedBody);
458087528a3SKadir Cetinkaya tooling::Replacements TargetFileReplacements(SemicolonToFuncBody);
459087528a3SKadir Cetinkaya TargetFileReplacements = TargetFileReplacements.merge(*ParamReplacements);
460087528a3SKadir Cetinkaya if (AddInlineIfNecessary) {
461087528a3SKadir Cetinkaya if (auto Err = TargetFileReplacements.add(*AddInlineIfNecessary))
462087528a3SKadir Cetinkaya return std::move(Err);
463087528a3SKadir Cetinkaya }
464087528a3SKadir Cetinkaya
4653d65def1SKadir Cetinkaya auto DefRange = toHalfOpenFileRange(
4663d65def1SKadir Cetinkaya SM, AST.getLangOpts(),
4673d65def1SKadir Cetinkaya SM.getExpansionRange(CharSourceRange::getCharRange(getBeginLoc(Source),
4683d65def1SKadir Cetinkaya Source->getEndLoc()))
4693d65def1SKadir Cetinkaya .getAsRange());
470687e1d71SSam McCall if (!DefRange)
471687e1d71SSam McCall return error("Couldn't get range for the source.");
4723d65def1SKadir Cetinkaya unsigned int SourceLen = SM.getFileOffset(DefRange->getEnd()) -
4733d65def1SKadir Cetinkaya SM.getFileOffset(DefRange->getBegin());
4743d65def1SKadir Cetinkaya const tooling::Replacement DeleteFuncBody(SM, DefRange->getBegin(),
4753d65def1SKadir Cetinkaya SourceLen, "");
4763d65def1SKadir Cetinkaya
477ee02e20cSKirill Bobyrev llvm::SmallVector<std::pair<std::string, Edit>> Edits;
4783d65def1SKadir Cetinkaya // Edit for Target.
479087528a3SKadir Cetinkaya auto FE = Effect::fileEdit(SM, SM.getFileID(*Semicolon),
480087528a3SKadir Cetinkaya std::move(TargetFileReplacements));
4813d65def1SKadir Cetinkaya if (!FE)
4823d65def1SKadir Cetinkaya return FE.takeError();
4833d65def1SKadir Cetinkaya Edits.push_back(std::move(*FE));
4843d65def1SKadir Cetinkaya
4853d65def1SKadir Cetinkaya // Edit for Source.
4863d65def1SKadir Cetinkaya if (!SM.isWrittenInSameFile(DefRange->getBegin(),
4873d65def1SKadir Cetinkaya SM.getExpansionLoc(Target->getBeginLoc()))) {
4883d65def1SKadir Cetinkaya // Generate a new edit if the Source and Target are in different files.
4893d65def1SKadir Cetinkaya auto FE = Effect::fileEdit(SM, SM.getFileID(Sel.Cursor),
4903d65def1SKadir Cetinkaya tooling::Replacements(DeleteFuncBody));
4913d65def1SKadir Cetinkaya if (!FE)
4923d65def1SKadir Cetinkaya return FE.takeError();
4933d65def1SKadir Cetinkaya Edits.push_back(std::move(*FE));
4943d65def1SKadir Cetinkaya } else {
4953d65def1SKadir Cetinkaya // Merge with previous edit if they are in the same file.
4963d65def1SKadir Cetinkaya if (auto Err = Edits.front().second.Replacements.add(DeleteFuncBody))
4973d65def1SKadir Cetinkaya return std::move(Err);
4983d65def1SKadir Cetinkaya }
4993d65def1SKadir Cetinkaya
5003d65def1SKadir Cetinkaya Effect E;
5013d65def1SKadir Cetinkaya for (auto &Pair : Edits)
5023d65def1SKadir Cetinkaya E.ApplyEdits.try_emplace(std::move(Pair.first), std::move(Pair.second));
5033d65def1SKadir Cetinkaya return E;
5045ab9a850SKadir Cetinkaya }
5055ab9a850SKadir Cetinkaya
5065ab9a850SKadir Cetinkaya private:
5075ab9a850SKadir Cetinkaya const FunctionDecl *Source = nullptr;
5085ab9a850SKadir Cetinkaya const FunctionDecl *Target = nullptr;
5095ab9a850SKadir Cetinkaya };
5105ab9a850SKadir Cetinkaya
51193a3128aSSimon Pilgrim REGISTER_TWEAK(DefineInline)
5125ab9a850SKadir Cetinkaya
5135ab9a850SKadir Cetinkaya } // namespace
5145ab9a850SKadir Cetinkaya } // namespace clangd
5155ab9a850SKadir Cetinkaya } // namespace clang
516