clang-tools  14.0.0git
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 "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 {
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)) {
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  } else if (auto *T = Node->ASTNode.get<TypeLoc>()) {
254  if (T->getAs<ElaboratedTypeLoc>()) {
255  break;
256  } else if (Node->Parent->ASTNode.get<TypeLoc>() ||
257  Node->Parent->ASTNode.get<NestedNameSpecifierLoc>()) {
258  // Node is TypeLoc, but it's parent is either TypeLoc or
259  // NestedNameSpecifier. In both cases, we want to go up, to find
260  // the outermost TypeLoc.
261  continue;
262  }
263  }
264  break;
265  }
266  if (Node == nullptr)
267  return false;
268 
269  if (auto *D = Node->ASTNode.get<DeclRefExpr>()) {
270  if (auto *II = D->getDecl()->getIdentifier()) {
271  QualifierToRemove = D->getQualifierLoc();
272  Name = II->getName();
273  MustInsertAfterLoc = D->getDecl()->getBeginLoc();
274  }
275  } else if (auto *T = Node->ASTNode.get<TypeLoc>()) {
276  if (auto E = T->getAs<ElaboratedTypeLoc>()) {
277  QualifierToRemove = E.getQualifierLoc();
278  if (!QualifierToRemove)
279  return false;
280 
281  auto SpelledTokens =
282  TB.spelledForExpanded(TB.expandedTokens(E.getSourceRange()));
283  if (!SpelledTokens)
284  return false;
285  auto SpelledRange = syntax::Token::range(SM, SpelledTokens->front(),
286  SpelledTokens->back());
287  Name = SpelledRange.text(SM);
288 
289  std::string QualifierToRemoveStr = getNNSLAsString(
290  QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy());
291  if (!Name.consume_front(QualifierToRemoveStr))
292  return false; // What's spelled doesn't match the qualifier.
293 
294  if (const auto *ET = E.getTypePtr()) {
295  if (const auto *TDT =
296  dyn_cast<TypedefType>(ET->getNamedType().getTypePtr())) {
297  MustInsertAfterLoc = TDT->getDecl()->getBeginLoc();
298  } else if (auto *TD = ET->getAsTagDecl()) {
299  MustInsertAfterLoc = TD->getBeginLoc();
300  }
301  }
302  }
303  }
304 
305  // FIXME: This only supports removing qualifiers that are made up of just
306  // namespace names. If qualifier contains a type, we could take the longest
307  // namespace prefix and remove that.
308  if (!QualifierToRemove.hasQualifier() ||
309  !QualifierToRemove.getNestedNameSpecifier()->getAsNamespace() ||
310  Name.empty()) {
311  return false;
312  }
313 
314  if (isNamespaceForbidden(Inputs, *QualifierToRemove.getNestedNameSpecifier()))
315  return false;
316 
317  // Macros are difficult. We only want to offer code action when what's spelled
318  // under the cursor is a namespace qualifier. If it's a macro that expands to
319  // a qualifier, user would not know what code action will actually change.
320  // On the other hand, if the qualifier is part of the macro argument, we
321  // should still support that.
322  if (SM.isMacroBodyExpansion(QualifierToRemove.getBeginLoc()) ||
323  !SM.isWrittenInSameFile(QualifierToRemove.getBeginLoc(),
324  QualifierToRemove.getEndLoc())) {
325  return false;
326  }
327 
328  return true;
329 }
330 
331 Expected<Tweak::Effect> AddUsing::apply(const Selection &Inputs) {
332  auto &SM = Inputs.AST->getSourceManager();
333 
334  std::string QualifierToRemoveStr = getNNSLAsString(
335  QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy());
336  tooling::Replacements R;
337  if (auto Err = R.add(tooling::Replacement(
338  SM, SM.getSpellingLoc(QualifierToRemove.getBeginLoc()),
339  QualifierToRemoveStr.length(), ""))) {
340  return std::move(Err);
341  }
342 
343  auto InsertionPoint =
344  findInsertionPoint(Inputs, QualifierToRemove, Name, MustInsertAfterLoc);
345  if (!InsertionPoint) {
346  return InsertionPoint.takeError();
347  }
348 
349  if (InsertionPoint->Loc.isValid()) {
350  // Add the using statement at appropriate location.
351  std::string UsingText;
352  llvm::raw_string_ostream UsingTextStream(UsingText);
353  UsingTextStream << "using ";
354  if (InsertionPoint->AlwaysFullyQualify &&
355  !isFullyQualified(QualifierToRemove.getNestedNameSpecifier()))
356  UsingTextStream << "::";
357  UsingTextStream << QualifierToRemoveStr << Name << ";"
358  << InsertionPoint->Suffix;
359 
360  assert(SM.getFileID(InsertionPoint->Loc) == SM.getMainFileID());
361  if (auto Err = R.add(tooling::Replacement(SM, InsertionPoint->Loc, 0,
362  UsingTextStream.str()))) {
363  return std::move(Err);
364  }
365  }
366 
367  return Effect::mainFileEdit(Inputs.AST->getASTContext().getSourceManager(),
368  std::move(R));
369 }
370 
371 } // namespace
372 } // namespace clangd
373 } // namespace clang
Suffix
std::string Suffix
Definition: AddUsing.cpp:112
RecursiveASTVisitor
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
clang::clangd::error
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition: Logger.h:80
InsertionPoint
SourceLocation InsertionPoint
Definition: ExtractFunction.cpp:350
clang::clangd::printNamespaceScope
std::string printNamespaceScope(const DeclContext &DC)
Returns the first enclosing namespace scope starting from DC.
Definition: AST.cpp:283
FindTarget.h
Inputs
ParseInputs Inputs
Definition: TUScheduler.cpp:451
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
Name
llvm::StringRef Name
Definition: CodeComplete.cpp:162
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
Tweak.h
Logger.h
Results
std::vector< CodeCompletionResult > Results
Definition: CodeComplete.cpp:771
Config.h
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:1152
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::CodeAction::REFACTOR_KIND
const static llvm::StringLiteral REFACTOR_KIND
Definition: Protocol.h:980
clang::clangd::Config::current
static const Config & current()
Returns the Config of the current Context, or an empty configuration.
Definition: Config.cpp:17
AlwaysFullyQualify
bool AlwaysFullyQualify
Definition: AddUsing.cpp:115
clang::clangd::SymbolKind::Namespace
@ Namespace
Out
CompiledFragmentImpl & Out
Definition: ConfigCompile.cpp:100
Loc
SourceLocation Loc
Definition: AddUsing.cpp:108
REGISTER_TWEAK
#define REGISTER_TWEAK(Subclass)
Definition: Tweak.h:132
AST.h