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/NestedNameSpecifier.h"
23#include "clang/AST/Stmt.h"
24#include "clang/Basic/LangOptions.h"
25#include "clang/Basic/SourceLocation.h"
26#include "clang/Basic/SourceManager.h"
27#include "clang/Basic/TokenKinds.h"
28#include "clang/Lex/Lexer.h"
29#include "clang/Lex/Token.h"
30#include "clang/Sema/Lookup.h"
31#include "clang/Sema/Sema.h"
32#include "clang/Tooling/Core/Replacement.h"
33#include "llvm/ADT/DenseMap.h"
34#include "llvm/ADT/DenseSet.h"
35#include "llvm/ADT/SmallVector.h"
36#include "llvm/ADT/StringRef.h"
37#include "llvm/Support/Casting.h"
38#include "llvm/Support/Error.h"
39#include "llvm/Support/raw_ostream.h"
44#include <unordered_map>
55std::optional<SourceLocation> getSemicolonForDecl(
const FunctionDecl *FD) {
56 const SourceManager &SM = FD->getASTContext().getSourceManager();
57 const LangOptions &LangOpts = FD->getASTContext().getLangOpts();
59 SourceLocation CurLoc = FD->getEndLoc();
60 auto NextTok = Lexer::findNextToken(CurLoc, SM, LangOpts);
61 if (!NextTok || !NextTok->is(tok::semi))
63 return NextTok->getLocation();
69const FunctionDecl *getSelectedFunction(
const SelectionTree::Node *SelNode) {
70 const DynTypedNode &AstNode = SelNode->ASTNode;
71 if (
const FunctionDecl *FD = AstNode.get<FunctionDecl>())
73 if (AstNode.get<CompoundStmt>() &&
75 if (
const SelectionTree::Node *P = SelNode->Parent)
76 return P->ASTNode.get<FunctionDecl>();
84bool checkDeclsAreVisible(
const llvm::DenseSet<const Decl *> &DeclRefs,
85 const FunctionDecl *Target,
const SourceManager &SM) {
86 SourceLocation TargetLoc = Target->getLocation();
89 const RecordDecl *
Class =
nullptr;
90 if (
const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Target))
91 Class = MD->getParent();
93 for (
const auto *DR : DeclRefs) {
95 const Decl *D = DR->getCanonicalDecl();
98 SourceLocation DeclLoc = D->getLocation();
101 if (!SM.isWrittenInSameFile(DeclLoc, TargetLoc))
105 if (SM.isBeforeInTranslationUnit(DeclLoc, TargetLoc))
111 const RecordDecl *
Parent =
nullptr;
112 if (
const auto *MD = llvm::dyn_cast<CXXMethodDecl>(D))
114 else if (
const auto *FD = llvm::dyn_cast<FieldDecl>(D))
124llvm::Expected<std::string> qualifyAllDecls(
const FunctionDecl *FD,
125 const FunctionDecl *Target,
126 const HeuristicResolver *Resolver) {
144 auto *TargetContext = Target->getLexicalDeclContext();
145 const SourceManager &SM = FD->getASTContext().getSourceManager();
147 tooling::Replacements Replacements;
148 bool HadErrors =
false;
151 [&](ReferenceLoc Ref) {
158 if (Ref.Targets.empty())
161 if (Ref.NameLoc.isMacroID())
164 for (const NamedDecl *ND : Ref.Targets) {
165 if (ND->getDeclContext() != Ref.Targets.front()->getDeclContext()) {
166 elog(
"define inline: Targets from multiple contexts: {0}, {1}",
167 printQualifiedName(*Ref.Targets.front()),
168 printQualifiedName(*ND));
174 const NamedDecl *ND = Ref.Targets.front();
185 if (!ND->getDeclContext()->isNamespace())
189 FD->getASTContext(), TargetContext, Target->getBeginLoc(), ND);
190 if (
auto Err = Replacements.add(
191 tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier))) {
193 elog(
"define inline: Failed to add quals: {0}", std::move(Err));
200 "define inline: Failed to compute qualifiers. See logs for details.");
204 SM, FD->getASTContext().getLangOpts(), FD->getBody()->getSourceRange());
206 return error(
"Couldn't get range func body.");
208 unsigned BodyBegin = SM.getFileOffset(OrigBodyRange->getBegin());
209 unsigned BodyEnd = Replacements.getShiftedCodePosition(
210 SM.getFileOffset(OrigBodyRange->getEnd()));
213 auto QualifiedFunc = tooling::applyAllReplacements(
214 SM.getBufferData(SM.getFileID(OrigBodyRange->getBegin())), Replacements);
216 return QualifiedFunc.takeError();
217 return QualifiedFunc->substr(BodyBegin, BodyEnd - BodyBegin + 1);
222llvm::Expected<tooling::Replacements>
223renameParameters(
const FunctionDecl *Dest,
const FunctionDecl *Source,
224 const HeuristicResolver *Resolver) {
225 llvm::DenseMap<const Decl *, std::string> ParamToNewName;
226 llvm::DenseMap<const NamedDecl *, std::vector<SourceLocation>> RefLocs;
227 auto HandleParam = [&](
const NamedDecl *DestParam,
228 const NamedDecl *SourceParam) {
230 if (DestParam->getName() == SourceParam->getName())
235 if (DestParam->getName().empty()) {
236 RefLocs[DestParam].push_back(DestParam->getLocation());
241 NewName.append(std::string(SourceParam->getName()));
242 ParamToNewName[DestParam->getCanonicalDecl()] = std::move(NewName);
246 auto *DestTempl = Dest->getDescribedFunctionTemplate();
247 auto *SourceTempl = Source->getDescribedFunctionTemplate();
248 assert(
bool(DestTempl) ==
bool(SourceTempl));
250 const auto *DestTPL = DestTempl->getTemplateParameters();
251 const auto *SourceTPL = SourceTempl->getTemplateParameters();
252 assert(DestTPL->size() == SourceTPL->size());
254 for (
size_t I = 0, EP = DestTPL->size(); I != EP; ++I)
255 HandleParam(DestTPL->getParam(I), SourceTPL->getParam(I));
259 assert(Dest->param_size() == Source->param_size());
260 for (
size_t I = 0,
E = Dest->param_size(); I !=
E; ++I)
261 HandleParam(Dest->getParamDecl(I), Source->getParamDecl(I));
263 const SourceManager &SM = Dest->getASTContext().getSourceManager();
264 const LangOptions &LangOpts = Dest->getASTContext().getLangOpts();
270 DestTempl ? llvm::dyn_cast<Decl>(DestTempl) : llvm::dyn_cast<Decl>(Dest),
271 [&](ReferenceLoc Ref) {
272 if (Ref.Targets.size() != 1)
275 llvm::cast<NamedDecl>(Ref.Targets.front()->getCanonicalDecl());
276 auto It = ParamToNewName.find(Target);
277 if (It == ParamToNewName.end())
279 RefLocs[Target].push_back(Ref.NameLoc);
284 tooling::Replacements Replacements;
285 for (
auto &
Entry : RefLocs) {
286 const auto *OldDecl =
Entry.first;
287 llvm::StringRef OldName = OldDecl->getName();
288 llvm::StringRef NewName = ParamToNewName[OldDecl];
289 for (SourceLocation RefLoc :
Entry.second) {
290 CharSourceRange ReplaceRange;
294 ReplaceRange = CharSourceRange::getCharRange(RefLoc, RefLoc);
296 ReplaceRange = CharSourceRange::getTokenRange(RefLoc, RefLoc);
299 if (RefLoc.isMacroID()) {
300 ReplaceRange = Lexer::makeFileCharRange(ReplaceRange, SM, LangOpts);
302 if (ReplaceRange.isInvalid()) {
303 auto Err =
error(
"Cant rename parameter inside macro body.");
304 elog(
"define inline: {0}", Err);
305 return std::move(Err);
309 if (
auto Err = Replacements.add(
310 tooling::Replacement(SM, ReplaceRange, NewName))) {
311 elog(
"define inline: Couldn't replace parameter name for {0} to {1}: "
313 OldName, NewName, Err);
314 return std::move(Err);
327const FunctionDecl *findTarget(
const FunctionDecl *FD) {
328 auto *CanonDecl = FD->getCanonicalDecl();
329 if (!FD->isFunctionTemplateSpecialization() || CanonDecl == FD)
335 while (PrevDecl->getPreviousDecl() != CanonDecl) {
336 PrevDecl = PrevDecl->getPreviousDecl();
337 assert(PrevDecl &&
"Found specialization without template decl");
344const SourceLocation getBeginLoc(
const FunctionDecl *FD) {
346 if (
auto *FTD = FD->getDescribedFunctionTemplate())
347 return FTD->getBeginLoc();
348 return FD->getBeginLoc();
351std::optional<tooling::Replacement>
352addInlineIfInHeader(
const FunctionDecl *FD) {
354 if (FD->isInlined() || llvm::isa<CXXMethodDecl>(FD))
357 if (FD->isTemplated() && !FD->isFunctionTemplateSpecialization())
360 const SourceManager &SM = FD->getASTContext().getSourceManager();
361 llvm::StringRef
FileName = SM.getFilename(FD->getLocation());
367 return tooling::Replacement(SM, FD->getInnerLocStart(), 0,
"inline ");
385class DefineInline :
public Tweak {
387 const char *id() const final;
389 llvm::StringLiteral kind()
const override {
392 std::string title()
const override {
393 return "Move function body to declaration";
398 bool prepare(
const Selection &Sel)
override {
399 const SelectionTree::Node *SelNode = Sel.ASTSelection.commonAncestor();
402 Source = getSelectedFunction(SelNode);
403 if (!Source || !Source->hasBody())
408 if (
auto *MD = llvm::dyn_cast<CXXMethodDecl>(Source)) {
409 if (MD->getParent()->isTemplated())
414 if (Source->getBody()->getBeginLoc().isMacroID() ||
415 Source->getBody()->getEndLoc().isMacroID())
418 Target = findTarget(Source);
419 if (Target == Source) {
431 Sel.AST->getSourceManager()))
437 Expected<Effect> apply(
const Selection &Sel)
override {
438 const auto &
AST = Sel.AST->getASTContext();
439 const auto &SM =
AST.getSourceManager();
441 auto Semicolon = getSemicolonForDecl(Target);
443 return error(
"Couldn't find semicolon for target declaration.");
445 auto AddInlineIfNecessary = addInlineIfInHeader(Target);
446 auto ParamReplacements =
447 renameParameters(Target, Source, Sel.AST->getHeuristicResolver());
448 if (!ParamReplacements)
449 return ParamReplacements.takeError();
452 qualifyAllDecls(Source, Target, Sel.AST->getHeuristicResolver());
454 return QualifiedBody.takeError();
456 const tooling::Replacement SemicolonToFuncBody(SM, *Semicolon, 1,
458 tooling::Replacements TargetFileReplacements(SemicolonToFuncBody);
459 TargetFileReplacements = TargetFileReplacements.merge(*ParamReplacements);
460 if (AddInlineIfNecessary) {
461 if (
auto Err = TargetFileReplacements.add(*AddInlineIfNecessary))
462 return std::move(Err);
466 SM,
AST.getLangOpts(),
467 SM.getExpansionRange(CharSourceRange::getCharRange(getBeginLoc(Source),
468 Source->getEndLoc()))
471 return error(
"Couldn't get range for the source.");
472 unsigned int SourceLen = SM.getFileOffset(DefRange->getEnd()) -
473 SM.getFileOffset(DefRange->getBegin());
474 const tooling::Replacement DeleteFuncBody(SM, DefRange->getBegin(),
477 llvm::SmallVector<std::pair<std::string, Edit>> Edits;
479 auto FE = Effect::fileEdit(SM, SM.getFileID(*Semicolon),
480 std::move(TargetFileReplacements));
482 return FE.takeError();
483 Edits.push_back(std::move(*FE));
486 if (!SM.isWrittenInSameFile(DefRange->getBegin(),
487 SM.getExpansionLoc(Target->getBeginLoc()))) {
489 auto FE = Effect::fileEdit(SM, SM.getFileID(Sel.Cursor),
490 tooling::Replacements(DeleteFuncBody));
492 return FE.takeError();
493 Edits.push_back(std::move(*FE));
496 if (
auto Err = Edits.front().second.Replacements.add(DeleteFuncBody))
497 return std::move(Err);
501 for (
auto &Pair : Edits)
502 E.ApplyEdits.try_emplace(std::move(
Pair.first), std::move(
Pair.second));
507 const FunctionDecl *Source =
nullptr;
508 const FunctionDecl *Target =
nullptr;
const FunctionDecl * Decl
#define REGISTER_TWEAK(Subclass)
std::optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
std::string getQualification(ASTContext &Context, const DeclContext *DestContext, SourceLocation InsertionPoint, const NamedDecl *ND)
Gets the nested name specifier necessary for spelling ND in DestContext, at InsertionPoint.
void findExplicitReferences(const Stmt *S, llvm::function_ref< void(ReferenceLoc)> Out, const HeuristicResolver *Resolver)
Recursively traverse S and report all references explicitly written in the code.
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
llvm::DenseSet< const Decl * > getNonLocalDeclRefs(ParsedAST &AST, const FunctionDecl *FD)
Returns all decls that are referenced in the FD except local symbols.
void elog(const char *Fmt, Ts &&... Vals)
bool isHeaderFile(llvm::StringRef FileName, std::optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
static const llvm::StringLiteral REFACTOR_KIND