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