xref: /llvm-project/clang-tools-extra/clangd/refactor/tweaks/AddUsing.cpp (revision d5953e3e3092f7142a07aa012fc9665ede09e53b)
124bb2d1eSAdam Czachorowski //===--- AddUsing.cpp --------------------------------------------*- C++-*-===//
224bb2d1eSAdam Czachorowski //
324bb2d1eSAdam Czachorowski // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
424bb2d1eSAdam Czachorowski // See https://llvm.org/LICENSE.txt for license information.
524bb2d1eSAdam Czachorowski // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
624bb2d1eSAdam Czachorowski //
724bb2d1eSAdam Czachorowski //===----------------------------------------------------------------------===//
824bb2d1eSAdam Czachorowski 
924bb2d1eSAdam Czachorowski #include "AST.h"
10c894bfd1SAdam Czachorowski #include "Config.h"
1135c2aac6SKadir Cetinkaya #include "SourceCode.h"
1224bb2d1eSAdam Czachorowski #include "refactor/Tweak.h"
13ad97ccf6SSam McCall #include "support/Logger.h"
1424bb2d1eSAdam Czachorowski #include "clang/AST/Decl.h"
1535c2aac6SKadir Cetinkaya #include "clang/AST/Expr.h"
1635c2aac6SKadir Cetinkaya #include "clang/AST/NestedNameSpecifier.h"
1724bb2d1eSAdam Czachorowski #include "clang/AST/RecursiveASTVisitor.h"
1835c2aac6SKadir Cetinkaya #include "clang/AST/Type.h"
1935c2aac6SKadir Cetinkaya #include "clang/AST/TypeLoc.h"
2035c2aac6SKadir Cetinkaya #include "clang/Basic/LLVM.h"
2135c2aac6SKadir Cetinkaya #include "clang/Basic/SourceLocation.h"
2235c2aac6SKadir Cetinkaya #include "clang/Tooling/Core/Replacement.h"
2335c2aac6SKadir Cetinkaya #include "clang/Tooling/Syntax/Tokens.h"
2435c2aac6SKadir Cetinkaya #include "llvm/ADT/StringRef.h"
2535c2aac6SKadir Cetinkaya #include "llvm/Support/FormatVariadic.h"
2635c2aac6SKadir Cetinkaya #include "llvm/Support/raw_ostream.h"
2735c2aac6SKadir Cetinkaya #include <string>
2835c2aac6SKadir Cetinkaya #include <tuple>
2935c2aac6SKadir Cetinkaya #include <utility>
3024bb2d1eSAdam Czachorowski 
3124bb2d1eSAdam Czachorowski namespace clang {
3224bb2d1eSAdam Czachorowski namespace clangd {
3324bb2d1eSAdam Czachorowski namespace {
3424bb2d1eSAdam Czachorowski 
3524bb2d1eSAdam Czachorowski // Tweak for removing full namespace qualifier under cursor on DeclRefExpr and
3624bb2d1eSAdam Czachorowski // types and adding "using" statement instead.
3724bb2d1eSAdam Czachorowski //
3824bb2d1eSAdam Czachorowski // Only qualifiers that refer exclusively to namespaces (no record types) are
3924bb2d1eSAdam Czachorowski // supported. There is some guessing of appropriate place to insert the using
4024bb2d1eSAdam Czachorowski // declaration. If we find any existing usings, we insert it there. If not, we
4124bb2d1eSAdam Czachorowski // insert right after the inner-most relevant namespace declaration. If there is
4224bb2d1eSAdam Czachorowski // none, or there is, but it was declared via macro, we insert above the first
4324bb2d1eSAdam Czachorowski // top level decl.
4424bb2d1eSAdam Czachorowski //
4524bb2d1eSAdam Czachorowski // Currently this only removes qualifier from under the cursor. In the future,
46dd5571d5SKazuaki Ishizaki // we should improve this to remove qualifier from all occurrences of this
4724bb2d1eSAdam Czachorowski // symbol.
4824bb2d1eSAdam Czachorowski class AddUsing : public Tweak {
4924bb2d1eSAdam Czachorowski public:
5024bb2d1eSAdam Czachorowski   const char *id() const override;
5124bb2d1eSAdam Czachorowski 
5224bb2d1eSAdam Czachorowski   bool prepare(const Selection &Inputs) override;
5324bb2d1eSAdam Czachorowski   Expected<Effect> apply(const Selection &Inputs) override;
5424bb2d1eSAdam Czachorowski   std::string title() const override;
kind() const5517747d2eSSam McCall   llvm::StringLiteral kind() const override {
5617747d2eSSam McCall     return CodeAction::REFACTOR_KIND;
5717747d2eSSam McCall   }
5824bb2d1eSAdam Czachorowski 
5924bb2d1eSAdam Czachorowski private:
60f6e59294SAdam Czachorowski   // All of the following are set by prepare().
61f6e59294SAdam Czachorowski   // The qualifier to remove.
6224bb2d1eSAdam Czachorowski   NestedNameSpecifierLoc QualifierToRemove;
6335c2aac6SKadir Cetinkaya   // Qualified name to use when spelling the using declaration. This might be
6435c2aac6SKadir Cetinkaya   // different than SpelledQualifier in presence of error correction.
6535c2aac6SKadir Cetinkaya   std::string QualifierToSpell;
6635c2aac6SKadir Cetinkaya   // The name and qualifier as spelled in the code.
6735c2aac6SKadir Cetinkaya   llvm::StringRef SpelledQualifier;
6835c2aac6SKadir Cetinkaya   llvm::StringRef SpelledName;
69a200501bSAdam Czachorowski   // If valid, the insertion point for "using" statement must come after this.
70a200501bSAdam Czachorowski   // This is relevant when the type is defined in the main file, to make sure
71a200501bSAdam Czachorowski   // the type/function is already defined at the point where "using" is added.
72a200501bSAdam Czachorowski   SourceLocation MustInsertAfterLoc;
7324bb2d1eSAdam Czachorowski };
REGISTER_TWEAK(AddUsing)7424bb2d1eSAdam Czachorowski REGISTER_TWEAK(AddUsing)
7524bb2d1eSAdam Czachorowski 
7624bb2d1eSAdam Czachorowski std::string AddUsing::title() const {
7724bb2d1eSAdam Czachorowski   return std::string(llvm::formatv(
7835c2aac6SKadir Cetinkaya       "Add using-declaration for {0} and remove qualifier", SpelledName));
7924bb2d1eSAdam Czachorowski }
8024bb2d1eSAdam Czachorowski 
8124bb2d1eSAdam Czachorowski // Locates all "using" statements relevant to SelectionDeclContext.
8224bb2d1eSAdam Czachorowski class UsingFinder : public RecursiveASTVisitor<UsingFinder> {
8324bb2d1eSAdam Czachorowski public:
UsingFinder(std::vector<const UsingDecl * > & Results,const DeclContext * SelectionDeclContext,const SourceManager & SM)8424bb2d1eSAdam Czachorowski   UsingFinder(std::vector<const UsingDecl *> &Results,
8524bb2d1eSAdam Czachorowski               const DeclContext *SelectionDeclContext, const SourceManager &SM)
8624bb2d1eSAdam Czachorowski       : Results(Results), SelectionDeclContext(SelectionDeclContext), SM(SM) {}
8724bb2d1eSAdam Czachorowski 
VisitUsingDecl(UsingDecl * D)8824bb2d1eSAdam Czachorowski   bool VisitUsingDecl(UsingDecl *D) {
8924bb2d1eSAdam Czachorowski     auto Loc = D->getUsingLoc();
9024bb2d1eSAdam Czachorowski     if (SM.getFileID(Loc) != SM.getMainFileID()) {
9124bb2d1eSAdam Czachorowski       return true;
9224bb2d1eSAdam Czachorowski     }
9324bb2d1eSAdam Czachorowski     if (D->getDeclContext()->Encloses(SelectionDeclContext)) {
9424bb2d1eSAdam Czachorowski       Results.push_back(D);
9524bb2d1eSAdam Czachorowski     }
9624bb2d1eSAdam Czachorowski     return true;
9724bb2d1eSAdam Czachorowski   }
9824bb2d1eSAdam Czachorowski 
TraverseDecl(Decl * Node)9924bb2d1eSAdam Czachorowski   bool TraverseDecl(Decl *Node) {
10046dbd19aSSam McCall     if (!Node)
10146dbd19aSSam McCall       return true;
10224bb2d1eSAdam Czachorowski     // There is no need to go deeper into nodes that do not enclose selection,
10324bb2d1eSAdam Czachorowski     // since "using" there will not affect selection, nor would it make a good
10424bb2d1eSAdam Czachorowski     // insertion point.
1056aca6032SSam McCall     if (!Node->getDeclContext() ||
1066aca6032SSam McCall         Node->getDeclContext()->Encloses(SelectionDeclContext)) {
10724bb2d1eSAdam Czachorowski       return RecursiveASTVisitor<UsingFinder>::TraverseDecl(Node);
10824bb2d1eSAdam Czachorowski     }
10924bb2d1eSAdam Czachorowski     return true;
11024bb2d1eSAdam Czachorowski   }
11124bb2d1eSAdam Czachorowski 
11224bb2d1eSAdam Czachorowski private:
11324bb2d1eSAdam Czachorowski   std::vector<const UsingDecl *> &Results;
11424bb2d1eSAdam Czachorowski   const DeclContext *SelectionDeclContext;
11524bb2d1eSAdam Czachorowski   const SourceManager &SM;
11624bb2d1eSAdam Czachorowski };
11724bb2d1eSAdam Czachorowski 
isFullyQualified(const NestedNameSpecifier * NNS)1184d90ff59SAdam Czachorowski bool isFullyQualified(const NestedNameSpecifier *NNS) {
1194d90ff59SAdam Czachorowski   if (!NNS)
1204d90ff59SAdam Czachorowski     return false;
1214d90ff59SAdam Czachorowski   return NNS->getKind() == NestedNameSpecifier::Global ||
1224d90ff59SAdam Czachorowski          isFullyQualified(NNS->getPrefix());
1234d90ff59SAdam Czachorowski }
1244d90ff59SAdam Czachorowski 
12524bb2d1eSAdam Czachorowski struct InsertionPointData {
12624bb2d1eSAdam Czachorowski   // Location to insert the "using" statement. If invalid then the statement
12724bb2d1eSAdam Czachorowski   // should not be inserted at all (it already exists).
12824bb2d1eSAdam Czachorowski   SourceLocation Loc;
12924bb2d1eSAdam Czachorowski   // Extra suffix to place after the "using" statement. Depending on what the
13024bb2d1eSAdam Czachorowski   // insertion point is anchored to, we may need one or more \n to ensure
13124bb2d1eSAdam Czachorowski   // proper formatting.
13224bb2d1eSAdam Czachorowski   std::string Suffix;
1334d90ff59SAdam Czachorowski   // Whether using should be fully qualified, even if what the user typed was
1344d90ff59SAdam Czachorowski   // not. This is based on our detection of the local style.
1354d90ff59SAdam Czachorowski   bool AlwaysFullyQualify = false;
13624bb2d1eSAdam Czachorowski };
13724bb2d1eSAdam Czachorowski 
13824bb2d1eSAdam Czachorowski // Finds the best place to insert the "using" statement. Returns invalid
13924bb2d1eSAdam Czachorowski // SourceLocation if the "using" statement already exists.
14024bb2d1eSAdam Czachorowski //
14124bb2d1eSAdam Czachorowski // The insertion point might be a little awkward if the decl we're anchoring to
14224bb2d1eSAdam Czachorowski // has a comment in an unfortunate place (e.g. directly above function or using
14324bb2d1eSAdam Czachorowski // decl, or immediately following "namespace {". We should add some helpers for
14424bb2d1eSAdam Czachorowski // dealing with that and use them in other code modifications as well.
14524bb2d1eSAdam Czachorowski llvm::Expected<InsertionPointData>
findInsertionPoint(const Tweak::Selection & Inputs,const NestedNameSpecifierLoc & QualifierToRemove,const llvm::StringRef Name,const SourceLocation MustInsertAfterLoc)14624bb2d1eSAdam Czachorowski findInsertionPoint(const Tweak::Selection &Inputs,
14724bb2d1eSAdam Czachorowski                    const NestedNameSpecifierLoc &QualifierToRemove,
148a200501bSAdam Czachorowski                    const llvm::StringRef Name,
149a200501bSAdam Czachorowski                    const SourceLocation MustInsertAfterLoc) {
15024bb2d1eSAdam Czachorowski   auto &SM = Inputs.AST->getSourceManager();
15124bb2d1eSAdam Czachorowski 
15224bb2d1eSAdam Czachorowski   // Search for all using decls that affect this point in file. We need this for
15324bb2d1eSAdam Czachorowski   // two reasons: to skip adding "using" if one already exists and to find best
15424bb2d1eSAdam Czachorowski   // place to add it, if it doesn't exist.
15524bb2d1eSAdam Czachorowski   SourceLocation LastUsingLoc;
15624bb2d1eSAdam Czachorowski   std::vector<const UsingDecl *> Usings;
15724bb2d1eSAdam Czachorowski   UsingFinder(Usings, &Inputs.ASTSelection.commonAncestor()->getDeclContext(),
15824bb2d1eSAdam Czachorowski               SM)
15924bb2d1eSAdam Czachorowski       .TraverseAST(Inputs.AST->getASTContext());
16024bb2d1eSAdam Czachorowski 
161a200501bSAdam Czachorowski   auto IsValidPoint = [&](const SourceLocation Loc) {
162a200501bSAdam Czachorowski     return MustInsertAfterLoc.isInvalid() ||
163a200501bSAdam Czachorowski            SM.isBeforeInTranslationUnit(MustInsertAfterLoc, Loc);
164a200501bSAdam Czachorowski   };
165a200501bSAdam Czachorowski 
1664d90ff59SAdam Czachorowski   bool AlwaysFullyQualify = true;
16724bb2d1eSAdam Czachorowski   for (auto &U : Usings) {
1684d90ff59SAdam Czachorowski     // Only "upgrade" to fully qualified is all relevant using decls are fully
1694d90ff59SAdam Czachorowski     // qualified. Otherwise trust what the user typed.
1704d90ff59SAdam Czachorowski     if (!isFullyQualified(U->getQualifier()))
1714d90ff59SAdam Czachorowski       AlwaysFullyQualify = false;
1724d90ff59SAdam Czachorowski 
17324bb2d1eSAdam Czachorowski     if (SM.isBeforeInTranslationUnit(Inputs.Cursor, U->getUsingLoc()))
17424bb2d1eSAdam Czachorowski       // "Usings" is sorted, so we're done.
17524bb2d1eSAdam Czachorowski       break;
1769d87739fSAdam Czachorowski     if (const auto *Namespace = U->getQualifier()->getAsNamespace()) {
1779d87739fSAdam Czachorowski       if (Namespace->getCanonicalDecl() ==
17824bb2d1eSAdam Czachorowski               QualifierToRemove.getNestedNameSpecifier()
17924bb2d1eSAdam Czachorowski                   ->getAsNamespace()
18024bb2d1eSAdam Czachorowski                   ->getCanonicalDecl() &&
18124bb2d1eSAdam Czachorowski           U->getName() == Name) {
18224bb2d1eSAdam Czachorowski         return InsertionPointData();
18324bb2d1eSAdam Czachorowski       }
1849d87739fSAdam Czachorowski     }
185a200501bSAdam Czachorowski 
18624bb2d1eSAdam Czachorowski     // Insertion point will be before last UsingDecl that affects cursor
18724bb2d1eSAdam Czachorowski     // position. For most cases this should stick with the local convention of
18824bb2d1eSAdam Czachorowski     // add using inside or outside namespace.
18924bb2d1eSAdam Czachorowski     LastUsingLoc = U->getUsingLoc();
19024bb2d1eSAdam Czachorowski   }
191a200501bSAdam Czachorowski   if (LastUsingLoc.isValid() && IsValidPoint(LastUsingLoc)) {
19224bb2d1eSAdam Czachorowski     InsertionPointData Out;
19324bb2d1eSAdam Czachorowski     Out.Loc = LastUsingLoc;
1944d90ff59SAdam Czachorowski     Out.AlwaysFullyQualify = AlwaysFullyQualify;
19524bb2d1eSAdam Czachorowski     return Out;
19624bb2d1eSAdam Czachorowski   }
19724bb2d1eSAdam Czachorowski 
19824bb2d1eSAdam Czachorowski   // No relevant "using" statements. Try the nearest namespace level.
19991087153SAdam Czachorowski   const DeclContext *ParentDeclCtx =
20091087153SAdam Czachorowski       &Inputs.ASTSelection.commonAncestor()->getDeclContext();
20191087153SAdam Czachorowski   while (ParentDeclCtx && !ParentDeclCtx->isFileContext()) {
20291087153SAdam Czachorowski     ParentDeclCtx = ParentDeclCtx->getLexicalParent();
20391087153SAdam Czachorowski   }
20491087153SAdam Czachorowski   if (auto *ND = llvm::dyn_cast_or_null<NamespaceDecl>(ParentDeclCtx)) {
20524bb2d1eSAdam Czachorowski     auto Toks = Inputs.AST->getTokens().expandedTokens(ND->getSourceRange());
20624bb2d1eSAdam Czachorowski     const auto *Tok = llvm::find_if(Toks, [](const syntax::Token &Tok) {
20724bb2d1eSAdam Czachorowski       return Tok.kind() == tok::l_brace;
20824bb2d1eSAdam Czachorowski     });
20924bb2d1eSAdam Czachorowski     if (Tok == Toks.end() || Tok->endLocation().isInvalid()) {
210fbf17457SKadir Cetinkaya       return error("Namespace with no {{");
21124bb2d1eSAdam Czachorowski     }
212a200501bSAdam Czachorowski     if (!Tok->endLocation().isMacroID() && IsValidPoint(Tok->endLocation())) {
21324bb2d1eSAdam Czachorowski       InsertionPointData Out;
21424bb2d1eSAdam Czachorowski       Out.Loc = Tok->endLocation();
21524bb2d1eSAdam Czachorowski       Out.Suffix = "\n";
21624bb2d1eSAdam Czachorowski       return Out;
21724bb2d1eSAdam Czachorowski     }
21824bb2d1eSAdam Czachorowski   }
21924bb2d1eSAdam Czachorowski   // No using, no namespace, no idea where to insert. Try above the first
220a200501bSAdam Czachorowski   // top level decl after MustInsertAfterLoc.
22124bb2d1eSAdam Czachorowski   auto TLDs = Inputs.AST->getLocalTopLevelDecls();
222a200501bSAdam Czachorowski   for (const auto &TLD : TLDs) {
223a200501bSAdam Czachorowski     if (!IsValidPoint(TLD->getBeginLoc()))
224a200501bSAdam Czachorowski       continue;
22524bb2d1eSAdam Czachorowski     InsertionPointData Out;
226a200501bSAdam Czachorowski     Out.Loc = SM.getExpansionLoc(TLD->getBeginLoc());
22724bb2d1eSAdam Czachorowski     Out.Suffix = "\n\n";
22824bb2d1eSAdam Czachorowski     return Out;
22924bb2d1eSAdam Czachorowski   }
230a200501bSAdam Czachorowski   return error("Cannot find place to insert \"using\"");
231a200501bSAdam Czachorowski }
23224bb2d1eSAdam Czachorowski 
isNamespaceForbidden(const Tweak::Selection & Inputs,const NestedNameSpecifier & Namespace)233c894bfd1SAdam Czachorowski bool isNamespaceForbidden(const Tweak::Selection &Inputs,
234c894bfd1SAdam Czachorowski                           const NestedNameSpecifier &Namespace) {
235c894bfd1SAdam Czachorowski   std::string NamespaceStr = printNamespaceScope(*Namespace.getAsNamespace());
236c894bfd1SAdam Czachorowski 
237c894bfd1SAdam Czachorowski   for (StringRef Banned : Config::current().Style.FullyQualifiedNamespaces) {
238c894bfd1SAdam Czachorowski     StringRef PrefixMatch = NamespaceStr;
239c894bfd1SAdam Czachorowski     if (PrefixMatch.consume_front(Banned) && PrefixMatch.consume_front("::"))
240c894bfd1SAdam Czachorowski       return true;
241c894bfd1SAdam Czachorowski   }
242c894bfd1SAdam Czachorowski 
243c894bfd1SAdam Czachorowski   return false;
244c894bfd1SAdam Czachorowski }
245c894bfd1SAdam Czachorowski 
getNNSLAsString(NestedNameSpecifierLoc & NNSL,const PrintingPolicy & Policy)246f6e59294SAdam Czachorowski std::string getNNSLAsString(NestedNameSpecifierLoc &NNSL,
247f6e59294SAdam Czachorowski                             const PrintingPolicy &Policy) {
248f6e59294SAdam Czachorowski   std::string Out;
249f6e59294SAdam Czachorowski   llvm::raw_string_ostream OutStream(Out);
250f6e59294SAdam Czachorowski   NNSL.getNestedNameSpecifier()->print(OutStream, Policy);
251f6e59294SAdam Czachorowski   return OutStream.str();
252f6e59294SAdam Czachorowski }
253f6e59294SAdam Czachorowski 
prepare(const Selection & Inputs)25424bb2d1eSAdam Czachorowski bool AddUsing::prepare(const Selection &Inputs) {
25524bb2d1eSAdam Czachorowski   auto &SM = Inputs.AST->getSourceManager();
256f6e59294SAdam Czachorowski   const auto &TB = Inputs.AST->getTokens();
25731978731SAdam Czachorowski 
25831978731SAdam Czachorowski   // Do not suggest "using" in header files. That way madness lies.
2596f3f1e98SSam McCall   if (isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
26031978731SAdam Czachorowski                    Inputs.AST->getLangOpts()))
26131978731SAdam Czachorowski     return false;
26231978731SAdam Czachorowski 
26324bb2d1eSAdam Czachorowski   auto *Node = Inputs.ASTSelection.commonAncestor();
26424bb2d1eSAdam Czachorowski   if (Node == nullptr)
26524bb2d1eSAdam Czachorowski     return false;
26624bb2d1eSAdam Czachorowski 
26724bb2d1eSAdam Czachorowski   // If we're looking at a type or NestedNameSpecifier, walk up the tree until
26824bb2d1eSAdam Czachorowski   // we find the "main" node we care about, which would be ElaboratedTypeLoc or
26924bb2d1eSAdam Czachorowski   // DeclRefExpr.
27024bb2d1eSAdam Czachorowski   for (; Node->Parent; Node = Node->Parent) {
27124bb2d1eSAdam Czachorowski     if (Node->ASTNode.get<NestedNameSpecifierLoc>()) {
27224bb2d1eSAdam Czachorowski       continue;
273ec4a2c95SChristian Kühnel     }
274ec4a2c95SChristian Kühnel     if (auto *T = Node->ASTNode.get<TypeLoc>()) {
27524bb2d1eSAdam Czachorowski       if (T->getAs<ElaboratedTypeLoc>()) {
27624bb2d1eSAdam Czachorowski         break;
2772f8da95eSChristian Kühnel       }
2782f8da95eSChristian Kühnel       if (Node->Parent->ASTNode.get<TypeLoc>() ||
27924bb2d1eSAdam Czachorowski           Node->Parent->ASTNode.get<NestedNameSpecifierLoc>()) {
28024bb2d1eSAdam Czachorowski         // Node is TypeLoc, but it's parent is either TypeLoc or
28124bb2d1eSAdam Czachorowski         // NestedNameSpecifier. In both cases, we want to go up, to find
28224bb2d1eSAdam Czachorowski         // the outermost TypeLoc.
28324bb2d1eSAdam Czachorowski         continue;
28424bb2d1eSAdam Czachorowski       }
28524bb2d1eSAdam Czachorowski     }
28624bb2d1eSAdam Czachorowski     break;
28724bb2d1eSAdam Czachorowski   }
28824bb2d1eSAdam Czachorowski   if (Node == nullptr)
28924bb2d1eSAdam Czachorowski     return false;
29024bb2d1eSAdam Czachorowski 
29151df1a9aSKadir Cetinkaya   // Closed range for the fully qualified name as spelled in source code.
29235c2aac6SKadir Cetinkaya   SourceRange SpelledNameRange;
29324bb2d1eSAdam Czachorowski   if (auto *D = Node->ASTNode.get<DeclRefExpr>()) {
2947370e45fSKazu Hirata     if (D->getDecl()->getIdentifier()) {
29524bb2d1eSAdam Czachorowski       QualifierToRemove = D->getQualifierLoc();
29651df1a9aSKadir Cetinkaya       // Use the name range rather than expr, as the latter can contain template
29751df1a9aSKadir Cetinkaya       // arguments in the range.
29835c2aac6SKadir Cetinkaya       SpelledNameRange = D->getSourceRange();
29951df1a9aSKadir Cetinkaya       // Remove the template arguments from the name, as they shouldn't be
30051df1a9aSKadir Cetinkaya       // spelled in the using declaration.
30151df1a9aSKadir Cetinkaya       if (auto AngleLoc = D->getLAngleLoc(); AngleLoc.isValid())
30251df1a9aSKadir Cetinkaya         SpelledNameRange.setEnd(AngleLoc.getLocWithOffset(-1));
303a200501bSAdam Czachorowski       MustInsertAfterLoc = D->getDecl()->getBeginLoc();
3048e7bb37dSAdam Czachorowski     }
30524bb2d1eSAdam Czachorowski   } else if (auto *T = Node->ASTNode.get<TypeLoc>()) {
30624bb2d1eSAdam Czachorowski     if (auto E = T->getAs<ElaboratedTypeLoc>()) {
30724bb2d1eSAdam Czachorowski       QualifierToRemove = E.getQualifierLoc();
308f6e59294SAdam Czachorowski 
30935c2aac6SKadir Cetinkaya       SpelledNameRange = E.getSourceRange();
31021745241SAdam Czachorowski       if (auto T = E.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>()) {
31121745241SAdam Czachorowski         // Remove the template arguments from the name.
31235c2aac6SKadir Cetinkaya         SpelledNameRange.setEnd(T.getLAngleLoc().getLocWithOffset(-1));
31321745241SAdam Czachorowski       }
31421745241SAdam Czachorowski 
315a200501bSAdam Czachorowski       if (const auto *ET = E.getTypePtr()) {
316a200501bSAdam Czachorowski         if (const auto *TDT =
317a200501bSAdam Czachorowski                 dyn_cast<TypedefType>(ET->getNamedType().getTypePtr())) {
318a200501bSAdam Czachorowski           MustInsertAfterLoc = TDT->getDecl()->getBeginLoc();
319a200501bSAdam Czachorowski         } else if (auto *TD = ET->getAsTagDecl()) {
320a200501bSAdam Czachorowski           MustInsertAfterLoc = TD->getBeginLoc();
321a200501bSAdam Czachorowski         }
322a200501bSAdam Czachorowski       }
32324bb2d1eSAdam Czachorowski     }
32424bb2d1eSAdam Czachorowski   }
32535c2aac6SKadir Cetinkaya   if (!QualifierToRemove ||
32624bb2d1eSAdam Czachorowski       // FIXME: This only supports removing qualifiers that are made up of just
32735c2aac6SKadir Cetinkaya       // namespace names. If qualifier contains a type, we could take the
32835c2aac6SKadir Cetinkaya       // longest namespace prefix and remove that.
32924bb2d1eSAdam Czachorowski       !QualifierToRemove.getNestedNameSpecifier()->getAsNamespace() ||
33035c2aac6SKadir Cetinkaya       // Respect user config.
33135c2aac6SKadir Cetinkaya       isNamespaceForbidden(Inputs, *QualifierToRemove.getNestedNameSpecifier()))
33224bb2d1eSAdam Czachorowski     return false;
33324bb2d1eSAdam Czachorowski   // Macros are difficult. We only want to offer code action when what's spelled
33424bb2d1eSAdam Czachorowski   // under the cursor is a namespace qualifier. If it's a macro that expands to
33524bb2d1eSAdam Czachorowski   // a qualifier, user would not know what code action will actually change.
33624bb2d1eSAdam Czachorowski   // On the other hand, if the qualifier is part of the macro argument, we
33724bb2d1eSAdam Czachorowski   // should still support that.
33824bb2d1eSAdam Czachorowski   if (SM.isMacroBodyExpansion(QualifierToRemove.getBeginLoc()) ||
33924bb2d1eSAdam Czachorowski       !SM.isWrittenInSameFile(QualifierToRemove.getBeginLoc(),
34024bb2d1eSAdam Czachorowski                               QualifierToRemove.getEndLoc())) {
34124bb2d1eSAdam Czachorowski     return false;
34224bb2d1eSAdam Czachorowski   }
34324bb2d1eSAdam Czachorowski 
34435c2aac6SKadir Cetinkaya   auto SpelledTokens =
34535c2aac6SKadir Cetinkaya       TB.spelledForExpanded(TB.expandedTokens(SpelledNameRange));
34635c2aac6SKadir Cetinkaya   if (!SpelledTokens)
34735c2aac6SKadir Cetinkaya     return false;
34835c2aac6SKadir Cetinkaya   auto SpelledRange =
34935c2aac6SKadir Cetinkaya       syntax::Token::range(SM, SpelledTokens->front(), SpelledTokens->back());
35035c2aac6SKadir Cetinkaya   // We only drop qualifiers that're namespaces, so this is safe.
35135c2aac6SKadir Cetinkaya   std::tie(SpelledQualifier, SpelledName) =
35235c2aac6SKadir Cetinkaya       splitQualifiedName(SpelledRange.text(SM));
35335c2aac6SKadir Cetinkaya   QualifierToSpell = getNNSLAsString(
35435c2aac6SKadir Cetinkaya       QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy());
355*d5953e3eSKazu Hirata   if (!llvm::StringRef(QualifierToSpell).ends_with(SpelledQualifier) ||
35635c2aac6SKadir Cetinkaya       SpelledName.empty())
35735c2aac6SKadir Cetinkaya     return false; // What's spelled doesn't match the qualifier.
35824bb2d1eSAdam Czachorowski   return true;
35924bb2d1eSAdam Czachorowski }
36024bb2d1eSAdam Czachorowski 
apply(const Selection & Inputs)36124bb2d1eSAdam Czachorowski Expected<Tweak::Effect> AddUsing::apply(const Selection &Inputs) {
36224bb2d1eSAdam Czachorowski   auto &SM = Inputs.AST->getSourceManager();
36324bb2d1eSAdam Czachorowski 
36424bb2d1eSAdam Czachorowski   tooling::Replacements R;
36524bb2d1eSAdam Czachorowski   if (auto Err = R.add(tooling::Replacement(
366f6e59294SAdam Czachorowski           SM, SM.getSpellingLoc(QualifierToRemove.getBeginLoc()),
36735c2aac6SKadir Cetinkaya           SpelledQualifier.size(), ""))) {
36824bb2d1eSAdam Czachorowski     return std::move(Err);
36924bb2d1eSAdam Czachorowski   }
37024bb2d1eSAdam Czachorowski 
37135c2aac6SKadir Cetinkaya   auto InsertionPoint = findInsertionPoint(Inputs, QualifierToRemove,
37235c2aac6SKadir Cetinkaya                                            SpelledName, MustInsertAfterLoc);
37324bb2d1eSAdam Czachorowski   if (!InsertionPoint) {
37424bb2d1eSAdam Czachorowski     return InsertionPoint.takeError();
37524bb2d1eSAdam Czachorowski   }
37624bb2d1eSAdam Czachorowski 
37724bb2d1eSAdam Czachorowski   if (InsertionPoint->Loc.isValid()) {
37824bb2d1eSAdam Czachorowski     // Add the using statement at appropriate location.
37924bb2d1eSAdam Czachorowski     std::string UsingText;
38024bb2d1eSAdam Czachorowski     llvm::raw_string_ostream UsingTextStream(UsingText);
38124bb2d1eSAdam Czachorowski     UsingTextStream << "using ";
3824d90ff59SAdam Czachorowski     if (InsertionPoint->AlwaysFullyQualify &&
3834d90ff59SAdam Czachorowski         !isFullyQualified(QualifierToRemove.getNestedNameSpecifier()))
3844d90ff59SAdam Czachorowski       UsingTextStream << "::";
38535c2aac6SKadir Cetinkaya     UsingTextStream << QualifierToSpell << SpelledName << ";"
386f6e59294SAdam Czachorowski                     << InsertionPoint->Suffix;
38724bb2d1eSAdam Czachorowski 
38824bb2d1eSAdam Czachorowski     assert(SM.getFileID(InsertionPoint->Loc) == SM.getMainFileID());
38924bb2d1eSAdam Czachorowski     if (auto Err = R.add(tooling::Replacement(SM, InsertionPoint->Loc, 0,
39024bb2d1eSAdam Czachorowski                                               UsingTextStream.str()))) {
39124bb2d1eSAdam Czachorowski       return std::move(Err);
39224bb2d1eSAdam Czachorowski     }
39324bb2d1eSAdam Czachorowski   }
39424bb2d1eSAdam Czachorowski 
39524bb2d1eSAdam Czachorowski   return Effect::mainFileEdit(Inputs.AST->getASTContext().getSourceManager(),
39624bb2d1eSAdam Czachorowski                               std::move(R));
39724bb2d1eSAdam Czachorowski }
39824bb2d1eSAdam Czachorowski 
39924bb2d1eSAdam Czachorowski } // namespace
40024bb2d1eSAdam Czachorowski } // namespace clangd
40124bb2d1eSAdam Czachorowski } // namespace clang
402