1 //===--- AddUsing.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 "Config.h"
11 #include "FindTarget.h"
12 #include "refactor/Tweak.h"
13 #include "support/Logger.h"
14 #include "clang/AST/Decl.h"
15 #include "clang/AST/RecursiveASTVisitor.h"
16 
17 namespace clang {
18 namespace clangd {
19 namespace {
20 
21 // Tweak for removing full namespace qualifier under cursor on DeclRefExpr and
22 // types and adding "using" statement instead.
23 //
24 // Only qualifiers that refer exclusively to namespaces (no record types) are
25 // supported. There is some guessing of appropriate place to insert the using
26 // declaration. If we find any existing usings, we insert it there. If not, we
27 // insert right after the inner-most relevant namespace declaration. If there is
28 // none, or there is, but it was declared via macro, we insert above the first
29 // top level decl.
30 //
31 // Currently this only removes qualifier from under the cursor. In the future,
32 // we should improve this to remove qualifier from all occurrences of this
33 // symbol.
34 class AddUsing : public Tweak {
35 public:
36   const char *id() const override;
37 
38   bool prepare(const Selection &Inputs) override;
39   Expected<Effect> apply(const Selection &Inputs) override;
40   std::string title() const override;
41   llvm::StringLiteral kind() const override {
42     return CodeAction::REFACTOR_KIND;
43   }
44 
45 private:
46   // All of the following are set by prepare().
47   // The qualifier to remove.
48   NestedNameSpecifierLoc QualifierToRemove;
49   // The name following QualifierToRemove.
50   llvm::StringRef Name;
51   // If valid, the insertion point for "using" statement must come after this.
52   // This is relevant when the type is defined in the main file, to make sure
53   // the type/function is already defined at the point where "using" is added.
54   SourceLocation MustInsertAfterLoc;
55 };
56 REGISTER_TWEAK(AddUsing)
57 
58 std::string AddUsing::title() const {
59   return std::string(llvm::formatv(
60       "Add using-declaration for {0} and remove qualifier", Name));
61 }
62 
63 // Locates all "using" statements relevant to SelectionDeclContext.
64 class UsingFinder : public RecursiveASTVisitor<UsingFinder> {
65 public:
66   UsingFinder(std::vector<const UsingDecl *> &Results,
67               const DeclContext *SelectionDeclContext, const SourceManager &SM)
68       : Results(Results), SelectionDeclContext(SelectionDeclContext), SM(SM) {}
69 
70   bool VisitUsingDecl(UsingDecl *D) {
71     auto Loc = D->getUsingLoc();
72     if (SM.getFileID(Loc) != SM.getMainFileID()) {
73       return true;
74     }
75     if (D->getDeclContext()->Encloses(SelectionDeclContext)) {
76       Results.push_back(D);
77     }
78     return true;
79   }
80 
81   bool TraverseDecl(Decl *Node) {
82     // There is no need to go deeper into nodes that do not enclose selection,
83     // since "using" there will not affect selection, nor would it make a good
84     // insertion point.
85     if (!Node->getDeclContext() ||
86         Node->getDeclContext()->Encloses(SelectionDeclContext)) {
87       return RecursiveASTVisitor<UsingFinder>::TraverseDecl(Node);
88     }
89     return true;
90   }
91 
92 private:
93   std::vector<const UsingDecl *> &Results;
94   const DeclContext *SelectionDeclContext;
95   const SourceManager &SM;
96 };
97 
98 bool isFullyQualified(const NestedNameSpecifier *NNS) {
99   if (!NNS)
100     return false;
101   return NNS->getKind() == NestedNameSpecifier::Global ||
102          isFullyQualified(NNS->getPrefix());
103 }
104 
105 struct InsertionPointData {
106   // Location to insert the "using" statement. If invalid then the statement
107   // should not be inserted at all (it already exists).
108   SourceLocation Loc;
109   // Extra suffix to place after the "using" statement. Depending on what the
110   // insertion point is anchored to, we may need one or more \n to ensure
111   // proper formatting.
112   std::string Suffix;
113   // Whether using should be fully qualified, even if what the user typed was
114   // not. This is based on our detection of the local style.
115   bool AlwaysFullyQualify = false;
116 };
117 
118 // Finds the best place to insert the "using" statement. Returns invalid
119 // SourceLocation if the "using" statement already exists.
120 //
121 // The insertion point might be a little awkward if the decl we're anchoring to
122 // has a comment in an unfortunate place (e.g. directly above function or using
123 // decl, or immediately following "namespace {". We should add some helpers for
124 // dealing with that and use them in other code modifications as well.
125 llvm::Expected<InsertionPointData>
126 findInsertionPoint(const Tweak::Selection &Inputs,
127                    const NestedNameSpecifierLoc &QualifierToRemove,
128                    const llvm::StringRef Name,
129                    const SourceLocation MustInsertAfterLoc) {
130   auto &SM = Inputs.AST->getSourceManager();
131 
132   // Search for all using decls that affect this point in file. We need this for
133   // two reasons: to skip adding "using" if one already exists and to find best
134   // place to add it, if it doesn't exist.
135   SourceLocation LastUsingLoc;
136   std::vector<const UsingDecl *> Usings;
137   UsingFinder(Usings, &Inputs.ASTSelection.commonAncestor()->getDeclContext(),
138               SM)
139       .TraverseAST(Inputs.AST->getASTContext());
140 
141   auto IsValidPoint = [&](const SourceLocation Loc) {
142     return MustInsertAfterLoc.isInvalid() ||
143            SM.isBeforeInTranslationUnit(MustInsertAfterLoc, Loc);
144   };
145 
146   bool AlwaysFullyQualify = true;
147   for (auto &U : Usings) {
148     // Only "upgrade" to fully qualified is all relevant using decls are fully
149     // qualified. Otherwise trust what the user typed.
150     if (!isFullyQualified(U->getQualifier()))
151       AlwaysFullyQualify = false;
152 
153     if (SM.isBeforeInTranslationUnit(Inputs.Cursor, U->getUsingLoc()))
154       // "Usings" is sorted, so we're done.
155       break;
156     if (const auto *Namespace = U->getQualifier()->getAsNamespace()) {
157       if (Namespace->getCanonicalDecl() ==
158               QualifierToRemove.getNestedNameSpecifier()
159                   ->getAsNamespace()
160                   ->getCanonicalDecl() &&
161           U->getName() == Name) {
162         return InsertionPointData();
163       }
164     }
165 
166     // Insertion point will be before last UsingDecl that affects cursor
167     // position. For most cases this should stick with the local convention of
168     // add using inside or outside namespace.
169     LastUsingLoc = U->getUsingLoc();
170   }
171   if (LastUsingLoc.isValid() && IsValidPoint(LastUsingLoc)) {
172     InsertionPointData Out;
173     Out.Loc = LastUsingLoc;
174     Out.AlwaysFullyQualify = AlwaysFullyQualify;
175     return Out;
176   }
177 
178   // No relevant "using" statements. Try the nearest namespace level.
179   const DeclContext *ParentDeclCtx =
180       &Inputs.ASTSelection.commonAncestor()->getDeclContext();
181   while (ParentDeclCtx && !ParentDeclCtx->isFileContext()) {
182     ParentDeclCtx = ParentDeclCtx->getLexicalParent();
183   }
184   if (auto *ND = llvm::dyn_cast_or_null<NamespaceDecl>(ParentDeclCtx)) {
185     auto Toks = Inputs.AST->getTokens().expandedTokens(ND->getSourceRange());
186     const auto *Tok = llvm::find_if(Toks, [](const syntax::Token &Tok) {
187       return Tok.kind() == tok::l_brace;
188     });
189     if (Tok == Toks.end() || Tok->endLocation().isInvalid()) {
190       return error("Namespace with no {{");
191     }
192     if (!Tok->endLocation().isMacroID() && IsValidPoint(Tok->endLocation())) {
193       InsertionPointData Out;
194       Out.Loc = Tok->endLocation();
195       Out.Suffix = "\n";
196       return Out;
197     }
198   }
199   // No using, no namespace, no idea where to insert. Try above the first
200   // top level decl after MustInsertAfterLoc.
201   auto TLDs = Inputs.AST->getLocalTopLevelDecls();
202   for (const auto &TLD : TLDs) {
203     if (!IsValidPoint(TLD->getBeginLoc()))
204       continue;
205     InsertionPointData Out;
206     Out.Loc = SM.getExpansionLoc(TLD->getBeginLoc());
207     Out.Suffix = "\n\n";
208     return Out;
209   }
210   return error("Cannot find place to insert \"using\"");
211 }
212 
213 bool isNamespaceForbidden(const Tweak::Selection &Inputs,
214                           const NestedNameSpecifier &Namespace) {
215   std::string NamespaceStr = printNamespaceScope(*Namespace.getAsNamespace());
216 
217   for (StringRef Banned : Config::current().Style.FullyQualifiedNamespaces) {
218     StringRef PrefixMatch = NamespaceStr;
219     if (PrefixMatch.consume_front(Banned) && PrefixMatch.consume_front("::"))
220       return true;
221   }
222 
223   return false;
224 }
225 
226 std::string getNNSLAsString(NestedNameSpecifierLoc &NNSL,
227                             const PrintingPolicy &Policy) {
228   std::string Out;
229   llvm::raw_string_ostream OutStream(Out);
230   NNSL.getNestedNameSpecifier()->print(OutStream, Policy);
231   return OutStream.str();
232 }
233 
234 bool AddUsing::prepare(const Selection &Inputs) {
235   auto &SM = Inputs.AST->getSourceManager();
236   const auto &TB = Inputs.AST->getTokens();
237 
238   // Do not suggest "using" in header files. That way madness lies.
239   if (isHeaderFile(SM.getFileEntryForID(SM.getMainFileID())->getName(),
240                    Inputs.AST->getLangOpts()))
241     return false;
242 
243   auto *Node = Inputs.ASTSelection.commonAncestor();
244   if (Node == nullptr)
245     return false;
246 
247   // If we're looking at a type or NestedNameSpecifier, walk up the tree until
248   // we find the "main" node we care about, which would be ElaboratedTypeLoc or
249   // DeclRefExpr.
250   for (; Node->Parent; Node = Node->Parent) {
251     if (Node->ASTNode.get<NestedNameSpecifierLoc>()) {
252       continue;
253     }
254     if (auto *T = Node->ASTNode.get<TypeLoc>()) {
255       if (T->getAs<ElaboratedTypeLoc>()) {
256         break;
257       }
258       if (Node->Parent->ASTNode.get<TypeLoc>() ||
259           Node->Parent->ASTNode.get<NestedNameSpecifierLoc>()) {
260         // Node is TypeLoc, but it's parent is either TypeLoc or
261         // NestedNameSpecifier. In both cases, we want to go up, to find
262         // the outermost TypeLoc.
263         continue;
264       }
265     }
266     break;
267   }
268   if (Node == nullptr)
269     return false;
270 
271   if (auto *D = Node->ASTNode.get<DeclRefExpr>()) {
272     if (auto *II = D->getDecl()->getIdentifier()) {
273       QualifierToRemove = D->getQualifierLoc();
274       Name = II->getName();
275       MustInsertAfterLoc = D->getDecl()->getBeginLoc();
276     }
277   } else if (auto *T = Node->ASTNode.get<TypeLoc>()) {
278     if (auto E = T->getAs<ElaboratedTypeLoc>()) {
279       QualifierToRemove = E.getQualifierLoc();
280       if (!QualifierToRemove)
281         return false;
282 
283       auto NameRange = E.getSourceRange();
284       if (auto T = E.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>()) {
285         // Remove the template arguments from the name.
286         NameRange.setEnd(T.getLAngleLoc().getLocWithOffset(-1));
287       }
288 
289       auto SpelledTokens = TB.spelledForExpanded(TB.expandedTokens(NameRange));
290       if (!SpelledTokens)
291         return false;
292       auto SpelledRange = syntax::Token::range(SM, SpelledTokens->front(),
293                                                SpelledTokens->back());
294       Name = SpelledRange.text(SM);
295 
296       std::string QualifierToRemoveStr = getNNSLAsString(
297           QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy());
298       if (!Name.consume_front(QualifierToRemoveStr))
299         return false; // What's spelled doesn't match the qualifier.
300 
301       if (const auto *ET = E.getTypePtr()) {
302         if (const auto *TDT =
303                 dyn_cast<TypedefType>(ET->getNamedType().getTypePtr())) {
304           MustInsertAfterLoc = TDT->getDecl()->getBeginLoc();
305         } else if (auto *TD = ET->getAsTagDecl()) {
306           MustInsertAfterLoc = TD->getBeginLoc();
307         }
308       }
309     }
310   }
311 
312   // FIXME: This only supports removing qualifiers that are made up of just
313   // namespace names. If qualifier contains a type, we could take the longest
314   // namespace prefix and remove that.
315   if (!QualifierToRemove.hasQualifier() ||
316       !QualifierToRemove.getNestedNameSpecifier()->getAsNamespace() ||
317       Name.empty()) {
318     return false;
319   }
320 
321   if (isNamespaceForbidden(Inputs, *QualifierToRemove.getNestedNameSpecifier()))
322     return false;
323 
324   // Macros are difficult. We only want to offer code action when what's spelled
325   // under the cursor is a namespace qualifier. If it's a macro that expands to
326   // a qualifier, user would not know what code action will actually change.
327   // On the other hand, if the qualifier is part of the macro argument, we
328   // should still support that.
329   if (SM.isMacroBodyExpansion(QualifierToRemove.getBeginLoc()) ||
330       !SM.isWrittenInSameFile(QualifierToRemove.getBeginLoc(),
331                               QualifierToRemove.getEndLoc())) {
332     return false;
333   }
334 
335   return true;
336 }
337 
338 Expected<Tweak::Effect> AddUsing::apply(const Selection &Inputs) {
339   auto &SM = Inputs.AST->getSourceManager();
340 
341   std::string QualifierToRemoveStr = getNNSLAsString(
342       QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy());
343   tooling::Replacements R;
344   if (auto Err = R.add(tooling::Replacement(
345           SM, SM.getSpellingLoc(QualifierToRemove.getBeginLoc()),
346           QualifierToRemoveStr.length(), ""))) {
347     return std::move(Err);
348   }
349 
350   auto InsertionPoint =
351       findInsertionPoint(Inputs, QualifierToRemove, Name, MustInsertAfterLoc);
352   if (!InsertionPoint) {
353     return InsertionPoint.takeError();
354   }
355 
356   if (InsertionPoint->Loc.isValid()) {
357     // Add the using statement at appropriate location.
358     std::string UsingText;
359     llvm::raw_string_ostream UsingTextStream(UsingText);
360     UsingTextStream << "using ";
361     if (InsertionPoint->AlwaysFullyQualify &&
362         !isFullyQualified(QualifierToRemove.getNestedNameSpecifier()))
363       UsingTextStream << "::";
364     UsingTextStream << QualifierToRemoveStr << Name << ";"
365                     << InsertionPoint->Suffix;
366 
367     assert(SM.getFileID(InsertionPoint->Loc) == SM.getMainFileID());
368     if (auto Err = R.add(tooling::Replacement(SM, InsertionPoint->Loc, 0,
369                                               UsingTextStream.str()))) {
370       return std::move(Err);
371     }
372   }
373 
374   return Effect::mainFileEdit(Inputs.AST->getASTContext().getSourceManager(),
375                               std::move(R));
376 }
377 
378 } // namespace
379 } // namespace clangd
380 } // namespace clang
381