• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- DefineInline.cpp ----------------------------------------*- C++-*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "AST.h"
10 #include "FindTarget.h"
11 #include "Selection.h"
12 #include "SourceCode.h"
13 #include "XRefs.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/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"
58 #include <cstddef>
59 #include <set>
60 #include <string>
61 #include <unordered_map>
62 #include <utility>
63 #include <vector>
64 
65 namespace clang {
66 namespace clangd {
67 namespace {
68 
69 // Returns semicolon location for the given FD. Since AST doesn't contain that
70 // information, searches for a semicolon by lexing from end of function decl
71 // while skipping comments.
getSemicolonForDecl(const FunctionDecl * FD)72 llvm::Optional<SourceLocation> getSemicolonForDecl(const FunctionDecl *FD) {
73   const SourceManager &SM = FD->getASTContext().getSourceManager();
74   const LangOptions &LangOpts = FD->getASTContext().getLangOpts();
75 
76   SourceLocation CurLoc = FD->getEndLoc();
77   auto NextTok = Lexer::findNextToken(CurLoc, SM, LangOpts);
78   if (!NextTok || !NextTok->is(tok::semi))
79     return llvm::None;
80   return NextTok->getLocation();
81 }
82 
83 // Deduces the FunctionDecl from a selection. Requires either the function body
84 // or the function decl to be selected. Returns null if none of the above
85 // criteria is met.
getSelectedFunction(const SelectionTree::Node * SelNode)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>())
89     return FD;
90   if (AstNode.get<CompoundStmt>() &&
91       SelNode->Selected == SelectionTree::Complete) {
92     if (const SelectionTree::Node *P = SelNode->Parent)
93       return P->ASTNode.get<FunctionDecl>();
94   }
95   return nullptr;
96 }
97 
98 // Checks the decls mentioned in Source are visible in the context of Target.
99 // Achieves that by checking declarations occur before target location in
100 // translation unit or declared in the same class.
checkDeclsAreVisible(const llvm::DenseSet<const Decl * > & DeclRefs,const FunctionDecl * Target,const SourceManager & SM)101 bool checkDeclsAreVisible(const llvm::DenseSet<const Decl *> &DeclRefs,
102                           const FunctionDecl *Target, const SourceManager &SM) {
103   SourceLocation TargetLoc = Target->getLocation();
104   // To be used in visibility check below, decls in a class are visible
105   // independent of order.
106   const RecordDecl *Class = nullptr;
107   if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Target))
108     Class = MD->getParent();
109 
110   for (const auto *DR : DeclRefs) {
111     // Use canonical decl, since having one decl before target is enough.
112     const Decl *D = DR->getCanonicalDecl();
113     if (D == Target)
114       continue;
115     SourceLocation DeclLoc = D->getLocation();
116 
117     // FIXME: Allow declarations from different files with include insertion.
118     if (!SM.isWrittenInSameFile(DeclLoc, TargetLoc))
119       return false;
120 
121     // If declaration is before target, then it is visible.
122     if (SM.isBeforeInTranslationUnit(DeclLoc, TargetLoc))
123       continue;
124 
125     // Otherwise they need to be in same class
126     if (!Class)
127       return false;
128     const RecordDecl *Parent = nullptr;
129     if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(D))
130       Parent = MD->getParent();
131     else if (const auto *FD = llvm::dyn_cast<FieldDecl>(D))
132       Parent = FD->getParent();
133     if (Parent != Class)
134       return false;
135   }
136   return true;
137 }
138 
139 // Rewrites body of FD by re-spelling all of the names to make sure they are
140 // still valid in context of Target.
qualifyAllDecls(const FunctionDecl * FD,const FunctionDecl * Target)141 llvm::Expected<std::string> qualifyAllDecls(const FunctionDecl *FD,
142                                             const FunctionDecl *Target) {
143   // There are three types of spellings that needs to be qualified in a function
144   // body:
145   // - Types:       Foo                 -> ns::Foo
146   // - DeclRefExpr: ns2::foo()          -> ns1::ns2::foo();
147   // - UsingDecls:
148   //    using ns2::foo      -> using ns1::ns2::foo
149   //    using namespace ns2 -> using namespace ns1::ns2
150   //    using ns3 = ns2     -> using ns3 = ns1::ns2
151   //
152   // Go over all references inside a function body to generate replacements that
153   // will qualify those. So that body can be moved into an arbitrary file.
154   // We perform the qualification by qualifying the first type/decl in a
155   // (un)qualified name. e.g:
156   //    namespace a { namespace b { class Bar{}; void foo(); } }
157   //    b::Bar x; -> a::b::Bar x;
158   //    foo(); -> a::b::foo();
159 
160   auto *TargetContext = Target->getLexicalDeclContext();
161   const SourceManager &SM = FD->getASTContext().getSourceManager();
162 
163   tooling::Replacements Replacements;
164   bool HadErrors = false;
165   findExplicitReferences(FD->getBody(), [&](ReferenceLoc Ref) {
166     // Since we want to qualify only the first qualifier, skip names with a
167     // qualifier.
168     if (Ref.Qualifier)
169       return;
170     // There might be no decl in dependent contexts, there's nothing much we can
171     // do in such cases.
172     if (Ref.Targets.empty())
173       return;
174     // Do not qualify names introduced by macro expansions.
175     if (Ref.NameLoc.isMacroID())
176       return;
177 
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}",
181              printQualifiedName(*Ref.Targets.front()), printQualifiedName(*ND));
182         HadErrors = true;
183         return;
184       }
185     }
186     // All Targets are in the same scope, so we can safely chose first one.
187     const NamedDecl *ND = Ref.Targets.front();
188     // Skip anything from a non-namespace scope, these can be:
189     // - Function or Method scopes, which means decl is local and doesn't need
190     //   qualification.
191     // - From Class/Struct/Union scope, which again doesn't need any qualifiers,
192     //   rather the left side of it requires qualification, like:
193     //   namespace a { class Bar { public: static int x; } }
194     //   void foo() { Bar::x; }
195     //                ~~~~~ -> we need to qualify Bar not x.
196     if (!ND->getDeclContext()->isNamespace())
197       return;
198 
199     const std::string Qualifier = getQualification(
200         FD->getASTContext(), TargetContext, Target->getBeginLoc(), ND);
201     if (auto Err = Replacements.add(
202             tooling::Replacement(SM, Ref.NameLoc, 0, Qualifier))) {
203       HadErrors = true;
204       elog("define inline: Failed to add quals: {0}", std::move(Err));
205     }
206   });
207 
208   if (HadErrors)
209     return error(
210         "define inline: Failed to compute qualifiers. See logs for details.");
211 
212   // Get new begin and end positions for the qualified body.
213   auto OrigBodyRange = toHalfOpenFileRange(
214       SM, FD->getASTContext().getLangOpts(), FD->getBody()->getSourceRange());
215   if (!OrigBodyRange)
216     return error("Couldn't get range func body.");
217 
218   unsigned BodyBegin = SM.getFileOffset(OrigBodyRange->getBegin());
219   unsigned BodyEnd = Replacements.getShiftedCodePosition(
220       SM.getFileOffset(OrigBodyRange->getEnd()));
221 
222   // Trim the result to function body.
223   auto QualifiedFunc = tooling::applyAllReplacements(
224       SM.getBufferData(SM.getFileID(OrigBodyRange->getBegin())), Replacements);
225   if (!QualifiedFunc)
226     return QualifiedFunc.takeError();
227   return QualifiedFunc->substr(BodyBegin, BodyEnd - BodyBegin + 1);
228 }
229 
230 /// Generates Replacements for changing template and function parameter names in
231 /// \p Dest to be the same as in \p Source.
232 llvm::Expected<tooling::Replacements>
renameParameters(const FunctionDecl * Dest,const FunctionDecl * Source)233 renameParameters(const FunctionDecl *Dest, const FunctionDecl *Source) {
234   llvm::DenseMap<const Decl *, std::string> ParamToNewName;
235   llvm::DenseMap<const NamedDecl *, std::vector<SourceLocation>> RefLocs;
236   auto HandleParam = [&](const NamedDecl *DestParam,
237                          const NamedDecl *SourceParam) {
238     // No need to rename if parameters already have the same name.
239     if (DestParam->getName() == SourceParam->getName())
240       return;
241     std::string NewName;
242     // Unnamed parameters won't be visited in findExplicitReferences. So add
243     // them here.
244     if (DestParam->getName().empty()) {
245       RefLocs[DestParam].push_back(DestParam->getLocation());
246       // If decl is unnamed in destination we pad the new name to avoid gluing
247       // with previous token, e.g. foo(int^) shouldn't turn into foo(intx).
248       NewName = " ";
249     }
250     NewName.append(std::string(SourceParam->getName()));
251     ParamToNewName[DestParam->getCanonicalDecl()] = std::move(NewName);
252   };
253 
254   // Populate mapping for template parameters.
255   auto *DestTempl = Dest->getDescribedFunctionTemplate();
256   auto *SourceTempl = Source->getDescribedFunctionTemplate();
257   assert(bool(DestTempl) == bool(SourceTempl));
258   if (DestTempl) {
259     const auto *DestTPL = DestTempl->getTemplateParameters();
260     const auto *SourceTPL = SourceTempl->getTemplateParameters();
261     assert(DestTPL->size() == SourceTPL->size());
262 
263     for (size_t I = 0, EP = DestTPL->size(); I != EP; ++I)
264       HandleParam(DestTPL->getParam(I), SourceTPL->getParam(I));
265   }
266 
267   // Populate mapping for function params.
268   assert(Dest->param_size() == Source->param_size());
269   for (size_t I = 0, E = Dest->param_size(); I != E; ++I)
270     HandleParam(Dest->getParamDecl(I), Source->getParamDecl(I));
271 
272   const SourceManager &SM = Dest->getASTContext().getSourceManager();
273   const LangOptions &LangOpts = Dest->getASTContext().getLangOpts();
274   // Collect other references in function signature, i.e parameter types and
275   // default arguments.
276   findExplicitReferences(
277       // Use function template in case of templated functions to visit template
278       // parameters.
279       DestTempl ? llvm::dyn_cast<Decl>(DestTempl) : llvm::dyn_cast<Decl>(Dest),
280       [&](ReferenceLoc Ref) {
281         if (Ref.Targets.size() != 1)
282           return;
283         const auto *Target =
284             llvm::cast<NamedDecl>(Ref.Targets.front()->getCanonicalDecl());
285         auto It = ParamToNewName.find(Target);
286         if (It == ParamToNewName.end())
287           return;
288         RefLocs[Target].push_back(Ref.NameLoc);
289       });
290 
291   // Now try to generate edits for all the refs.
292   tooling::Replacements Replacements;
293   for (auto &Entry : RefLocs) {
294     const auto *OldDecl = Entry.first;
295     llvm::StringRef OldName = OldDecl->getName();
296     llvm::StringRef NewName = ParamToNewName[OldDecl];
297     for (SourceLocation RefLoc : Entry.second) {
298       CharSourceRange ReplaceRange;
299       // In case of unnamed parameters, we have an empty char range, whereas we
300       // have a tokenrange at RefLoc with named parameters.
301       if (OldName.empty())
302         ReplaceRange = CharSourceRange::getCharRange(RefLoc, RefLoc);
303       else
304         ReplaceRange = CharSourceRange::getTokenRange(RefLoc, RefLoc);
305       // If occurrence is coming from a macro expansion, try to get back to the
306       // file range.
307       if (RefLoc.isMacroID()) {
308         ReplaceRange = Lexer::makeFileCharRange(ReplaceRange, SM, LangOpts);
309         // Bail out if we need to replace macro bodies.
310         if (ReplaceRange.isInvalid()) {
311           auto Err = error("Cant rename parameter inside macro body.");
312           elog("define inline: {0}", Err);
313           return std::move(Err);
314         }
315       }
316 
317       if (auto Err = Replacements.add(
318               tooling::Replacement(SM, ReplaceRange, NewName))) {
319         elog("define inline: Couldn't replace parameter name for {0} to {1}: "
320              "{2}",
321              OldName, NewName, Err);
322         return std::move(Err);
323       }
324     }
325   }
326   return Replacements;
327 }
328 
329 // Returns the canonical declaration for the given FunctionDecl. This will
330 // usually be the first declaration in current translation unit with the
331 // exception of template specialization.
332 // For those we return first declaration different than the canonical one.
333 // Because canonical declaration points to template decl instead of
334 // specialization.
findTarget(const FunctionDecl * FD)335 const FunctionDecl *findTarget(const FunctionDecl *FD) {
336   auto CanonDecl = FD->getCanonicalDecl();
337   if (!FD->isFunctionTemplateSpecialization() || CanonDecl == FD)
338     return CanonDecl;
339   // For specializations CanonicalDecl is the TemplatedDecl, which is not the
340   // target we want to inline into. Instead we traverse previous decls to find
341   // the first forward decl for this specialization.
342   auto PrevDecl = FD;
343   while (PrevDecl->getPreviousDecl() != CanonDecl) {
344     PrevDecl = PrevDecl->getPreviousDecl();
345     assert(PrevDecl && "Found specialization without template decl");
346   }
347   return PrevDecl;
348 }
349 
350 // Returns the beginning location for a FunctionDecl. Returns location of
351 // template keyword for templated functions.
getBeginLoc(const FunctionDecl * FD)352 const SourceLocation getBeginLoc(const FunctionDecl *FD) {
353   // Include template parameter list.
354   if (auto *FTD = FD->getDescribedFunctionTemplate())
355     return FTD->getBeginLoc();
356   return FD->getBeginLoc();
357 }
358 
359 llvm::Optional<tooling::Replacement>
addInlineIfInHeader(const FunctionDecl * FD)360 addInlineIfInHeader(const FunctionDecl *FD) {
361   // This includes inline functions and constexpr functions.
362   if (FD->isInlined() || llvm::isa<CXXMethodDecl>(FD))
363     return llvm::None;
364   // Primary template doesn't need inline.
365   if (FD->isTemplated() && !FD->isFunctionTemplateSpecialization())
366     return llvm::None;
367 
368   const SourceManager &SM = FD->getASTContext().getSourceManager();
369   llvm::StringRef FileName = SM.getFilename(FD->getLocation());
370 
371   // If it is not a header we don't need to mark function as "inline".
372   if (!isHeaderFile(FileName, FD->getASTContext().getLangOpts()))
373     return llvm::None;
374 
375   return tooling::Replacement(SM, FD->getInnerLocStart(), 0, "inline ");
376 }
377 
378 /// Moves definition of a function/method to its declaration location.
379 /// Before:
380 /// a.h:
381 ///   void foo();
382 ///
383 /// a.cc:
384 ///   void foo() { return; }
385 ///
386 /// ------------------------
387 /// After:
388 /// a.h:
389 ///   void foo() { return; }
390 ///
391 /// a.cc:
392 ///
393 class DefineInline : public Tweak {
394 public:
395   const char *id() const override final;
396 
kind() const397   llvm::StringLiteral kind() const override {
398     return CodeAction::REFACTOR_KIND;
399   }
title() const400   std::string title() const override {
401     return "Move function body to declaration";
402   }
403 
404   // Returns true when selection is on a function definition that does not
405   // make use of any internal symbols.
prepare(const Selection & Sel)406   bool prepare(const Selection &Sel) override {
407     const SelectionTree::Node *SelNode = Sel.ASTSelection.commonAncestor();
408     if (!SelNode)
409       return false;
410     Source = getSelectedFunction(SelNode);
411     if (!Source || !Source->hasBody())
412       return false;
413     // Only the last level of template parameter locations are not kept in AST,
414     // so if we are inlining a method that is in a templated class, there is no
415     // way to verify template parameter names. Therefore we bail out.
416     if (auto *MD = llvm::dyn_cast<CXXMethodDecl>(Source)) {
417       if (MD->getParent()->isTemplated())
418         return false;
419     }
420     // If function body starts or ends inside a macro, we refuse to move it into
421     // declaration location.
422     if (Source->getBody()->getBeginLoc().isMacroID() ||
423         Source->getBody()->getEndLoc().isMacroID())
424       return false;
425 
426     Target = findTarget(Source);
427     if (Target == Source) {
428       // The only declaration is Source. No other declaration to move function
429       // body.
430       // FIXME: If we are in an implementation file, figure out a suitable
431       // location to put declaration. Possibly using other declarations in the
432       // AST.
433       return false;
434     }
435 
436     // Check if the decls referenced in function body are visible in the
437     // declaration location.
438     if (!checkDeclsAreVisible(getNonLocalDeclRefs(*Sel.AST, Source), Target,
439                               Sel.AST->getSourceManager()))
440       return false;
441 
442     return true;
443   }
444 
apply(const Selection & Sel)445   Expected<Effect> apply(const Selection &Sel) override {
446     const auto &AST = Sel.AST->getASTContext();
447     const auto &SM = AST.getSourceManager();
448 
449     auto Semicolon = getSemicolonForDecl(Target);
450     if (!Semicolon)
451       return error("Couldn't find semicolon for target declaration.");
452 
453     auto AddInlineIfNecessary = addInlineIfInHeader(Target);
454     auto ParamReplacements = renameParameters(Target, Source);
455     if (!ParamReplacements)
456       return ParamReplacements.takeError();
457 
458     auto QualifiedBody = qualifyAllDecls(Source, Target);
459     if (!QualifiedBody)
460       return QualifiedBody.takeError();
461 
462     const tooling::Replacement SemicolonToFuncBody(SM, *Semicolon, 1,
463                                                    *QualifiedBody);
464     tooling::Replacements TargetFileReplacements(SemicolonToFuncBody);
465     TargetFileReplacements = TargetFileReplacements.merge(*ParamReplacements);
466     if (AddInlineIfNecessary) {
467       if (auto Err = TargetFileReplacements.add(*AddInlineIfNecessary))
468         return std::move(Err);
469     }
470 
471     auto DefRange = toHalfOpenFileRange(
472         SM, AST.getLangOpts(),
473         SM.getExpansionRange(CharSourceRange::getCharRange(getBeginLoc(Source),
474                                                            Source->getEndLoc()))
475             .getAsRange());
476     if (!DefRange)
477       return error("Couldn't get range for the source.");
478     unsigned int SourceLen = SM.getFileOffset(DefRange->getEnd()) -
479                              SM.getFileOffset(DefRange->getBegin());
480     const tooling::Replacement DeleteFuncBody(SM, DefRange->getBegin(),
481                                               SourceLen, "");
482 
483     llvm::SmallVector<std::pair<std::string, Edit>, 2> Edits;
484     // Edit for Target.
485     auto FE = Effect::fileEdit(SM, SM.getFileID(*Semicolon),
486                                std::move(TargetFileReplacements));
487     if (!FE)
488       return FE.takeError();
489     Edits.push_back(std::move(*FE));
490 
491     // Edit for Source.
492     if (!SM.isWrittenInSameFile(DefRange->getBegin(),
493                                 SM.getExpansionLoc(Target->getBeginLoc()))) {
494       // Generate a new edit if the Source and Target are in different files.
495       auto FE = Effect::fileEdit(SM, SM.getFileID(Sel.Cursor),
496                                  tooling::Replacements(DeleteFuncBody));
497       if (!FE)
498         return FE.takeError();
499       Edits.push_back(std::move(*FE));
500     } else {
501       // Merge with previous edit if they are in the same file.
502       if (auto Err = Edits.front().second.Replacements.add(DeleteFuncBody))
503         return std::move(Err);
504     }
505 
506     Effect E;
507     for (auto &Pair : Edits)
508       E.ApplyEdits.try_emplace(std::move(Pair.first), std::move(Pair.second));
509     return E;
510   }
511 
512 private:
513   const FunctionDecl *Source = nullptr;
514   const FunctionDecl *Target = nullptr;
515 };
516 
517 REGISTER_TWEAK(DefineInline)
518 
519 } // namespace
520 } // namespace clangd
521 } // namespace clang
522