1 //===--- DefineInline.cpp ----------------------------------------*- C++-*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 #include "FindTarget.h"
11 #include "Selection.h"
12 #include "SourceCode.h"
14 #include "refactor/Tweak.h"
15 #include "support/Logger.h"
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>
52 // Returns semicolon location for the given FD. Since AST doesn't contain that
53 // information, searches for a semicolon by lexing from end of function decl
54 // while skipping comments.
55 std::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();
66 // Deduces the FunctionDecl from a selection. Requires either the function body
67 // or the function decl to be selected. Returns null if none of the above
69 const 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
>() &&
74 SelNode
->Selected
== SelectionTree::Complete
) {
75 if (const SelectionTree::Node
*P
= SelNode
->Parent
)
76 return P
->ASTNode
.get
<FunctionDecl
>();
81 // Checks the decls mentioned in Source are visible in the context of Target.
82 // Achieves that by checking declarations occur before target location in
83 // translation unit or declared in the same class.
84 bool checkDeclsAreVisible(const llvm::DenseSet
<const Decl
*> &DeclRefs
,
85 const FunctionDecl
*Target
, const SourceManager
&SM
) {
86 SourceLocation TargetLoc
= Target
->getLocation();
87 // To be used in visibility check below, decls in a class are visible
88 // independent of order.
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
) {
94 // Use canonical decl, since having one decl before target is enough.
95 const Decl
*D
= DR
->getCanonicalDecl();
98 SourceLocation DeclLoc
= D
->getLocation();
100 // FIXME: Allow declarations from different files with include insertion.
101 if (!SM
.isWrittenInSameFile(DeclLoc
, TargetLoc
))
104 // If declaration is before target, then it is visible.
105 if (SM
.isBeforeInTranslationUnit(DeclLoc
, TargetLoc
))
108 // Otherwise they need to be in same class
111 const RecordDecl
*Parent
= nullptr;
112 if (const auto *MD
= llvm::dyn_cast
<CXXMethodDecl
>(D
))
113 Parent
= MD
->getParent();
114 else if (const auto *FD
= llvm::dyn_cast
<FieldDecl
>(D
))
115 Parent
= FD
->getParent();
122 // Rewrites body of FD by re-spelling all of the names to make sure they are
123 // still valid in context of Target.
124 llvm::Expected
<std::string
> qualifyAllDecls(const FunctionDecl
*FD
,
125 const FunctionDecl
*Target
,
126 const HeuristicResolver
*Resolver
) {
127 // There are three types of spellings that needs to be qualified in a function
129 // - Types: Foo -> ns::Foo
130 // - DeclRefExpr: ns2::foo() -> ns1::ns2::foo();
132 // using ns2::foo -> using ns1::ns2::foo
133 // using namespace ns2 -> using namespace ns1::ns2
134 // using ns3 = ns2 -> using ns3 = ns1::ns2
136 // Go over all references inside a function body to generate replacements that
137 // will qualify those. So that body can be moved into an arbitrary file.
138 // We perform the qualification by qualifying the first type/decl in a
139 // (un)qualified name. e.g:
140 // namespace a { namespace b { class Bar{}; void foo(); } }
141 // b::Bar x; -> a::b::Bar x;
142 // foo(); -> a::b::foo();
144 auto *TargetContext
= Target
->getLexicalDeclContext();
145 const SourceManager
&SM
= FD
->getASTContext().getSourceManager();
147 tooling::Replacements Replacements
;
148 bool HadErrors
= false;
149 findExplicitReferences(
151 [&](ReferenceLoc Ref
) {
152 // Since we want to qualify only the first qualifier, skip names with a
156 // There might be no decl in dependent contexts, there's nothing much we
157 // can do in such cases.
158 if (Ref
.Targets
.empty())
160 // Do not qualify names introduced by macro expansions.
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
));
173 // All Targets are in the same scope, so we can safely chose first one.
174 const NamedDecl
*ND
= Ref
.Targets
.front();
175 // Skip anything from a non-namespace scope, these can be:
176 // - Function or Method scopes, which means decl is local and doesn't
179 // - From Class/Struct/Union scope, which again doesn't need any
181 // rather the left side of it requires qualification, like:
182 // namespace a { class Bar { public: static int x; } }
183 // void foo() { Bar::x; }
184 // ~~~~~ -> we need to qualify Bar not x.
185 if (!ND
->getDeclContext()->isNamespace())
188 const std::string Qualifier
= getQualification(
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.");
202 // Get new begin and end positions for the qualified body.
203 auto OrigBodyRange
= toHalfOpenFileRange(
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()));
212 // Trim the result to function body.
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);
220 /// Generates Replacements for changing template and function parameter names in
221 /// \p Dest to be the same as in \p Source.
222 llvm::Expected
<tooling::Replacements
>
223 renameParameters(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
) {
229 // No need to rename if parameters already have the same name.
230 if (DestParam
->getName() == SourceParam
->getName())
233 // Unnamed parameters won't be visited in findExplicitReferences. So add
235 if (DestParam
->getName().empty()) {
236 RefLocs
[DestParam
].push_back(DestParam
->getLocation());
237 // If decl is unnamed in destination we pad the new name to avoid gluing
238 // with previous token, e.g. foo(int^) shouldn't turn into foo(intx).
241 NewName
.append(std::string(SourceParam
->getName()));
242 ParamToNewName
[DestParam
->getCanonicalDecl()] = std::move(NewName
);
245 // Populate mapping for template parameters.
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
));
258 // Populate mapping for function params.
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();
265 // Collect other references in function signature, i.e parameter types and
266 // default arguments.
267 findExplicitReferences(
268 // Use function template in case of templated functions to visit template
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
);
283 // Now try to generate edits for all the refs.
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
;
291 // In case of unnamed parameters, we have an empty char range, whereas we
292 // have a tokenrange at RefLoc with named parameters.
294 ReplaceRange
= CharSourceRange::getCharRange(RefLoc
, RefLoc
);
296 ReplaceRange
= CharSourceRange::getTokenRange(RefLoc
, RefLoc
);
297 // If occurrence is coming from a macro expansion, try to get back to the
299 if (RefLoc
.isMacroID()) {
300 ReplaceRange
= Lexer::makeFileCharRange(ReplaceRange
, SM
, LangOpts
);
301 // Bail out if we need to replace macro bodies.
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
);
321 // Returns the canonical declaration for the given FunctionDecl. This will
322 // usually be the first declaration in current translation unit with the
323 // exception of template specialization.
324 // For those we return first declaration different than the canonical one.
325 // Because canonical declaration points to template decl instead of
327 const FunctionDecl
*findTarget(const FunctionDecl
*FD
) {
328 auto *CanonDecl
= FD
->getCanonicalDecl();
329 if (!FD
->isFunctionTemplateSpecialization() || CanonDecl
== FD
)
331 // For specializations CanonicalDecl is the TemplatedDecl, which is not the
332 // target we want to inline into. Instead we traverse previous decls to find
333 // the first forward decl for this specialization.
335 while (PrevDecl
->getPreviousDecl() != CanonDecl
) {
336 PrevDecl
= PrevDecl
->getPreviousDecl();
337 assert(PrevDecl
&& "Found specialization without template decl");
342 // Returns the beginning location for a FunctionDecl. Returns location of
343 // template keyword for templated functions.
344 const SourceLocation
getBeginLoc(const FunctionDecl
*FD
) {
345 // Include template parameter list.
346 if (auto *FTD
= FD
->getDescribedFunctionTemplate())
347 return FTD
->getBeginLoc();
348 return FD
->getBeginLoc();
351 std::optional
<tooling::Replacement
>
352 addInlineIfInHeader(const FunctionDecl
*FD
) {
353 // This includes inline functions and constexpr functions.
354 if (FD
->isInlined() || llvm::isa
<CXXMethodDecl
>(FD
))
356 // Primary template doesn't need inline.
357 if (FD
->isTemplated() && !FD
->isFunctionTemplateSpecialization())
360 const SourceManager
&SM
= FD
->getASTContext().getSourceManager();
361 llvm::StringRef FileName
= SM
.getFilename(FD
->getLocation());
363 // If it is not a header we don't need to mark function as "inline".
364 if (!isHeaderFile(FileName
, FD
->getASTContext().getLangOpts()))
367 return tooling::Replacement(SM
, FD
->getInnerLocStart(), 0, "inline ");
370 /// Moves definition of a function/method to its declaration location.
376 /// void foo() { return; }
378 /// ------------------------
381 /// void foo() { return; }
385 class DefineInline
: public Tweak
{
387 const char *id() const final
;
389 llvm::StringLiteral
kind() const override
{
390 return CodeAction::REFACTOR_KIND
;
392 std::string
title() const override
{
393 return "Move function body to declaration";
396 // Returns true when selection is on a function definition that does not
397 // make use of any internal symbols.
398 bool prepare(const Selection
&Sel
) override
{
399 const SelectionTree::Node
*SelNode
= Sel
.ASTSelection
.commonAncestor();
402 Source
= getSelectedFunction(SelNode
);
403 if (!Source
|| !Source
->hasBody())
405 // Only the last level of template parameter locations are not kept in AST,
406 // so if we are inlining a method that is in a templated class, there is no
407 // way to verify template parameter names. Therefore we bail out.
408 if (auto *MD
= llvm::dyn_cast
<CXXMethodDecl
>(Source
)) {
409 if (MD
->getParent()->isTemplated())
412 // If function body starts or ends inside a macro, we refuse to move it into
413 // declaration location.
414 if (Source
->getBody()->getBeginLoc().isMacroID() ||
415 Source
->getBody()->getEndLoc().isMacroID())
418 Target
= findTarget(Source
);
419 if (Target
== Source
) {
420 // The only declaration is Source. No other declaration to move function
422 // FIXME: If we are in an implementation file, figure out a suitable
423 // location to put declaration. Possibly using other declarations in the
428 // Check if the decls referenced in function body are visible in the
429 // declaration location.
430 if (!checkDeclsAreVisible(getNonLocalDeclRefs(*Sel
.AST
, Source
), Target
,
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
);
465 auto DefRange
= toHalfOpenFileRange(
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()))) {
488 // Generate a new edit if the Source and Target are in different files.
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
));
495 // Merge with previous edit if they are in the same file.
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;
511 REGISTER_TWEAK(DefineInline
)
514 } // namespace clangd