clang-tools  11.0.0
AddUsing.cpp
Go to the documentation of this file.
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 "FindTarget.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  Intent intent() const override { return Refactor; }
41 
42 private:
43  // The qualifier to remove. Set by prepare().
44  NestedNameSpecifierLoc QualifierToRemove;
45  // The name following QualifierToRemove. Set by prepare().
46  llvm::StringRef Name;
47 };
48 REGISTER_TWEAK(AddUsing)
49 
50 std::string AddUsing::title() const {
51  return std::string(llvm::formatv(
52  "Add using-declaration for {0} and remove qualifier.", Name));
53 }
54 
55 // Locates all "using" statements relevant to SelectionDeclContext.
56 class UsingFinder : public RecursiveASTVisitor<UsingFinder> {
57 public:
58  UsingFinder(std::vector<const UsingDecl *> &Results,
59  const DeclContext *SelectionDeclContext, const SourceManager &SM)
60  : Results(Results), SelectionDeclContext(SelectionDeclContext), SM(SM) {}
61 
62  bool VisitUsingDecl(UsingDecl *D) {
63  auto Loc = D->getUsingLoc();
64  if (SM.getFileID(Loc) != SM.getMainFileID()) {
65  return true;
66  }
67  if (D->getDeclContext()->Encloses(SelectionDeclContext)) {
68  Results.push_back(D);
69  }
70  return true;
71  }
72 
73  bool TraverseDecl(Decl *Node) {
74  // There is no need to go deeper into nodes that do not enclose selection,
75  // since "using" there will not affect selection, nor would it make a good
76  // insertion point.
77  if (Node->getDeclContext()->Encloses(SelectionDeclContext)) {
78  return RecursiveASTVisitor<UsingFinder>::TraverseDecl(Node);
79  }
80  return true;
81  }
82 
83 private:
84  std::vector<const UsingDecl *> &Results;
85  const DeclContext *SelectionDeclContext;
86  const SourceManager &SM;
87 };
88 
89 struct InsertionPointData {
90  // Location to insert the "using" statement. If invalid then the statement
91  // should not be inserted at all (it already exists).
92  SourceLocation Loc;
93  // Extra suffix to place after the "using" statement. Depending on what the
94  // insertion point is anchored to, we may need one or more \n to ensure
95  // proper formatting.
96  std::string Suffix;
97 };
98 
99 // Finds the best place to insert the "using" statement. Returns invalid
100 // SourceLocation if the "using" statement already exists.
101 //
102 // The insertion point might be a little awkward if the decl we're anchoring to
103 // has a comment in an unfortunate place (e.g. directly above function or using
104 // decl, or immediately following "namespace {". We should add some helpers for
105 // dealing with that and use them in other code modifications as well.
106 llvm::Expected<InsertionPointData>
107 findInsertionPoint(const Tweak::Selection &Inputs,
108  const NestedNameSpecifierLoc &QualifierToRemove,
109  const llvm::StringRef Name) {
110  auto &SM = Inputs.AST->getSourceManager();
111 
112  // Search for all using decls that affect this point in file. We need this for
113  // two reasons: to skip adding "using" if one already exists and to find best
114  // place to add it, if it doesn't exist.
115  SourceLocation LastUsingLoc;
116  std::vector<const UsingDecl *> Usings;
117  UsingFinder(Usings, &Inputs.ASTSelection.commonAncestor()->getDeclContext(),
118  SM)
119  .TraverseAST(Inputs.AST->getASTContext());
120 
121  for (auto &U : Usings) {
122  if (SM.isBeforeInTranslationUnit(Inputs.Cursor, U->getUsingLoc()))
123  // "Usings" is sorted, so we're done.
124  break;
125  if (U->getQualifier()->getAsNamespace()->getCanonicalDecl() ==
126  QualifierToRemove.getNestedNameSpecifier()
127  ->getAsNamespace()
128  ->getCanonicalDecl() &&
129  U->getName() == Name) {
130  return InsertionPointData();
131  }
132  // Insertion point will be before last UsingDecl that affects cursor
133  // position. For most cases this should stick with the local convention of
134  // add using inside or outside namespace.
135  LastUsingLoc = U->getUsingLoc();
136  }
137  if (LastUsingLoc.isValid()) {
138  InsertionPointData Out;
139  Out.Loc = LastUsingLoc;
140  return Out;
141  }
142 
143  // No relevant "using" statements. Try the nearest namespace level.
144  const DeclContext *ParentDeclCtx =
145  &Inputs.ASTSelection.commonAncestor()->getDeclContext();
146  while (ParentDeclCtx && !ParentDeclCtx->isFileContext()) {
147  ParentDeclCtx = ParentDeclCtx->getLexicalParent();
148  }
149  if (auto *ND = llvm::dyn_cast_or_null<NamespaceDecl>(ParentDeclCtx)) {
150  auto Toks = Inputs.AST->getTokens().expandedTokens(ND->getSourceRange());
151  const auto *Tok = llvm::find_if(Toks, [](const syntax::Token &Tok) {
152  return Tok.kind() == tok::l_brace;
153  });
154  if (Tok == Toks.end() || Tok->endLocation().isInvalid()) {
155  return llvm::createStringError(llvm::inconvertibleErrorCode(),
156  "Namespace with no {");
157  }
158  if (!Tok->endLocation().isMacroID()) {
159  InsertionPointData Out;
160  Out.Loc = Tok->endLocation();
161  Out.Suffix = "\n";
162  return Out;
163  }
164  }
165  // No using, no namespace, no idea where to insert. Try above the first
166  // top level decl.
167  auto TLDs = Inputs.AST->getLocalTopLevelDecls();
168  if (TLDs.empty()) {
169  return llvm::createStringError(llvm::inconvertibleErrorCode(),
170  "Cannot find place to insert \"using\"");
171  }
172  InsertionPointData Out;
173  Out.Loc = SM.getExpansionLoc(TLDs[0]->getBeginLoc());
174  Out.Suffix = "\n\n";
175  return Out;
176 }
177 
178 bool AddUsing::prepare(const Selection &Inputs) {
179  auto &SM = Inputs.AST->getSourceManager();
180 
181  // Do not suggest "using" in header files. That way madness lies.
182  if (isHeaderFile(SM.getFileEntryForID(SM.getMainFileID())->getName(),
183  Inputs.AST->getLangOpts()))
184  return false;
185 
186  auto *Node = Inputs.ASTSelection.commonAncestor();
187  if (Node == nullptr)
188  return false;
189 
190  // If we're looking at a type or NestedNameSpecifier, walk up the tree until
191  // we find the "main" node we care about, which would be ElaboratedTypeLoc or
192  // DeclRefExpr.
193  for (; Node->Parent; Node = Node->Parent) {
194  if (Node->ASTNode.get<NestedNameSpecifierLoc>()) {
195  continue;
196  } else if (auto *T = Node->ASTNode.get<TypeLoc>()) {
197  if (T->getAs<ElaboratedTypeLoc>()) {
198  break;
199  } else if (Node->Parent->ASTNode.get<TypeLoc>() ||
200  Node->Parent->ASTNode.get<NestedNameSpecifierLoc>()) {
201  // Node is TypeLoc, but it's parent is either TypeLoc or
202  // NestedNameSpecifier. In both cases, we want to go up, to find
203  // the outermost TypeLoc.
204  continue;
205  }
206  }
207  break;
208  }
209  if (Node == nullptr)
210  return false;
211 
212  if (auto *D = Node->ASTNode.get<DeclRefExpr>()) {
213  if (auto *II = D->getDecl()->getIdentifier()) {
214  QualifierToRemove = D->getQualifierLoc();
215  Name = II->getName();
216  }
217  } else if (auto *T = Node->ASTNode.get<TypeLoc>()) {
218  if (auto E = T->getAs<ElaboratedTypeLoc>()) {
219  if (auto *BaseTypeIdentifier =
220  E.getType().getUnqualifiedType().getBaseTypeIdentifier()) {
221  Name = BaseTypeIdentifier->getName();
222  QualifierToRemove = E.getQualifierLoc();
223  }
224  }
225  }
226 
227  // FIXME: This only supports removing qualifiers that are made up of just
228  // namespace names. If qualifier contains a type, we could take the longest
229  // namespace prefix and remove that.
230  if (!QualifierToRemove.hasQualifier() ||
231  !QualifierToRemove.getNestedNameSpecifier()->getAsNamespace() ||
232  Name.empty()) {
233  return false;
234  }
235 
236  // Macros are difficult. We only want to offer code action when what's spelled
237  // under the cursor is a namespace qualifier. If it's a macro that expands to
238  // a qualifier, user would not know what code action will actually change.
239  // On the other hand, if the qualifier is part of the macro argument, we
240  // should still support that.
241  if (SM.isMacroBodyExpansion(QualifierToRemove.getBeginLoc()) ||
242  !SM.isWrittenInSameFile(QualifierToRemove.getBeginLoc(),
243  QualifierToRemove.getEndLoc())) {
244  return false;
245  }
246 
247  return true;
248 }
249 
250 Expected<Tweak::Effect> AddUsing::apply(const Selection &Inputs) {
251  auto &SM = Inputs.AST->getSourceManager();
252  auto &TB = Inputs.AST->getTokens();
253 
254  // Determine the length of the qualifier under the cursor, then remove it.
255  auto SpelledTokens = TB.spelledForExpanded(
256  TB.expandedTokens(QualifierToRemove.getSourceRange()));
257  if (!SpelledTokens) {
258  return llvm::createStringError(
259  llvm::inconvertibleErrorCode(),
260  "Could not determine length of the qualifier");
261  }
262  unsigned Length =
263  syntax::Token::range(SM, SpelledTokens->front(), SpelledTokens->back())
264  .length();
265  tooling::Replacements R;
266  if (auto Err = R.add(tooling::Replacement(
267  SM, SpelledTokens->front().location(), Length, ""))) {
268  return std::move(Err);
269  }
270 
271  auto InsertionPoint = findInsertionPoint(Inputs, QualifierToRemove, Name);
272  if (!InsertionPoint) {
273  return InsertionPoint.takeError();
274  }
275 
276  if (InsertionPoint->Loc.isValid()) {
277  // Add the using statement at appropriate location.
278  std::string UsingText;
279  llvm::raw_string_ostream UsingTextStream(UsingText);
280  UsingTextStream << "using ";
281  QualifierToRemove.getNestedNameSpecifier()->print(
282  UsingTextStream, Inputs.AST->getASTContext().getPrintingPolicy());
283  UsingTextStream << Name << ";" << InsertionPoint->Suffix;
284 
285  assert(SM.getFileID(InsertionPoint->Loc) == SM.getMainFileID());
286  if (auto Err = R.add(tooling::Replacement(SM, InsertionPoint->Loc, 0,
287  UsingTextStream.str()))) {
288  return std::move(Err);
289  }
290  }
291 
292  return Effect::mainFileEdit(Inputs.AST->getASTContext().getSourceManager(),
293  std::move(R));
294 }
295 
296 } // namespace
297 } // namespace clangd
298 } // namespace clang
Suffix
std::string Suffix
Definition: AddUsing.cpp:96
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
InsertionPoint
SourceLocation InsertionPoint
Definition: ExtractFunction.cpp:305
FindTarget.h
Length
unsigned Length
Definition: SourceCodeTests.cpp:96
Inputs
ParseInputs Inputs
Definition: TUScheduler.cpp:321
Name
llvm::StringRef Name
Definition: CodeComplete.cpp:160
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
Tweak.h
Logger.h
Results
std::vector< CodeCompletionResult > Results
Definition: CodeComplete.cpp:712
clang::clangd::isHeaderFile
bool isHeaderFile(llvm::StringRef FileName, llvm::Optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
Definition: SourceCode.cpp:1095
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Out
CompiledFragmentImpl & Out
Definition: ConfigCompile.cpp:70
Loc
SourceLocation Loc
Definition: AddUsing.cpp:92
REGISTER_TWEAK
#define REGISTER_TWEAK(Subclass)
Definition: Tweak.h:129
AST.h