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