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/DeclObjC.h"
23#include "clang/AST/DeclTemplate.h"
24#include "clang/AST/ParentMapContext.h"
25#include "clang/AST/Stmt.h"
26#include "clang/Basic/CharInfo.h"
27#include "clang/Basic/LLVM.h"
28#include "clang/Basic/SourceLocation.h"
29#include "clang/Tooling/Syntax/Tokens.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"
43std::optional<std::string> filePath(
const SymbolLocation &
Loc,
44 llvm::StringRef HintFilePath) {
49 elog(
"Could not resolve URI {0}: {1}",
Loc.FileURI,
Path.takeError());
57bool isInMacroBody(
const SourceManager &SM, SourceLocation
Loc) {
58 while (
Loc.isMacroID()) {
59 if (SM.isMacroBodyExpansion(
Loc))
61 Loc = SM.getImmediateMacroCallerLoc(
Loc);
86const NamedDecl *canonicalRenameDecl(
const NamedDecl *D) {
87 if (
const auto *VarTemplate = dyn_cast<VarTemplateSpecializationDecl>(D))
88 return canonicalRenameDecl(
89 VarTemplate->getSpecializedTemplate()->getTemplatedDecl());
90 if (
const auto *Template = dyn_cast<TemplateDecl>(D))
91 if (
const NamedDecl *TemplatedDecl = Template->getTemplatedDecl())
92 return canonicalRenameDecl(TemplatedDecl);
93 if (
const auto *ClassTemplateSpecialization =
94 dyn_cast<ClassTemplateSpecializationDecl>(D))
95 return canonicalRenameDecl(
96 ClassTemplateSpecialization->getSpecializedTemplate()
97 ->getTemplatedDecl());
98 if (
const auto *
Method = dyn_cast<CXXMethodDecl>(D)) {
99 if (
Method->getDeclKind() == Decl::Kind::CXXConstructor ||
100 Method->getDeclKind() == Decl::Kind::CXXDestructor)
101 return canonicalRenameDecl(
Method->getParent());
102 if (
const FunctionDecl *InstantiatedMethod =
103 Method->getInstantiatedFromMemberFunction())
104 return canonicalRenameDecl(InstantiatedMethod);
109 if (
Method->isVirtual() &&
Method->size_overridden_methods())
110 return canonicalRenameDecl(*
Method->overridden_methods().begin());
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)) {
120 const auto *FieldParent =
121 dyn_cast_or_null<CXXRecordDecl>(
Field->getParent());
123 return Field->getCanonicalDecl();
124 FieldParent = FieldParent->getTemplateInstantiationPattern();
126 if (!FieldParent ||
Field->getParent() == FieldParent)
127 return Field->getCanonicalDecl();
128 for (
const FieldDecl *
Candidate : FieldParent->fields())
131 elog(
"FieldParent should have field with the same name as Field.");
133 if (
const auto *VD = dyn_cast<VarDecl>(D)) {
134 if (
const VarDecl *OriginalVD = VD->getInstantiatedFromStaticDataMember())
135 return canonicalRenameDecl(OriginalVD);
137 if (
const auto *UD = dyn_cast<UsingShadowDecl>(D)) {
138 if (
const auto *TargetDecl = UD->getTargetDecl())
139 return canonicalRenameDecl(TargetDecl);
141 return dyn_cast<NamedDecl>(
D->getCanonicalDecl());
146const NamedDecl *pickInterestingTarget(
const NamedDecl *D) {
150 if (
const auto *CD = dyn_cast<ObjCCategoryDecl>(D))
151 if (
const auto CI = CD->getClassInterface())
156llvm::DenseSet<const NamedDecl *> locateDeclAt(ParsedAST &
AST,
157 SourceLocation TokenStartLoc) {
159 AST.getSourceManager().getDecomposedSpellingLoc(TokenStartLoc).second;
163 const SelectionTree::Node *SelectedNode = Selection.commonAncestor();
167 llvm::DenseSet<const NamedDecl *> Result;
168 for (
const NamedDecl *D :
171 AST.getHeuristicResolver())) {
172 D = pickInterestingTarget(D);
173 Result.insert(canonicalRenameDecl(D));
178void filterRenameTargets(llvm::DenseSet<const NamedDecl *> &Decls) {
186 auto UD = std::find_if(Decls.begin(), Decls.end(), [](
const NamedDecl *D) {
187 return llvm::isa<UsingDecl>(D);
189 if (UD != Decls.end()) {
197bool isExcluded(
const NamedDecl &RenameDecl) {
198 const auto &SM = RenameDecl.getASTContext().getSourceManager();
199 return SM.isInSystemHeader(RenameDecl.getLocation()) ||
203enum class ReasonToReject {
214std::optional<ReasonToReject> renameable(
const NamedDecl &RenameDecl,
215 StringRef MainFilePath,
216 const SymbolIndex *Index,
217 const RenameOptions &Opts) {
218 trace::Span Tracer(
"Renameable");
219 if (!Opts.RenameVirtual) {
220 if (
const auto *S = llvm::dyn_cast<CXXMethodDecl>(&RenameDecl)) {
222 return ReasonToReject::UnsupportedSymbol;
226 if (llvm::isa<NamespaceDecl>(&RenameDecl))
227 return ReasonToReject::UnsupportedSymbol;
228 if (
const auto *FD = llvm::dyn_cast<FunctionDecl>(&RenameDecl)) {
229 if (FD->isOverloadedOperator())
230 return ReasonToReject::UnsupportedSymbol;
233 if (RenameDecl.getParentFunctionOrMethod())
236 if (isExcluded(RenameDecl))
237 return ReasonToReject::UnsupportedSymbol;
240 auto &ASTCtx = RenameDecl.getASTContext();
241 bool MainFileIsHeader =
isHeaderFile(MainFilePath, ASTCtx.getLangOpts());
242 bool DeclaredInMainFile =
244 bool IsMainFileOnly =
true;
245 if (MainFileIsHeader)
247 IsMainFileOnly =
false;
248 else if (!DeclaredInMainFile)
249 IsMainFileOnly =
false;
252 RenameDecl, RenameDecl.getASTContext(), SymbolCollector::Options(),
254 return ReasonToReject::NonIndexable;
259llvm::Error makeError(ReasonToReject Reason) {
260 auto Message = [](ReasonToReject Reason) {
262 case ReasonToReject::NoSymbolFound:
263 return "there is no symbol at the given location";
264 case ReasonToReject::NoIndexProvided:
265 return "no index provided";
266 case ReasonToReject::NonIndexable:
267 return "symbol may be used in other files (not eligible for indexing)";
268 case ReasonToReject::UnsupportedSymbol:
269 return "symbol is not a supported kind (e.g. namespace, macro)";
270 case ReasonToReject::AmbiguousSymbol:
271 return "there are multiple symbols at the given location";
272 case ReasonToReject::SameName:
273 return "new name is the same as the old name";
275 llvm_unreachable(
"unhandled reason kind");
277 return error(
"Cannot rename symbol: {0}",
Message(Reason));
281std::vector<SourceLocation> findOccurrencesWithinFile(ParsedAST &
AST,
282 const NamedDecl &ND) {
283 trace::Span Tracer(
"FindOccurrencesWithinFile");
284 assert(canonicalRenameDecl(&ND) == &ND &&
285 "ND should be already canonicalized.");
287 std::vector<SourceLocation>
Results;
288 for (
Decl *TopLevelDecl :
AST.getLocalTopLevelDecls()) {
291 [&](ReferenceLoc Ref) {
292 if (Ref.Targets.empty())
294 for (
const auto *Target : Ref.Targets) {
295 if (canonicalRenameDecl(Target) == &ND) {
296 Results.push_back(Ref.NameLoc);
301 AST.getHeuristicResolver());
308const NamedDecl *lookupSiblingWithinEnclosingScope(ASTContext &Ctx,
309 const NamedDecl &RenamedDecl,
313 DynTypedNodeList Storage(DynTypedNode::create(RenamedDecl));
314 auto GetSingleParent = [&](
const DynTypedNode &
Node) ->
const DynTypedNode * {
315 Storage = Ctx.getParents(Node);
316 return (Storage.size() == 1) ? Storage.begin() :
nullptr;
322 const auto *
Parent = GetSingleParent(DynTypedNode::create(RenamedDecl));
329 auto CheckDeclStmt = [&](
const DeclStmt *DS,
330 StringRef
Name) ->
const NamedDecl * {
333 for (
const auto &Child : DS->getDeclGroup())
334 if (
const auto *ND = dyn_cast<NamedDecl>(Child))
335 if (ND != &RenamedDecl && ND->getDeclName().isIdentifier() &&
336 ND->getName() ==
Name)
340 auto CheckCompoundStmt = [&](
const Stmt *S,
341 StringRef
Name) ->
const NamedDecl * {
342 if (
const auto *CS = dyn_cast_or_null<CompoundStmt>(S))
343 for (
const auto *Node : CS->children())
344 if (
const auto *Result = CheckDeclStmt(dyn_cast<DeclStmt>(Node),
Name))
348 auto CheckConditionVariable = [&](
const auto *Scope,
349 StringRef
Name) ->
const NamedDecl * {
352 return CheckDeclStmt(Scope->getConditionVariableDeclStmt(),
Name);
358 if (
const auto *EnclosingCS =
Parent->get<CompoundStmt>()) {
359 if (
const auto *Result = CheckCompoundStmt(EnclosingCS, NewName))
361 const auto *ScopeParent = GetSingleParent(*
Parent);
365 if (
const auto *Result =
366 CheckConditionVariable(ScopeParent->get<IfStmt>(), NewName))
368 if (
const auto *Result =
369 CheckConditionVariable(ScopeParent->get<WhileStmt>(), NewName))
371 if (
const auto *For = ScopeParent->get<ForStmt>())
372 if (
const auto *Result = CheckDeclStmt(
373 dyn_cast_or_null<DeclStmt>(For->getInit()), NewName))
376 if (
const auto *
Function = ScopeParent->get<FunctionDecl>())
385 if (
const auto *EnclosingIf =
Parent->get<IfStmt>()) {
386 if (
const auto *Result = CheckCompoundStmt(EnclosingIf->getElse(), NewName))
388 return CheckCompoundStmt(EnclosingIf->getThen(), NewName);
390 if (
const auto *EnclosingWhile =
Parent->get<WhileStmt>())
391 return CheckCompoundStmt(EnclosingWhile->getBody(), NewName);
392 if (
const auto *EnclosingFor =
Parent->get<ForStmt>()) {
395 if (
const auto *Result = CheckDeclStmt(
396 dyn_cast_or_null<DeclStmt>(EnclosingFor->getInit()), NewName))
398 return CheckCompoundStmt(EnclosingFor->getBody(), NewName);
418const NamedDecl *lookupSiblingsWithinContext(ASTContext &Ctx,
419 const NamedDecl &RenamedDecl,
420 llvm::StringRef NewName) {
421 const auto &II = Ctx.Idents.get(NewName);
422 DeclarationName LookupName(&II);
423 DeclContextLookupResult LookupResult;
424 const auto *DC = RenamedDecl.getDeclContext();
425 while (DC->isTransparentContext())
426 DC = DC->getParent();
427 switch (DC->getDeclKind()) {
436 case Decl::TranslationUnit:
437 case Decl::Namespace:
440 case Decl::CXXRecord:
441 LookupResult = DC->lookup(LookupName);
447 for (
const auto *D : LookupResult)
448 if (
D->getCanonicalDecl() != RenamedDecl.getCanonicalDecl())
453const NamedDecl *lookupSiblingWithName(ASTContext &Ctx,
454 const NamedDecl &RenamedDecl,
455 llvm::StringRef NewName) {
456 trace::Span Tracer(
"LookupSiblingWithName");
457 if (
const auto *Result =
458 lookupSiblingsWithinContext(Ctx, RenamedDecl, NewName))
460 return lookupSiblingWithinEnclosingScope(Ctx, RenamedDecl, NewName);
472std::string
toString(InvalidName::Kind
K) {
474 case InvalidName::Keywords:
476 case InvalidName::Conflict:
478 case InvalidName::BadIdentifier:
479 return "BadIdentifier";
481 llvm_unreachable(
"unhandled InvalidName kind");
484llvm::Error makeError(InvalidName Reason) {
485 auto Message = [](
const InvalidName &Reason) {
487 case InvalidName::Keywords:
488 return llvm::formatv(
"the chosen name \"{0}\" is a keyword",
490 case InvalidName::Conflict:
491 return llvm::formatv(
"conflict with the symbol in {0}", Reason.Details);
492 case InvalidName::BadIdentifier:
493 return llvm::formatv(
"the chosen name \"{0}\" is not a valid identifier",
496 llvm_unreachable(
"unhandled InvalidName kind");
501static bool mayBeValidIdentifier(llvm::StringRef Ident) {
502 assert(llvm::json::isUTF8(Ident));
506 bool AllowDollar =
true;
507 if (llvm::isASCII(Ident.front()) &&
508 !isAsciiIdentifierStart(Ident.front(), AllowDollar))
510 for (
char C : Ident) {
511 if (llvm::isASCII(
C) && !isAsciiIdentifierContinue(
C, AllowDollar))
519std::optional<InvalidName> checkName(
const NamedDecl &RenameDecl,
520 llvm::StringRef NewName) {
521 trace::Span Tracer(
"CheckName");
522 static constexpr trace::Metric InvalidNameMetric(
524 auto &ASTCtx = RenameDecl.getASTContext();
525 std::optional<InvalidName> Result;
526 if (
isKeyword(NewName, ASTCtx.getLangOpts()))
527 Result = InvalidName{InvalidName::Keywords, NewName.str()};
528 else if (!mayBeValidIdentifier(NewName))
529 Result = InvalidName{InvalidName::BadIdentifier, NewName.str()};
533 if (RenameDecl.getKind() != Decl::Function &&
534 RenameDecl.getKind() != Decl::CXXMethod) {
535 if (
auto *Conflict = lookupSiblingWithName(ASTCtx, RenameDecl, NewName))
536 Result = InvalidName{
537 InvalidName::Conflict,
538 Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
542 InvalidNameMetric.record(1,
toString(Result->K));
547llvm::Expected<tooling::Replacements>
548renameWithinFile(ParsedAST &
AST,
const NamedDecl &RenameDecl,
549 llvm::StringRef NewName) {
550 trace::Span Tracer(
"RenameWithinFile");
551 const SourceManager &SM =
AST.getSourceManager();
553 tooling::Replacements FilteredChanges;
554 for (SourceLocation
Loc : findOccurrencesWithinFile(
AST, RenameDecl)) {
555 SourceLocation RenameLoc =
Loc;
558 if (RenameLoc.isMacroID()) {
559 if (isInMacroBody(SM, RenameLoc))
561 RenameLoc = SM.getSpellingLoc(
Loc);
572 if (
auto Err = FilteredChanges.add(tooling::Replacement(
573 SM, CharSourceRange::getTokenRange(RenameLoc), NewName)))
574 return std::move(Err);
576 return FilteredChanges;
581 R.start.line = L.Start.line();
582 R.start.character = L.Start.column();
583 R.end.line = L.End.line();
584 R.end.character = L.End.column();
591void insertTransitiveOverrides(SymbolID Base, llvm::DenseSet<SymbolID> &IDs,
592 const SymbolIndex &Index) {
593 RelationsRequest Req;
596 llvm::DenseSet<SymbolID> Pending = {Base};
597 while (!Pending.empty()) {
598 Req.Subjects = std::move(Pending);
601 Index.
relations(Req, [&](
const SymbolID &,
const Symbol &Override) {
602 if (IDs.insert(Override.ID).second)
603 Pending.insert(Override.ID);
610llvm::Expected<llvm::StringMap<std::vector<Range>>>
611findOccurrencesOutsideFile(
const NamedDecl &RenameDecl,
612 llvm::StringRef
MainFile,
const SymbolIndex &Index,
613 size_t MaxLimitFiles) {
614 trace::Span Tracer(
"FindOccurrencesOutsideFile");
618 if (
const auto *MethodDecl = llvm::dyn_cast<CXXMethodDecl>(&RenameDecl))
619 if (MethodDecl->isVirtual())
620 insertTransitiveOverrides(*RQuest.IDs.begin(), RQuest.IDs, Index);
623 llvm::StringMap<std::vector<Range>> AffectedFiles;
624 bool HasMore = Index.
refs(RQuest, [&](
const Ref &R) {
625 if (AffectedFiles.size() >= MaxLimitFiles)
629 if (
auto RefFilePath = filePath(R.Location,
MainFile)) {
630 if (!pathEqual(*RefFilePath, MainFile))
631 AffectedFiles[*RefFilePath].push_back(toRange(R.Location));
635 if (AffectedFiles.size() >= MaxLimitFiles)
636 return error(
"The number of affected files exceeds the max limit {0}",
639 return error(
"The symbol {0} has too many occurrences",
640 RenameDecl.getQualifiedNameAsString());
642 for (
auto &FileAndOccurrences : AffectedFiles) {
643 auto &Ranges = FileAndOccurrences.getValue();
645 Ranges.erase(std::unique(Ranges.begin(), Ranges.end()), Ranges.end());
648 static_cast<int64_t
>(Ranges.size()));
650 return AffectedFiles;
665llvm::Expected<FileEdits>
666renameOutsideFile(
const NamedDecl &RenameDecl, llvm::StringRef MainFilePath,
667 llvm::StringRef NewName,
const SymbolIndex &Index,
668 size_t MaxLimitFiles, llvm::vfs::FileSystem &FS) {
669 trace::Span Tracer(
"RenameOutsideFile");
670 auto AffectedFiles = findOccurrencesOutsideFile(RenameDecl, MainFilePath,
671 Index, MaxLimitFiles);
673 return AffectedFiles.takeError();
675 for (
auto &FileAndOccurrences : *AffectedFiles) {
676 llvm::StringRef FilePath = FileAndOccurrences.first();
678 auto ExpBuffer = FS.getBufferForFile(FilePath);
680 elog(
"Fail to read file content: Fail to open file {0}: {1}", FilePath,
681 ExpBuffer.getError().message());
685 auto AffectedFileCode = (*ExpBuffer)->getBuffer();
688 std::move(FileAndOccurrences.second),
689 RenameDecl.getASTContext().getLangOpts());
694 return error(
"Index results don't match the content of file {0} "
695 "(the index may be stale)",
701 return error(
"failed to rename in file {0}: {1}", FilePath,
702 RenameEdit.takeError());
703 if (!RenameEdit->Replacements.empty())
704 Results.insert({FilePath, std::move(*RenameEdit)});
710bool impliesSimpleEdit(
const Position &LHS,
const Position &RHS) {
711 return LHS.line == RHS.line || LHS.character == RHS.character;
726 std::vector<size_t> &PartialMatch, ArrayRef<Range> IndexedRest,
727 ArrayRef<Range> LexedRest,
int LexedIndex,
int &Fuel,
728 llvm::function_ref<
void(
const std::vector<size_t> &)> MatchedCB) {
731 if (IndexedRest.size() > LexedRest.size())
733 if (IndexedRest.empty()) {
734 MatchedCB(PartialMatch);
737 if (impliesSimpleEdit(IndexedRest.front().start, LexedRest.front().start)) {
738 PartialMatch.push_back(LexedIndex);
739 findNearMiss(PartialMatch, IndexedRest.drop_front(), LexedRest.drop_front(),
740 LexedIndex + 1, Fuel, MatchedCB);
741 PartialMatch.pop_back();
743 findNearMiss(PartialMatch, IndexedRest, LexedRest.drop_front(),
744 LexedIndex + 1, Fuel, MatchedCB);
750 assert(!RInputs.
Index == !RInputs.
FS &&
751 "Index and FS must either both be specified or both null.");
753 const auto &Opts = RInputs.
Opts;
755 const SourceManager &SM = AST.getSourceManager();
756 llvm::StringRef MainFileCode = SM.getBufferData(SM.getMainFileID());
760 return Loc.takeError();
761 const syntax::Token *IdentifierToken =
762 spelledIdentifierTouching(*
Loc, AST.getTokens());
765 if (!IdentifierToken)
766 return makeError(ReasonToReject::NoSymbolFound);
768 SM, CharSourceRange::getCharRange(IdentifierToken->location(),
769 IdentifierToken->endLocation()));
773 return makeError(ReasonToReject::UnsupportedSymbol);
775 auto DeclsUnderCursor = locateDeclAt(AST, IdentifierToken->location());
776 filterRenameTargets(DeclsUnderCursor);
777 if (DeclsUnderCursor.empty())
778 return makeError(ReasonToReject::NoSymbolFound);
779 if (DeclsUnderCursor.size() > 1)
780 return makeError(ReasonToReject::AmbiguousSymbol);
781 const auto &RenameDecl = **DeclsUnderCursor.begin();
782 const auto *
ID = RenameDecl.getIdentifier();
784 return makeError(ReasonToReject::UnsupportedSymbol);
786 return makeError(ReasonToReject::SameName);
787 auto Invalid = checkName(RenameDecl, RInputs.
NewName);
789 return makeError(std::move(*Invalid));
794 return makeError(*Reject);
805 auto MainFileRenameEdit = renameWithinFile(AST, RenameDecl, RInputs.
NewName);
806 if (!MainFileRenameEdit)
807 return MainFileRenameEdit.takeError();
817 return StartOffset.takeError();
819 return EndOffset.takeError();
822 [&StartOffset, &EndOffset](
const clang::tooling::Replacement &R) {
823 return R.getOffset() == *StartOffset &&
824 R.getLength() == *EndOffset - *StartOffset;
826 return makeError(ReasonToReject::NoSymbolFound);
829 Result.Target = CurrentIdentifier;
830 Edit MainFileEdits =
Edit(MainFileCode, std::move(*MainFileRenameEdit));
832 Result.LocalChanges.push_back(TE.range);
836 if (RenameDecl.getParentFunctionOrMethod()) {
838 {std::make_pair(RInputs.
MainFilePath, std::move(MainFileEdits))});
844 if (!RInputs.
Index) {
845 assert(Result.GlobalChanges.empty());
849 auto OtherFilesEdits = renameOutsideFile(
851 Opts.LimitFiles == 0 ? std::numeric_limits<size_t>::max()
854 if (!OtherFilesEdits)
855 return OtherFilesEdits.takeError();
856 Result.GlobalChanges = *OtherFilesEdits;
859 std::move(MainFileEdits));
864 llvm::StringRef InitialCode,
865 std::vector<Range> Occurrences,
866 llvm::StringRef NewName) {
870 static_cast<int64_t
>(Occurrences.size()));
872 assert(llvm::is_sorted(Occurrences));
873 assert(std::unique(Occurrences.begin(), Occurrences.end()) ==
875 "Occurrences must be unique");
879 size_t LastOffset = 0;
882 assert(LastPos <= P &&
"malformed input");
884 P.
line - LastPos.line,
885 P.line > LastPos.line ? P.character : P.character - LastPos.character};
889 return error(
"fail to convert the position {0} to offset ({1})", P,
890 ShiftedOffset.takeError());
892 LastOffset += *ShiftedOffset;
896 std::vector<std::pair< size_t,
size_t>> OccurrencesOffsets;
897 for (
const auto &R : Occurrences) {
898 auto StartOffset =
Offset(R.start);
900 return StartOffset.takeError();
901 auto EndOffset =
Offset(R.end);
903 return EndOffset.takeError();
904 OccurrencesOffsets.push_back({*StartOffset, *EndOffset});
907 tooling::Replacements RenameEdit;
908 for (
const auto &R : OccurrencesOffsets) {
909 auto ByteLength = R.second - R.first;
910 if (
auto Err = RenameEdit.add(
911 tooling::Replacement(AbsFilePath, R.first, ByteLength, NewName)))
912 return std::move(Err);
914 return Edit(InitialCode, std::move(RenameEdit));
930std::optional<std::vector<Range>>
932 std::vector<Range> Indexed,
const LangOptions &LangOpts) {
934 assert(!Indexed.empty());
935 assert(llvm::is_sorted(Indexed));
936 std::vector<Range> Lexed =
943 ArrayRef<Range> Lexed) {
945 assert(!Indexed.empty());
946 assert(llvm::is_sorted(Indexed));
947 assert(llvm::is_sorted(Lexed));
949 if (Indexed.size() > Lexed.size()) {
950 vlog(
"The number of lexed occurrences is less than indexed occurrences");
953 "The number of lexed occurrences is less than indexed occurrences");
957 if (std::includes(Indexed.begin(), Indexed.end(), Lexed.begin(), Lexed.end()))
958 return Indexed.vec();
960 std::vector<size_t> Best;
961 size_t BestCost = std::numeric_limits<size_t>::max();
962 bool HasMultiple =
false;
963 std::vector<size_t> ResultStorage;
965 findNearMiss(ResultStorage, Indexed, Lexed, 0, Fuel,
966 [&](
const std::vector<size_t> &Matched) {
969 if (MCost < BestCost) {
971 Best = std::move(Matched);
975 if (MCost == BestCost)
979 vlog(
"The best near miss is not unique.");
980 SPAN_ATTACH(Tracer,
"error",
"The best near miss is not unique");
984 vlog(
"Didn't find a near miss.");
985 SPAN_ATTACH(Tracer,
"error",
"Didn't find a near miss");
988 std::vector<Range> Mapped;
990 Mapped.push_back(Lexed[I]);
991 SPAN_ATTACH(Tracer,
"mapped_ranges",
static_cast<int64_t
>(Mapped.size()));
1011 ArrayRef<size_t> MappedIndex) {
1012 assert(Indexed.size() == MappedIndex.size());
1013 assert(llvm::is_sorted(Indexed));
1014 assert(llvm::is_sorted(Lexed));
1017 int LastDLine = 0, LastDColumn = 0;
1019 for (
size_t I = 0; I < Indexed.size(); ++I) {
1020 int DLine = Indexed[I].start.line - Lexed[MappedIndex[I]].start.line;
1022 Indexed[I].start.character - Lexed[MappedIndex[I]].start.character;
1023 int Line = Indexed[I].start.line;
1026 Cost += abs(DLine - LastDLine) + abs(DColumn - LastDColumn);
1027 std::tie(
LastLine, LastDLine, LastDColumn) = std::tie(
Line, DLine, DColumn);
const FunctionDecl * Decl
std::vector< CodeCompletionResult > Results
SmallVector< Detail, DefaultLimit > Details
CharSourceRange Range
SourceRange for the file name.
::clang::DynTypedNode Node
std::unique_ptr< CompilerInvocation > CI
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
Stores and provides access to parsed AST.
static SelectionTree createRight(ASTContext &AST, const syntax::TokenBuffer &Tokens, unsigned Begin, unsigned End)
static bool shouldCollectSymbol(const NamedDecl &ND, const ASTContext &ASTCtx, const Options &Opts, bool IsMainFileSymbol)
Returns true is ND should be collected.
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....
virtual bool refs(const RefsRequest &Req, llvm::function_ref< void(const Ref &)> Callback) const =0
Finds all occurrences (e.g.
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
Resolves the absolute path of U.
Records an event whose duration is the lifetime of the Span object.
Range toRange(llvm::SMRange R, const llvm::SourceMgr &SM)
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.
SymbolID getSymbolID(const Decl *D)
Gets the symbol ID for a declaration. Returned SymbolID might be null.
Range halfOpenToRange(const SourceManager &SM, CharSourceRange R)
std::string Path
A typedef to represent a file path.
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
void vlog(const char *Fmt, Ts &&... Vals)
static const char * toString(OffsetEncoding OE)
std::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.
llvm::Expected< RenameResult > rename(const RenameInputs &RInputs)
Renames all occurrences of the symbol.
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.
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
llvm::SmallVector< const NamedDecl *, 1 > targetDecl(const DynTypedNode &N, DeclRelationSet Mask, const HeuristicResolver *Resolver)
targetDecl() finds the declaration referred to by an AST node.
std::optional< DefinedMacro > locateMacroAt(const syntax::Token &SpelledTok, Preprocessor &PP)
Gets the macro referenced by SpelledTok.
std::optional< std::vector< Range > > getMappedRanges(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed)
Calculates the lexed occurrences that the given indexed occurrences map to.
size_t renameRangeAdjustmentCost(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed, ArrayRef< size_t > MappedIndex)
Evaluates how good the mapped result is.
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
std::vector< Range > collectIdentifierRanges(llvm::StringRef Identifier, llvm::StringRef Content, const LangOptions &LangOpts)
Collects all ranges of the given identifier in the source code.
llvm::Expected< SourceLocation > sourceLocationInMainFile(const SourceManager &SM, Position P)
Return the file location, corresponding to P.
@ Parameter
An inlay hint that is for a parameter.
bool isKeyword(llvm::StringRef NewName, const LangOptions &LangOpts)
Return true if the TokenName is in the list of reversed keywords of the language.
void elog(const char *Fmt, Ts &&... Vals)
bool isProtoFile(SourceLocation Loc, const SourceManager &SM)
Returns true if the given location is in a generated protobuf file.
@ TemplatePattern
This is the pattern the template specialization was instantiated from.
@ Alias
This declaration is an alias that was referred to.
bool isHeaderFile(llvm::StringRef FileName, std::optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
constexpr llvm::StringLiteral Message
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A set of edits generated for a single file.
std::vector< TextEdit > asTextEdits() const
Represents Replacements as TextEdits that are available for use in LSP.
int line
Line position in a document (zero-based).
Position start
The range's start position.
Position end
The range's end position.
@ Counter
An aggregate number whose rate of change over time is meaningful.