clang-tools  16.0.0git
Rename.cpp
Go to the documentation of this file.
1 //===--- Rename.cpp - Symbol-rename refactorings -----------------*- 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 "refactor/Rename.h"
10 #include "AST.h"
11 #include "FindTarget.h"
12 #include "ParsedAST.h"
13 #include "Selection.h"
14 #include "SourceCode.h"
15 #include "index/SymbolCollector.h"
16 #include "support/Logger.h"
17 #include "support/Trace.h"
18 #include "clang/AST/ASTContext.h"
19 #include "clang/AST/ASTTypeTraits.h"
20 #include "clang/AST/Decl.h"
21 #include "clang/AST/DeclCXX.h"
22 #include "clang/AST/DeclTemplate.h"
23 #include "clang/AST/ParentMapContext.h"
24 #include "clang/AST/Stmt.h"
25 #include "clang/Basic/CharInfo.h"
26 #include "clang/Basic/LLVM.h"
27 #include "clang/Basic/SourceLocation.h"
28 #include "clang/Tooling/Syntax/Tokens.h"
29 #include "llvm/ADT/None.h"
30 #include "llvm/ADT/STLExtras.h"
31 #include "llvm/ADT/StringExtras.h"
32 #include "llvm/Support/Casting.h"
33 #include "llvm/Support/Error.h"
34 #include "llvm/Support/FormatVariadic.h"
35 #include "llvm/Support/JSON.h"
36 #include <algorithm>
37 
38 namespace clang {
39 namespace clangd {
40 namespace {
41 
42 llvm::Optional<std::string> filePath(const SymbolLocation &Loc,
43  llvm::StringRef HintFilePath) {
44  if (!Loc)
45  return std::nullopt;
46  auto Path = URI::resolve(Loc.FileURI, HintFilePath);
47  if (!Path) {
48  elog("Could not resolve URI {0}: {1}", Loc.FileURI, Path.takeError());
49  return std::nullopt;
50  }
51 
52  return *Path;
53 }
54 
55 // Returns true if the given location is expanded from any macro body.
56 bool isInMacroBody(const SourceManager &SM, SourceLocation Loc) {
57  while (Loc.isMacroID()) {
58  if (SM.isMacroBodyExpansion(Loc))
59  return true;
60  Loc = SM.getImmediateMacroCallerLoc(Loc);
61  }
62 
63  return false;
64 }
65 
66 // Canonical declarations help simplify the process of renaming. Examples:
67 // - Template's canonical decl is the templated declaration (i.e.
68 // ClassTemplateDecl is canonicalized to its child CXXRecordDecl,
69 // FunctionTemplateDecl - to child FunctionDecl)
70 // - Given a constructor/destructor, canonical declaration is the parent
71 // CXXRecordDecl because we want to rename both type name and its ctor/dtor.
72 // - All specializations are canonicalized to the primary template. For example:
73 //
74 // template <typename T, int U>
75 // bool Foo = true; (1)
76 //
77 // template <typename T>
78 // bool Foo<T, 0> = true; (2)
79 //
80 // template <>
81 // bool Foo<int, 0> = true; (3)
82 //
83 // Here, both partial (2) and full (3) specializations are canonicalized to (1)
84 // which ensures all three of them are renamed.
85 const NamedDecl *canonicalRenameDecl(const NamedDecl *D) {
86  if (const auto *VarTemplate = dyn_cast<VarTemplateSpecializationDecl>(D))
87  return canonicalRenameDecl(
88  VarTemplate->getSpecializedTemplate()->getTemplatedDecl());
89  if (const auto *Template = dyn_cast<TemplateDecl>(D))
90  if (const NamedDecl *TemplatedDecl = Template->getTemplatedDecl())
91  return canonicalRenameDecl(TemplatedDecl);
92  if (const auto *ClassTemplateSpecialization =
93  dyn_cast<ClassTemplateSpecializationDecl>(D))
94  return canonicalRenameDecl(
95  ClassTemplateSpecialization->getSpecializedTemplate()
96  ->getTemplatedDecl());
97  if (const auto *Method = dyn_cast<CXXMethodDecl>(D)) {
98  if (Method->getDeclKind() == Decl::Kind::CXXConstructor ||
99  Method->getDeclKind() == Decl::Kind::CXXDestructor)
100  return canonicalRenameDecl(Method->getParent());
101  if (const FunctionDecl *InstantiatedMethod =
102  Method->getInstantiatedFromMemberFunction())
103  return canonicalRenameDecl(InstantiatedMethod);
104  // FIXME(kirillbobyrev): For virtual methods with
105  // size_overridden_methods() > 1, this will not rename all functions it
106  // overrides, because this code assumes there is a single canonical
107  // declaration.
108  if (Method->isVirtual() && Method->size_overridden_methods())
109  return canonicalRenameDecl(*Method->overridden_methods().begin());
110  }
111  if (const auto *Function = dyn_cast<FunctionDecl>(D))
112  if (const FunctionTemplateDecl *Template = Function->getPrimaryTemplate())
113  return canonicalRenameDecl(Template);
114  if (const auto *Field = dyn_cast<FieldDecl>(D)) {
115  // This is a hacky way to do something like
116  // CXXMethodDecl::getInstantiatedFromMemberFunction for the field because
117  // Clang AST does not store relevant information about the field that is
118  // instantiated.
119  const auto *FieldParent =
120  dyn_cast_or_null<CXXRecordDecl>(Field->getParent());
121  if (!FieldParent)
122  return Field->getCanonicalDecl();
123  FieldParent = FieldParent->getTemplateInstantiationPattern();
124  // Field is not instantiation.
125  if (!FieldParent || Field->getParent() == FieldParent)
126  return Field->getCanonicalDecl();
127  for (const FieldDecl *Candidate : FieldParent->fields())
128  if (Field->getDeclName() == Candidate->getDeclName())
129  return Candidate->getCanonicalDecl();
130  elog("FieldParent should have field with the same name as Field.");
131  }
132  if (const auto *VD = dyn_cast<VarDecl>(D)) {
133  if (const VarDecl *OriginalVD = VD->getInstantiatedFromStaticDataMember())
134  return canonicalRenameDecl(OriginalVD);
135  }
136  if (const auto *UD = dyn_cast<UsingShadowDecl>(D)) {
137  if (const auto *TargetDecl = UD->getTargetDecl())
138  return canonicalRenameDecl(TargetDecl);
139  }
140  return dyn_cast<NamedDecl>(D->getCanonicalDecl());
141 }
142 
143 llvm::DenseSet<const NamedDecl *> locateDeclAt(ParsedAST &AST,
144  SourceLocation TokenStartLoc) {
145  unsigned Offset =
146  AST.getSourceManager().getDecomposedSpellingLoc(TokenStartLoc).second;
147 
148  SelectionTree Selection = SelectionTree::createRight(
149  AST.getASTContext(), AST.getTokens(), Offset, Offset);
150  const SelectionTree::Node *SelectedNode = Selection.commonAncestor();
151  if (!SelectedNode)
152  return {};
153 
154  llvm::DenseSet<const NamedDecl *> Result;
155  for (const NamedDecl *D :
156  targetDecl(SelectedNode->ASTNode,
158  AST.getHeuristicResolver())) {
159  Result.insert(canonicalRenameDecl(D));
160  }
161  return Result;
162 }
163 
164 void filterRenameTargets(llvm::DenseSet<const NamedDecl *> &Decls) {
165  // For something like
166  // namespace ns { void foo(); }
167  // void bar() { using ns::f^oo; foo(); }
168  // locateDeclAt() will return a UsingDecl and foo's actual declaration.
169  // For renaming, we're only interested in foo's declaration, so drop the other
170  // one. There should never be more than one UsingDecl here, otherwise the
171  // rename would be ambiguos anyway.
172  auto UD = std::find_if(Decls.begin(), Decls.end(), [](const NamedDecl *D) {
173  return llvm::isa<UsingDecl>(D);
174  });
175  if (UD != Decls.end()) {
176  Decls.erase(UD);
177  }
178 }
179 
180 // By default, we exclude symbols from system headers and protobuf symbols as
181 // renaming these symbols would change system/generated files which are unlikely
182 // to be good candidates for modification.
183 bool isExcluded(const NamedDecl &RenameDecl) {
184  const auto &SM = RenameDecl.getASTContext().getSourceManager();
185  return SM.isInSystemHeader(RenameDecl.getLocation()) ||
186  isProtoFile(RenameDecl.getLocation(), SM);
187 }
188 
189 enum class ReasonToReject {
190  NoSymbolFound,
191  NoIndexProvided,
192  NonIndexable,
193  UnsupportedSymbol,
194  AmbiguousSymbol,
195 
196  // name validation. FIXME: reconcile with InvalidName
197  SameName,
198 };
199 
200 llvm::Optional<ReasonToReject> renameable(const NamedDecl &RenameDecl,
201  StringRef MainFilePath,
202  const SymbolIndex *Index,
203  const RenameOptions& Opts) {
204  trace::Span Tracer("Renameable");
205  if (!Opts.RenameVirtual) {
206  if (const auto *S = llvm::dyn_cast<CXXMethodDecl>(&RenameDecl)) {
207  if (S->isVirtual())
208  return ReasonToReject::UnsupportedSymbol;
209  }
210  }
211  // Filter out symbols that are unsupported in both rename modes.
212  if (llvm::isa<NamespaceDecl>(&RenameDecl))
213  return ReasonToReject::UnsupportedSymbol;
214  if (const auto *FD = llvm::dyn_cast<FunctionDecl>(&RenameDecl)) {
215  if (FD->isOverloadedOperator())
216  return ReasonToReject::UnsupportedSymbol;
217  }
218  // function-local symbols is safe to rename.
219  if (RenameDecl.getParentFunctionOrMethod())
220  return std::nullopt;
221 
222  if (isExcluded(RenameDecl))
223  return ReasonToReject::UnsupportedSymbol;
224 
225  // Check whether the symbol being rename is indexable.
226  auto &ASTCtx = RenameDecl.getASTContext();
227  bool MainFileIsHeader = isHeaderFile(MainFilePath, ASTCtx.getLangOpts());
228  bool DeclaredInMainFile =
229  isInsideMainFile(RenameDecl.getBeginLoc(), ASTCtx.getSourceManager());
230  bool IsMainFileOnly = true;
231  if (MainFileIsHeader)
232  // main file is a header, the symbol can't be main file only.
233  IsMainFileOnly = false;
234  else if (!DeclaredInMainFile)
235  IsMainFileOnly = false;
236  // If the symbol is not indexable, we disallow rename.
238  RenameDecl, RenameDecl.getASTContext(), SymbolCollector::Options(),
239  IsMainFileOnly))
240  return ReasonToReject::NonIndexable;
241 
242  return std::nullopt;
243 }
244 
245 llvm::Error makeError(ReasonToReject Reason) {
246  auto Message = [](ReasonToReject Reason) {
247  switch (Reason) {
248  case ReasonToReject::NoSymbolFound:
249  return "there is no symbol at the given location";
250  case ReasonToReject::NoIndexProvided:
251  return "no index provided";
252  case ReasonToReject::NonIndexable:
253  return "symbol may be used in other files (not eligible for indexing)";
254  case ReasonToReject::UnsupportedSymbol:
255  return "symbol is not a supported kind (e.g. namespace, macro)";
256  case ReasonToReject::AmbiguousSymbol:
257  return "there are multiple symbols at the given location";
258  case ReasonToReject::SameName:
259  return "new name is the same as the old name";
260  }
261  llvm_unreachable("unhandled reason kind");
262  };
263  return error("Cannot rename symbol: {0}", Message(Reason));
264 }
265 
266 // Return all rename occurrences in the main file.
267 std::vector<SourceLocation> findOccurrencesWithinFile(ParsedAST &AST,
268  const NamedDecl &ND) {
269  trace::Span Tracer("FindOccurrencesWithinFile");
270  assert(canonicalRenameDecl(&ND) == &ND &&
271  "ND should be already canonicalized.");
272 
273  std::vector<SourceLocation> Results;
274  for (Decl *TopLevelDecl : AST.getLocalTopLevelDecls()) {
276  TopLevelDecl,
277  [&](ReferenceLoc Ref) {
278  if (Ref.Targets.empty())
279  return;
280  for (const auto *Target : Ref.Targets) {
281  if (canonicalRenameDecl(Target) == &ND) {
282  Results.push_back(Ref.NameLoc);
283  return;
284  }
285  }
286  },
287  AST.getHeuristicResolver());
288  }
289 
290  return Results;
291 }
292 
293 // Detect name conflict with othter DeclStmts in the same enclosing scope.
294 const NamedDecl *lookupSiblingWithinEnclosingScope(ASTContext &Ctx,
295  const NamedDecl &RenamedDecl,
296  StringRef NewName) {
297  // Store Parents list outside of GetSingleParent, so that returned pointer is
298  // not invalidated.
299  DynTypedNodeList Storage(DynTypedNode::create(RenamedDecl));
300  auto GetSingleParent = [&](const DynTypedNode &Node) -> const DynTypedNode * {
301  Storage = Ctx.getParents(Node);
302  return (Storage.size() == 1) ? Storage.begin() : nullptr;
303  };
304 
305  // We need to get to the enclosing scope: NamedDecl's parent is typically
306  // DeclStmt (or FunctionProtoTypeLoc in case of function arguments), so
307  // enclosing scope would be the second order parent.
308  const auto *Parent = GetSingleParent(DynTypedNode::create(RenamedDecl));
309  if (!Parent || !(Parent->get<DeclStmt>() || Parent->get<TypeLoc>()))
310  return nullptr;
311  Parent = GetSingleParent(*Parent);
312 
313  // The following helpers check corresponding AST nodes for variable
314  // declarations with the name collision.
315  auto CheckDeclStmt = [&](const DeclStmt *DS,
316  StringRef Name) -> const NamedDecl * {
317  if (!DS)
318  return nullptr;
319  for (const auto &Child : DS->getDeclGroup())
320  if (const auto *ND = dyn_cast<NamedDecl>(Child))
321  if (ND != &RenamedDecl && ND->getName() == Name)
322  return ND;
323  return nullptr;
324  };
325  auto CheckCompoundStmt = [&](const Stmt *S,
326  StringRef Name) -> const NamedDecl * {
327  if (const auto *CS = dyn_cast_or_null<CompoundStmt>(S))
328  for (const auto *Node : CS->children())
329  if (const auto *Result = CheckDeclStmt(dyn_cast<DeclStmt>(Node), Name))
330  return Result;
331  return nullptr;
332  };
333  auto CheckConditionVariable = [&](const auto *Scope,
334  StringRef Name) -> const NamedDecl * {
335  if (!Scope)
336  return nullptr;
337  return CheckDeclStmt(Scope->getConditionVariableDeclStmt(), Name);
338  };
339 
340  // CompoundStmt is the most common enclosing scope for function-local symbols
341  // In the simplest case we just iterate through sibling DeclStmts and check
342  // for collisions.
343  if (const auto *EnclosingCS = Parent->get<CompoundStmt>()) {
344  if (const auto *Result = CheckCompoundStmt(EnclosingCS, NewName))
345  return Result;
346  const auto *ScopeParent = GetSingleParent(*Parent);
347  // CompoundStmt may be found within if/while/for. In these cases, rename can
348  // collide with the init-statement variable decalaration, they should be
349  // checked.
350  if (const auto *Result =
351  CheckConditionVariable(ScopeParent->get<IfStmt>(), NewName))
352  return Result;
353  if (const auto *Result =
354  CheckConditionVariable(ScopeParent->get<WhileStmt>(), NewName))
355  return Result;
356  if (const auto *For = ScopeParent->get<ForStmt>())
357  if (const auto *Result = CheckDeclStmt(
358  dyn_cast_or_null<DeclStmt>(For->getInit()), NewName))
359  return Result;
360  // Also check if there is a name collision with function arguments.
361  if (const auto *Function = ScopeParent->get<FunctionDecl>())
362  for (const auto *Parameter : Function->parameters())
363  if (Parameter->getName() == NewName)
364  return Parameter;
365  return nullptr;
366  }
367 
368  // When renaming a variable within init-statement within if/while/for
369  // condition, also check the CompoundStmt in the body.
370  if (const auto *EnclosingIf = Parent->get<IfStmt>()) {
371  if (const auto *Result = CheckCompoundStmt(EnclosingIf->getElse(), NewName))
372  return Result;
373  return CheckCompoundStmt(EnclosingIf->getThen(), NewName);
374  }
375  if (const auto *EnclosingWhile = Parent->get<WhileStmt>())
376  return CheckCompoundStmt(EnclosingWhile->getBody(), NewName);
377  if (const auto *EnclosingFor = Parent->get<ForStmt>()) {
378  // Check for conflicts with other declarations within initialization
379  // statement.
380  if (const auto *Result = CheckDeclStmt(
381  dyn_cast_or_null<DeclStmt>(EnclosingFor->getInit()), NewName))
382  return Result;
383  return CheckCompoundStmt(EnclosingFor->getBody(), NewName);
384  }
385  if (const auto *EnclosingFunction = Parent->get<FunctionDecl>()) {
386  // Check for conflicts with other arguments.
387  for (const auto *Parameter : EnclosingFunction->parameters())
388  if (Parameter != &RenamedDecl && Parameter->getName() == NewName)
389  return Parameter;
390  // FIXME: We don't modify all references to function parameters when
391  // renaming from forward declaration now, so using a name colliding with
392  // something in the definition's body is a valid transformation.
393  if (!EnclosingFunction->doesThisDeclarationHaveABody())
394  return nullptr;
395  return CheckCompoundStmt(EnclosingFunction->getBody(), NewName);
396  }
397 
398  return nullptr;
399 }
400 
401 // Lookup the declarations (if any) with the given Name in the context of
402 // RenameDecl.
403 const NamedDecl *lookupSiblingsWithinContext(ASTContext &Ctx,
404  const NamedDecl &RenamedDecl,
405  llvm::StringRef NewName) {
406  const auto &II = Ctx.Idents.get(NewName);
407  DeclarationName LookupName(&II);
408  DeclContextLookupResult LookupResult;
409  const auto *DC = RenamedDecl.getDeclContext();
410  while (DC->isTransparentContext())
411  DC = DC->getParent();
412  switch (DC->getDeclKind()) {
413  // The enclosing DeclContext may not be the enclosing scope, it might have
414  // false positives and negatives, so we only choose "confident" DeclContexts
415  // that don't have any subscopes that are neither DeclContexts nor
416  // transparent.
417  //
418  // Notably, FunctionDecl is excluded -- because local variables are not scoped
419  // to the function, but rather to the CompoundStmt that is its body. Lookup
420  // will not find function-local variables.
421  case Decl::TranslationUnit:
422  case Decl::Namespace:
423  case Decl::Record:
424  case Decl::Enum:
425  case Decl::CXXRecord:
426  LookupResult = DC->lookup(LookupName);
427  break;
428  default:
429  break;
430  }
431  // Lookup may contain the RenameDecl itself, exclude it.
432  for (const auto *D : LookupResult)
433  if (D->getCanonicalDecl() != RenamedDecl.getCanonicalDecl())
434  return D;
435  return nullptr;
436 }
437 
438 const NamedDecl *lookupSiblingWithName(ASTContext &Ctx,
439  const NamedDecl &RenamedDecl,
440  llvm::StringRef NewName) {
441  trace::Span Tracer("LookupSiblingWithName");
442  if (const auto *Result =
443  lookupSiblingsWithinContext(Ctx, RenamedDecl, NewName))
444  return Result;
445  return lookupSiblingWithinEnclosingScope(Ctx, RenamedDecl, NewName);
446 }
447 
448 struct InvalidName {
449  enum Kind {
450  Keywords,
451  Conflict,
452  BadIdentifier,
453  };
455  std::string Details;
456 };
457 std::string toString(InvalidName::Kind K) {
458  switch (K) {
459  case InvalidName::Keywords:
460  return "Keywords";
461  case InvalidName::Conflict:
462  return "Conflict";
463  case InvalidName::BadIdentifier:
464  return "BadIdentifier";
465  }
466  llvm_unreachable("unhandled InvalidName kind");
467 }
468 
469 llvm::Error makeError(InvalidName Reason) {
470  auto Message = [](const InvalidName &Reason) {
471  switch (Reason.K) {
472  case InvalidName::Keywords:
473  return llvm::formatv("the chosen name \"{0}\" is a keyword",
474  Reason.Details);
475  case InvalidName::Conflict:
476  return llvm::formatv("conflict with the symbol in {0}", Reason.Details);
477  case InvalidName::BadIdentifier:
478  return llvm::formatv("the chosen name \"{0}\" is not a valid identifier",
479  Reason.Details);
480  }
481  llvm_unreachable("unhandled InvalidName kind");
482  };
483  return error("invalid name: {0}", Message(Reason));
484 }
485 
486 static bool mayBeValidIdentifier(llvm::StringRef Ident) {
487  assert(llvm::json::isUTF8(Ident));
488  if (Ident.empty())
489  return false;
490  // We don't check all the rules for non-ascii characters (most are allowed).
491  bool AllowDollar = true; // lenient
492  if (llvm::isASCII(Ident.front()) &&
493  !isAsciiIdentifierStart(Ident.front(), AllowDollar))
494  return false;
495  for (char C : Ident) {
496  if (llvm::isASCII(C) && !isAsciiIdentifierContinue(C, AllowDollar))
497  return false;
498  }
499  return true;
500 }
501 
502 // Check if we can rename the given RenameDecl into NewName.
503 // Return details if the rename would produce a conflict.
504 llvm::Optional<InvalidName> checkName(const NamedDecl &RenameDecl,
505  llvm::StringRef NewName) {
506  trace::Span Tracer("CheckName");
507  static constexpr trace::Metric InvalidNameMetric(
508  "rename_name_invalid", trace::Metric::Counter, "invalid_kind");
509  auto &ASTCtx = RenameDecl.getASTContext();
510  llvm::Optional<InvalidName> Result;
511  if (isKeyword(NewName, ASTCtx.getLangOpts()))
512  Result = InvalidName{InvalidName::Keywords, NewName.str()};
513  else if (!mayBeValidIdentifier(NewName))
514  Result = InvalidName{InvalidName::BadIdentifier, NewName.str()};
515  else {
516  // Name conflict detection.
517  // Function conflicts are subtle (overloading), so ignore them.
518  if (RenameDecl.getKind() != Decl::Function) {
519  if (auto *Conflict = lookupSiblingWithName(ASTCtx, RenameDecl, NewName))
520  Result = InvalidName{
521  InvalidName::Conflict,
522  Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
523  }
524  }
525  if (Result)
526  InvalidNameMetric.record(1, toString(Result->K));
527  return Result;
528 }
529 
530 // AST-based rename, it renames all occurrences in the main file.
531 llvm::Expected<tooling::Replacements>
532 renameWithinFile(ParsedAST &AST, const NamedDecl &RenameDecl,
533  llvm::StringRef NewName) {
534  trace::Span Tracer("RenameWithinFile");
535  const SourceManager &SM = AST.getSourceManager();
536 
537  tooling::Replacements FilteredChanges;
538  for (SourceLocation Loc : findOccurrencesWithinFile(AST, RenameDecl)) {
539  SourceLocation RenameLoc = Loc;
540  // We don't rename in any macro bodies, but we allow rename the symbol
541  // spelled in a top-level macro argument in the main file.
542  if (RenameLoc.isMacroID()) {
543  if (isInMacroBody(SM, RenameLoc))
544  continue;
545  RenameLoc = SM.getSpellingLoc(Loc);
546  }
547  // Filter out locations not from main file.
548  // We traverse only main file decls, but locations could come from an
549  // non-preamble #include file e.g.
550  // void test() {
551  // int f^oo;
552  // #include "use_foo.inc"
553  // }
554  if (!isInsideMainFile(RenameLoc, SM))
555  continue;
556  if (auto Err = FilteredChanges.add(tooling::Replacement(
557  SM, CharSourceRange::getTokenRange(RenameLoc), NewName)))
558  return std::move(Err);
559  }
560  return FilteredChanges;
561 }
562 
563 Range toRange(const SymbolLocation &L) {
564  Range R;
565  R.start.line = L.Start.line();
566  R.start.character = L.Start.column();
567  R.end.line = L.End.line();
568  R.end.character = L.End.column();
569  return R;
570 }
571 
572 // Walk down from a virtual method to overriding methods, we rename them as a
573 // group. Note that canonicalRenameDecl() ensures we're starting from the base
574 // method.
575 void insertTransitiveOverrides(SymbolID Base, llvm::DenseSet<SymbolID> &IDs,
576  const SymbolIndex &Index) {
577  RelationsRequest Req;
578  Req.Predicate = RelationKind::OverriddenBy;
579 
580  llvm::DenseSet<SymbolID> Pending = {Base};
581  while (!Pending.empty()) {
582  Req.Subjects = std::move(Pending);
583  Pending.clear();
584 
585  Index.relations(Req, [&](const SymbolID &, const Symbol &Override) {
586  if (IDs.insert(Override.ID).second)
587  Pending.insert(Override.ID);
588  });
589  }
590 }
591 
592 // Return all rename occurrences (using the index) outside of the main file,
593 // grouped by the absolute file path.
594 llvm::Expected<llvm::StringMap<std::vector<Range>>>
595 findOccurrencesOutsideFile(const NamedDecl &RenameDecl,
596  llvm::StringRef MainFile, const SymbolIndex &Index,
597  size_t MaxLimitFiles) {
598  trace::Span Tracer("FindOccurrencesOutsideFile");
599  RefsRequest RQuest;
600  RQuest.IDs.insert(getSymbolID(&RenameDecl));
601 
602  if (const auto *MethodDecl = llvm::dyn_cast<CXXMethodDecl>(&RenameDecl))
603  if (MethodDecl->isVirtual())
604  insertTransitiveOverrides(*RQuest.IDs.begin(), RQuest.IDs, Index);
605 
606  // Absolute file path => rename occurrences in that file.
607  llvm::StringMap<std::vector<Range>> AffectedFiles;
608  bool HasMore = Index.refs(RQuest, [&](const Ref &R) {
609  if (AffectedFiles.size() >= MaxLimitFiles)
610  return;
611  if ((R.Kind & RefKind::Spelled) == RefKind::Unknown)
612  return;
613  if (auto RefFilePath = filePath(R.Location, /*HintFilePath=*/MainFile)) {
614  if (!pathEqual(*RefFilePath, MainFile))
615  AffectedFiles[*RefFilePath].push_back(toRange(R.Location));
616  }
617  });
618 
619  if (AffectedFiles.size() >= MaxLimitFiles)
620  return error("The number of affected files exceeds the max limit {0}",
621  MaxLimitFiles);
622  if (HasMore)
623  return error("The symbol {0} has too many occurrences",
624  RenameDecl.getQualifiedNameAsString());
625  // Sort and deduplicate the results, in case that index returns duplications.
626  for (auto &FileAndOccurrences : AffectedFiles) {
627  auto &Ranges = FileAndOccurrences.getValue();
628  llvm::sort(Ranges);
629  Ranges.erase(std::unique(Ranges.begin(), Ranges.end()), Ranges.end());
630 
631  SPAN_ATTACH(Tracer, FileAndOccurrences.first(),
632  static_cast<int64_t>(Ranges.size()));
633  }
634  return AffectedFiles;
635 }
636 
637 // Index-based rename, it renames all occurrences outside of the main file.
638 //
639 // The cross-file rename is purely based on the index, as we don't want to
640 // build all ASTs for affected files, which may cause a performance hit.
641 // We choose to trade off some correctness for performance and scalability.
642 //
643 // Clangd builds a dynamic index for all opened files on top of the static
644 // index of the whole codebase. Dynamic index is up-to-date (respects dirty
645 // buffers) as long as clangd finishes processing opened files, while static
646 // index (background index) is relatively stale. We choose the dirty buffers
647 // as the file content we rename on, and fallback to file content on disk if
648 // there is no dirty buffer.
649 llvm::Expected<FileEdits>
650 renameOutsideFile(const NamedDecl &RenameDecl, llvm::StringRef MainFilePath,
651  llvm::StringRef NewName, const SymbolIndex &Index,
652  size_t MaxLimitFiles, llvm::vfs::FileSystem &FS) {
653  trace::Span Tracer("RenameOutsideFile");
654  auto AffectedFiles = findOccurrencesOutsideFile(RenameDecl, MainFilePath,
655  Index, MaxLimitFiles);
656  if (!AffectedFiles)
657  return AffectedFiles.takeError();
659  for (auto &FileAndOccurrences : *AffectedFiles) {
660  llvm::StringRef FilePath = FileAndOccurrences.first();
661 
662  auto ExpBuffer = FS.getBufferForFile(FilePath);
663  if (!ExpBuffer) {
664  elog("Fail to read file content: Fail to open file {0}: {1}", FilePath,
665  ExpBuffer.getError().message());
666  continue;
667  }
668 
669  auto AffectedFileCode = (*ExpBuffer)->getBuffer();
670  auto RenameRanges =
671  adjustRenameRanges(AffectedFileCode, RenameDecl.getNameAsString(),
672  std::move(FileAndOccurrences.second),
673  RenameDecl.getASTContext().getLangOpts());
674  if (!RenameRanges) {
675  // Our heuristics fails to adjust rename ranges to the current state of
676  // the file, it is most likely the index is stale, so we give up the
677  // entire rename.
678  return error("Index results don't match the content of file {0} "
679  "(the index may be stale)",
680  FilePath);
681  }
682  auto RenameEdit =
683  buildRenameEdit(FilePath, AffectedFileCode, *RenameRanges, NewName);
684  if (!RenameEdit)
685  return error("failed to rename in file {0}: {1}", FilePath,
686  RenameEdit.takeError());
687  if (!RenameEdit->Replacements.empty())
688  Results.insert({FilePath, std::move(*RenameEdit)});
689  }
690  return Results;
691 }
692 
693 // A simple edit is either changing line or column, but not both.
694 bool impliesSimpleEdit(const Position &LHS, const Position &RHS) {
695  return LHS.line == RHS.line || LHS.character == RHS.character;
696 }
697 
698 // Performs a DFS to enumerate all possible near-miss matches.
699 // It finds the locations where the indexed occurrences are now spelled in
700 // Lexed occurrences, a near miss is defined as:
701 // - a near miss maps all of the **name** occurrences from the index onto a
702 // *subset* of lexed occurrences (we allow a single name refers to more
703 // than one symbol)
704 // - all indexed occurrences must be mapped, and Result must be distinct and
705 // preserve order (only support detecting simple edits to ensure a
706 // robust mapping)
707 // - each indexed -> lexed occurrences mapping correspondence may change the
708 // *line* or *column*, but not both (increases chance of a robust mapping)
709 void findNearMiss(
710  std::vector<size_t> &PartialMatch, ArrayRef<Range> IndexedRest,
711  ArrayRef<Range> LexedRest, int LexedIndex, int &Fuel,
712  llvm::function_ref<void(const std::vector<size_t> &)> MatchedCB) {
713  if (--Fuel < 0)
714  return;
715  if (IndexedRest.size() > LexedRest.size())
716  return;
717  if (IndexedRest.empty()) {
718  MatchedCB(PartialMatch);
719  return;
720  }
721  if (impliesSimpleEdit(IndexedRest.front().start, LexedRest.front().start)) {
722  PartialMatch.push_back(LexedIndex);
723  findNearMiss(PartialMatch, IndexedRest.drop_front(), LexedRest.drop_front(),
724  LexedIndex + 1, Fuel, MatchedCB);
725  PartialMatch.pop_back();
726  }
727  findNearMiss(PartialMatch, IndexedRest, LexedRest.drop_front(),
728  LexedIndex + 1, Fuel, MatchedCB);
729 }
730 
731 } // namespace
732 
733 llvm::Expected<RenameResult> rename(const RenameInputs &RInputs) {
734  assert(!RInputs.Index == !RInputs.FS &&
735  "Index and FS must either both be specified or both null.");
736  trace::Span Tracer("Rename flow");
737  const auto &Opts = RInputs.Opts;
738  ParsedAST &AST = RInputs.AST;
739  const SourceManager &SM = AST.getSourceManager();
740  llvm::StringRef MainFileCode = SM.getBufferData(SM.getMainFileID());
741  // Try to find the tokens adjacent to the cursor position.
742  auto Loc = sourceLocationInMainFile(SM, RInputs.Pos);
743  if (!Loc)
744  return Loc.takeError();
745  const syntax::Token *IdentifierToken =
746  spelledIdentifierTouching(*Loc, AST.getTokens());
747 
748  // Renames should only triggered on identifiers.
749  if (!IdentifierToken)
750  return makeError(ReasonToReject::NoSymbolFound);
751  Range CurrentIdentifier = halfOpenToRange(
752  SM, CharSourceRange::getCharRange(IdentifierToken->location(),
753  IdentifierToken->endLocation()));
754  // FIXME: Renaming macros is not supported yet, the macro-handling code should
755  // be moved to rename tooling library.
756  if (locateMacroAt(*IdentifierToken, AST.getPreprocessor()))
757  return makeError(ReasonToReject::UnsupportedSymbol);
758 
759  auto DeclsUnderCursor = locateDeclAt(AST, IdentifierToken->location());
760  filterRenameTargets(DeclsUnderCursor);
761  if (DeclsUnderCursor.empty())
762  return makeError(ReasonToReject::NoSymbolFound);
763  if (DeclsUnderCursor.size() > 1)
764  return makeError(ReasonToReject::AmbiguousSymbol);
765  const auto &RenameDecl = **DeclsUnderCursor.begin();
766  const auto *ID = RenameDecl.getIdentifier();
767  if (!ID)
768  return makeError(ReasonToReject::UnsupportedSymbol);
769  if (ID->getName() == RInputs.NewName)
770  return makeError(ReasonToReject::SameName);
771  auto Invalid = checkName(RenameDecl, RInputs.NewName);
772  if (Invalid)
773  return makeError(std::move(*Invalid));
774 
775  auto Reject =
776  renameable(RenameDecl, RInputs.MainFilePath, RInputs.Index, Opts);
777  if (Reject)
778  return makeError(*Reject);
779 
780  // We have two implementations of the rename:
781  // - AST-based rename: used for renaming local symbols, e.g. variables
782  // defined in a function body;
783  // - index-based rename: used for renaming non-local symbols, and not
784  // feasible for local symbols (as by design our index don't index these
785  // symbols by design;
786  // To make cross-file rename work for local symbol, we use a hybrid solution:
787  // - run AST-based rename on the main file;
788  // - run index-based rename on other affected files;
789  auto MainFileRenameEdit = renameWithinFile(AST, RenameDecl, RInputs.NewName);
790  if (!MainFileRenameEdit)
791  return MainFileRenameEdit.takeError();
792 
793  // Check the rename-triggering location is actually being renamed.
794  // This is a robustness check to avoid surprising rename results -- if the
795  // the triggering location is not actually the name of the node we identified
796  // (e.g. for broken code), then rename is likely not what users expect, so we
797  // reject this kind of rename.
798  auto StartOffset = positionToOffset(MainFileCode, CurrentIdentifier.start);
799  auto EndOffset = positionToOffset(MainFileCode, CurrentIdentifier.end);
800  if (!StartOffset)
801  return StartOffset.takeError();
802  if (!EndOffset)
803  return EndOffset.takeError();
804  if (llvm::none_of(
805  *MainFileRenameEdit,
806  [&StartOffset, &EndOffset](const clang::tooling::Replacement &R) {
807  return R.getOffset() == *StartOffset &&
808  R.getLength() == *EndOffset - *StartOffset;
809  })) {
810  return makeError(ReasonToReject::NoSymbolFound);
811  }
812  RenameResult Result;
813  Result.Target = CurrentIdentifier;
814  Edit MainFileEdits = Edit(MainFileCode, std::move(*MainFileRenameEdit));
815  for (const TextEdit &TE : MainFileEdits.asTextEdits())
816  Result.LocalChanges.push_back(TE.range);
817 
818  // return the main file edit if this is a within-file rename or the symbol
819  // being renamed is function local.
820  if (RenameDecl.getParentFunctionOrMethod()) {
821  Result.GlobalChanges = FileEdits(
822  {std::make_pair(RInputs.MainFilePath, std::move(MainFileEdits))});
823  return Result;
824  }
825 
826  // If the index is nullptr, we don't know the completeness of the result, so
827  // we don't populate the field GlobalChanges.
828  if (!RInputs.Index) {
829  assert(Result.GlobalChanges.empty());
830  return Result;
831  }
832 
833  auto OtherFilesEdits = renameOutsideFile(
834  RenameDecl, RInputs.MainFilePath, RInputs.NewName, *RInputs.Index,
835  Opts.LimitFiles == 0 ? std::numeric_limits<size_t>::max()
836  : Opts.LimitFiles,
837  *RInputs.FS);
838  if (!OtherFilesEdits)
839  return OtherFilesEdits.takeError();
840  Result.GlobalChanges = *OtherFilesEdits;
841  // Attach the rename edits for the main file.
842  Result.GlobalChanges.try_emplace(RInputs.MainFilePath,
843  std::move(MainFileEdits));
844  return Result;
845 }
846 
847 llvm::Expected<Edit> buildRenameEdit(llvm::StringRef AbsFilePath,
848  llvm::StringRef InitialCode,
849  std::vector<Range> Occurrences,
850  llvm::StringRef NewName) {
851  trace::Span Tracer("BuildRenameEdit");
852  SPAN_ATTACH(Tracer, "file_path", AbsFilePath);
853  SPAN_ATTACH(Tracer, "rename_occurrences",
854  static_cast<int64_t>(Occurrences.size()));
855 
856  assert(llvm::is_sorted(Occurrences));
857  assert(std::unique(Occurrences.begin(), Occurrences.end()) ==
858  Occurrences.end() &&
859  "Occurrences must be unique");
860 
861  // These two always correspond to the same position.
862  Position LastPos{0, 0};
863  size_t LastOffset = 0;
864 
865  auto Offset = [&](const Position &P) -> llvm::Expected<size_t> {
866  assert(LastPos <= P && "malformed input");
867  Position Shifted = {
868  P.line - LastPos.line,
869  P.line > LastPos.line ? P.character : P.character - LastPos.character};
870  auto ShiftedOffset =
871  positionToOffset(InitialCode.substr(LastOffset), Shifted);
872  if (!ShiftedOffset)
873  return error("fail to convert the position {0} to offset ({1})", P,
874  ShiftedOffset.takeError());
875  LastPos = P;
876  LastOffset += *ShiftedOffset;
877  return LastOffset;
878  };
879 
880  std::vector<std::pair</*start*/ size_t, /*end*/ size_t>> OccurrencesOffsets;
881  for (const auto &R : Occurrences) {
882  auto StartOffset = Offset(R.start);
883  if (!StartOffset)
884  return StartOffset.takeError();
885  auto EndOffset = Offset(R.end);
886  if (!EndOffset)
887  return EndOffset.takeError();
888  OccurrencesOffsets.push_back({*StartOffset, *EndOffset});
889  }
890 
891  tooling::Replacements RenameEdit;
892  for (const auto &R : OccurrencesOffsets) {
893  auto ByteLength = R.second - R.first;
894  if (auto Err = RenameEdit.add(
895  tooling::Replacement(AbsFilePath, R.first, ByteLength, NewName)))
896  return std::move(Err);
897  }
898  return Edit(InitialCode, std::move(RenameEdit));
899 }
900 
901 // Details:
902 // - lex the draft code to get all rename candidates, this yields a superset
903 // of candidates.
904 // - apply range patching heuristics to generate "authoritative" occurrences,
905 // cases we consider:
906 // (a) index returns a subset of candidates, we use the indexed results.
907 // - fully equal, we are sure the index is up-to-date
908 // - proper subset, index is correct in most cases? there may be false
909 // positives (e.g. candidates got appended), but rename is still safe
910 // (b) index returns non-candidate results, we attempt to map the indexed
911 // ranges onto candidates in a plausible way (e.g. guess that lines
912 // were inserted). If such a "near miss" is found, the rename is still
913 // possible
914 llvm::Optional<std::vector<Range>>
915 adjustRenameRanges(llvm::StringRef DraftCode, llvm::StringRef Identifier,
916  std::vector<Range> Indexed, const LangOptions &LangOpts) {
917  trace::Span Tracer("AdjustRenameRanges");
918  assert(!Indexed.empty());
919  assert(llvm::is_sorted(Indexed));
920  std::vector<Range> Lexed =
922  llvm::sort(Lexed);
923  return getMappedRanges(Indexed, Lexed);
924 }
925 
926 llvm::Optional<std::vector<Range>> getMappedRanges(ArrayRef<Range> Indexed,
927  ArrayRef<Range> Lexed) {
928  trace::Span Tracer("GetMappedRanges");
929  assert(!Indexed.empty());
930  assert(llvm::is_sorted(Indexed));
931  assert(llvm::is_sorted(Lexed));
932 
933  if (Indexed.size() > Lexed.size()) {
934  vlog("The number of lexed occurrences is less than indexed occurrences");
935  SPAN_ATTACH(
936  Tracer, "error",
937  "The number of lexed occurrences is less than indexed occurrences");
938  return std::nullopt;
939  }
940  // Fast check for the special subset case.
941  if (std::includes(Indexed.begin(), Indexed.end(), Lexed.begin(), Lexed.end()))
942  return Indexed.vec();
943 
944  std::vector<size_t> Best;
945  size_t BestCost = std::numeric_limits<size_t>::max();
946  bool HasMultiple = false;
947  std::vector<size_t> ResultStorage;
948  int Fuel = 10000;
949  findNearMiss(ResultStorage, Indexed, Lexed, 0, Fuel,
950  [&](const std::vector<size_t> &Matched) {
951  size_t MCost =
952  renameRangeAdjustmentCost(Indexed, Lexed, Matched);
953  if (MCost < BestCost) {
954  BestCost = MCost;
955  Best = std::move(Matched);
956  HasMultiple = false; // reset
957  return;
958  }
959  if (MCost == BestCost)
960  HasMultiple = true;
961  });
962  if (HasMultiple) {
963  vlog("The best near miss is not unique.");
964  SPAN_ATTACH(Tracer, "error", "The best near miss is not unique");
965  return std::nullopt;
966  }
967  if (Best.empty()) {
968  vlog("Didn't find a near miss.");
969  SPAN_ATTACH(Tracer, "error", "Didn't find a near miss");
970  return std::nullopt;
971  }
972  std::vector<Range> Mapped;
973  for (auto I : Best)
974  Mapped.push_back(Lexed[I]);
975  SPAN_ATTACH(Tracer, "mapped_ranges", static_cast<int64_t>(Mapped.size()));
976  return Mapped;
977 }
978 
979 // The cost is the sum of the implied edit sizes between successive diffs, only
980 // simple edits are considered:
981 // - insert/remove a line (change line offset)
982 // - insert/remove a character on an existing line (change column offset)
983 //
984 // Example I, total result is 1 + 1 = 2.
985 // diff[0]: line + 1 <- insert a line before edit 0.
986 // diff[1]: line + 1
987 // diff[2]: line + 1
988 // diff[3]: line + 2 <- insert a line before edits 2 and 3.
989 //
990 // Example II, total result is 1 + 1 + 1 = 3.
991 // diff[0]: line + 1 <- insert a line before edit 0.
992 // diff[1]: column + 1 <- remove a line between edits 0 and 1, and insert a
993 // character on edit 1.
994 size_t renameRangeAdjustmentCost(ArrayRef<Range> Indexed, ArrayRef<Range> Lexed,
995  ArrayRef<size_t> MappedIndex) {
996  assert(Indexed.size() == MappedIndex.size());
997  assert(llvm::is_sorted(Indexed));
998  assert(llvm::is_sorted(Lexed));
999 
1000  int LastLine = -1;
1001  int LastDLine = 0, LastDColumn = 0;
1002  int Cost = 0;
1003  for (size_t I = 0; I < Indexed.size(); ++I) {
1004  int DLine = Indexed[I].start.line - Lexed[MappedIndex[I]].start.line;
1005  int DColumn =
1006  Indexed[I].start.character - Lexed[MappedIndex[I]].start.character;
1007  int Line = Indexed[I].start.line;
1008  if (Line != LastLine)
1009  LastDColumn = 0; // column offsets don't carry cross lines.
1010  Cost += abs(DLine - LastDLine) + abs(DColumn - LastDColumn);
1011  std::tie(LastLine, LastDLine, LastDColumn) = std::tie(Line, DLine, DColumn);
1012  }
1013  return Cost;
1014 }
1015 
1016 } // namespace clangd
1017 } // namespace clang
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:39
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:45
Base
std::unique_ptr< GlobalCompilationDatabase > Base
Definition: GlobalCompilationDatabaseTests.cpp:85
clang::clangd::isKeyword
bool isKeyword(llvm::StringRef NewName, const LangOptions &LangOpts)
Return true if the TokenName is in the list of reversed keywords of the language.
Definition: SourceCode.cpp:643
clang::clangd::locateMacroAt
llvm::Optional< DefinedMacro > locateMacroAt(const syntax::Token &SpelledTok, Preprocessor &PP)
Gets the macro referenced by SpelledTok.
Definition: SourceCode.cpp:977
Selection.h
clang::clangd::Edit
A set of edits generated for a single file.
Definition: SourceCode.h:184
clang::clangd::rename
llvm::Expected< RenameResult > rename(const RenameInputs &RInputs)
Renames all occurrences of the symbol.
Definition: Rename.cpp:733
clang::clangd::RenameInputs
Definition: Rename.h:35
clang::clangd::RefKind::Unknown
@ Unknown
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
Tracer
std::unique_ptr< trace::EventTracer > Tracer
Definition: TraceTests.cpp:161
clang::clangd::error
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition: Logger.h:79
clang::clangd::SymbolCollector::shouldCollectSymbol
static bool shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx, const Options &Opts, bool IsMainFileSymbol)
Returns true is ND should be collected.
Definition: SymbolCollector.cpp:467
clang::clangd::RenameInputs::MainFilePath
llvm::StringRef MainFilePath
Definition: Rename.h:40
clang::clangd::Context::get
const Type * get(const Key< Type > &Key) const
Get data stored for a typed Key.
Definition: Context.h:98
clang::clangd::ParsedAST::getASTContext
ASTContext & getASTContext()
Note that the returned ast will not contain decls from the preamble that were not deserialized during...
Definition: ParsedAST.cpp:700
clang::clangd::detail::error
llvm::Error error(std::error_code, std::string &&)
Definition: Logger.cpp:80
clang::tidy::bugprone::Message
static const char Message[]
Definition: ReservedIdentifierCheck.cpp:31
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:59
clang::clangd::Range::start
Position start
The range's start position.
Definition: Protocol.h:187
clang::clangd::RenameInputs::AST
ParsedAST & AST
Definition: Rename.h:39
Ctx
Context Ctx
Definition: TUScheduler.cpp:553
Trace.h
clang::clangd::RenameResult
Definition: Rename.h:52
FindTarget.h
clang::clangd::RenameInputs::Opts
RenameOptions Opts
Definition: Rename.h:49
clang::clangd::Position::line
int line
Line position in a document (zero-based).
Definition: Protocol.h:158
clang::clangd::URI::resolve
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
Resolves the absolute path of U.
Definition: URI.cpp:245
Offset
size_t Offset
Definition: CodeComplete.cpp:1213
Rename.h
clang::clangd::halfOpenToRange
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R)
Definition: SourceCode.cpp:467
clang::clangd::Position
Definition: Protocol.h:156
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
LastLine
unsigned int LastLine
Definition: MacroToEnumCheck.cpp:102
clang::clangd::RenameInputs::FS
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS
Definition: Rename.h:45
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
clang::clangd::SymbolIndex::relations
virtual void relations(const RelationsRequest &Req, llvm::function_ref< void(const SymbolID &Subject, const Symbol &Object)> Callback) const =0
Finds all relations (S, P, O) stored in the index such that S is among Req.Subjects and P is Req....
clang::clangd::SelectionTree::createRight
static SelectionTree createRight(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End)
Definition: Selection.cpp:1059
Line
int Line
Definition: PreprocessorTracker.cpp:514
clang::clangd::CompletionItemKind::Function
@ Function
Logger.h
clang::clangd::positionToOffset
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
Definition: SourceCode.cpp:172
clang::clangd::collectIdentifierRanges
std::vector< Range > collectIdentifierRanges(llvm::StringRef Identifier, llvm::StringRef Content, const LangOptions &LangOpts)
Collects all ranges of the given identifier in the source code.
Definition: SourceCode.cpp:630
clang::clangd::RenameInputs::Index
const SymbolIndex * Index
Definition: Rename.h:47
SPAN_ATTACH
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
Definition: Trace.h:164
clang::clangd::toString
static const char * toString(OffsetEncoding OE)
Definition: Protocol.cpp:1393
clang::clangd::buildRenameEdit
llvm::Expected< Edit > buildRenameEdit(llvm::StringRef AbsFilePath, llvm::StringRef InitialCode, std::vector< Range > Occurrences, llvm::StringRef NewName)
Generates rename edits that replaces all given occurrences with the NewName.
Definition: Rename.cpp:847
clang::clangd::vlog
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:72
Results
std::vector< CodeCompletionResult > Results
Definition: CodeComplete.cpp:792
clang::clangd::Range::end
Position end
The range's end position.
Definition: Protocol.h:190
clang::doc::SymbolID
std::array< uint8_t, 20 > SymbolID
Definition: Representation.h:31
Name
Token Name
Definition: MacroToEnumCheck.cpp:89
clang::clangd::ParsedAST::getHeuristicResolver
const HeuristicResolver * getHeuristicResolver() const
Definition: ParsedAST.h:119
clang::clangd::CompletionItemKind::Method
@ Method
clang::clangd::getMappedRanges
llvm::Optional< std::vector< Range > > getMappedRanges(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed)
Calculates the lexed occurrences that the given indexed occurrences map to.
Definition: Rename.cpp:926
clang::clangd::isProtoFile
bool isProtoFile(SourceLocation Loc, const SourceManager &SM)
Returns true if the given location is in a generated protobuf file.
Definition: SourceCode.cpp:1210
Parent
const Node * Parent
Definition: ExtractFunction.cpp:157
clang::clangd::getSymbolID
SymbolID getSymbolID(const Decl *D)
Gets the symbol ID for a declaration. Returned SymbolID might be null.
Definition: AST.cpp:349
SourceCode.h
Index
const SymbolIndex * Index
Definition: Dexp.cpp:98
clang::clangd::SymbolIndex::refs
virtual bool refs(const RefsRequest &Req, llvm::function_ref< void(const Ref &)> Callback) const =0
Finds all occurrences (e.g.
clang::clangd::trace::Metric::Counter
@ Counter
An aggregate number whose rate of change over time is meaningful.
Definition: Trace.h:46
clang::clangd::RefKind::Spelled
@ Spelled
ID
static char ID
Definition: Logger.cpp:74
Candidate
ExpectedMatch Candidate
Definition: FuzzyMatchTests.cpp:46
clang::clangd::Range
Definition: Protocol.h:185
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
if
if(CLANG_PLUGIN_SUPPORT) export_executable_symbols_for_plugins(clang-tidy) endif() install(PROGRAMS clang-tidy-diff.py DESTINATION "$
Definition: clang-tidy/tool/CMakeLists.txt:59
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::Edit::asTextEdits
std::vector< TextEdit > asTextEdits() const
Represents Replacements as TextEdits that are available for use in LSP.
Definition: SourceCode.cpp:1018
clang::clangd::renameRangeAdjustmentCost
size_t renameRangeAdjustmentCost(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed, ArrayRef< size_t > MappedIndex)
Evaluates how good the mapped result is.
Definition: Rename.cpp:994
clang::clangd::ParsedAST::getTokens
const syntax::TokenBuffer & getTokens() const
Tokens recorded while parsing the main file.
Definition: ParsedAST.h:107
clang::clangd::isInsideMainFile
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
Definition: SourceCode.cpp:418
LangOpts
const LangOptions * LangOpts
Definition: ExtractFunction.cpp:375
clang::clangd::ParsedAST::getLocalTopLevelDecls
ArrayRef< Decl * > getLocalTopLevelDecls()
This function returns top-level decls present in the main file of the AST.
Definition: ParsedAST.cpp:718
clang::doc::Record
llvm::SmallVector< uint64_t, 1024 > Record
Definition: BitcodeReader.cpp:18
clang::clangd::FileEdits
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
Definition: SourceCode.h:204
EnclosingFunction
const FunctionDecl * EnclosingFunction
Definition: ExtractFunction.cpp:161
clang::clangd::TextEdit
Definition: Protocol.h:231
clang::clangd::targetDecl
llvm::SmallVector< const NamedDecl *, 1 > targetDecl(const DynTypedNode &N, DeclRelationSet Mask, const HeuristicResolver *Resolver)
targetDecl() finds the declaration referred to by an AST node.
Definition: FindTarget.cpp:564
SymbolCollector.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:1198
clang::clangd::ParsedAST
Stores and provides access to parsed AST.
Definition: ParsedAST.h:46
MainFile
std::string MainFile
Definition: HeadersTests.cpp:136
clang::clangd::config::toRange
Range toRange(llvm::SMRange R, const llvm::SourceMgr &SM)
Definition: ConfigTesting.h:81
Field
const FieldDecl * Field
Definition: MemberwiseConstructor.cpp:260
clang::clangd::RenameInputs::Pos
Position Pos
Definition: Rename.h:36
clang::clangd::elog
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:61
clang::clangd::adjustRenameRanges
llvm::Optional< std::vector< Range > > adjustRenameRanges(llvm::StringRef DraftCode, llvm::StringRef Identifier, std::vector< Range > Indexed, const LangOptions &LangOpts)
Adjusts indexed occurrences to match the current state of the file.
Definition: Rename.cpp:915
clang::clangd::ParsedAST::getSourceManager
SourceManager & getSourceManager()
Definition: ParsedAST.h:74
clang::clangd::findExplicitReferences
void findExplicitReferences(const Stmt *S, llvm::function_ref< void(ReferenceLoc)> Out, const HeuristicResolver *Resolver)
Recursively traverse S and report all references explicitly written in the code.
Definition: FindTarget.cpp:1125
Details
std::string Details
Definition: Rename.cpp:455
clang::clangd::DeclRelation::TemplatePattern
@ TemplatePattern
This is the pattern the template specialization was instantiated from.
clang::clangd::RelationKind::OverriddenBy
@ OverriddenBy
clang::clangd::RenameInputs::NewName
llvm::StringRef NewName
Definition: Rename.h:37
clang::clangd::SymbolOrigin::Identifier
@ Identifier
clang::clangd::sourceLocationInMainFile
llvm::Expected< SourceLocation > sourceLocationInMainFile(const SourceManager &SM, Position P)
Return the file location, corresponding to P.
Definition: SourceCode.cpp:457
K
Kind K
Definition: Rename.cpp:454
clang::clangd::DeclRelation::Alias
@ Alias
This declaration is an alias that was referred to.
AST.h
ParsedAST.h
clang::clangd::InlayHintKind::Parameter
@ Parameter
An inlay hint that is for a parameter.
clang::clangd::trace::Span
Records an event whose duration is the lifetime of the Span object.
Definition: Trace.h:143