[clang] [clang-tools-extra] [clangd] Add support to rename Objective-C selectors (PR #78872)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 20 18:00:42 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangd
@llvm/pr-subscribers-clang
Author: Alex Hoppen (ahoppen)
<details>
<summary>Changes</summary>
This adds support to rename Objective-C method declarations and calls to clangd.
---
Patch is 54.98 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/78872.diff
24 Files Affected:
- (modified) clang-tools-extra/clangd/ClangdLSPServer.cpp (+3-2)
- (modified) clang-tools-extra/clangd/ClangdLSPServer.h (+1-1)
- (modified) clang-tools-extra/clangd/ClangdServer.cpp (+1-2)
- (modified) clang-tools-extra/clangd/Protocol.cpp (+4)
- (modified) clang-tools-extra/clangd/Protocol.h (+11)
- (modified) clang-tools-extra/clangd/SourceCode.cpp (+9-9)
- (modified) clang-tools-extra/clangd/SourceCode.h (+3-3)
- (modified) clang-tools-extra/clangd/index/SymbolCollector.cpp (+24-3)
- (modified) clang-tools-extra/clangd/refactor/Rename.cpp (+110-37)
- (modified) clang-tools-extra/clangd/refactor/Rename.h (+26-13)
- (modified) clang-tools-extra/clangd/unittests/RenameTests.cpp (+342-9)
- (modified) clang-tools-extra/clangd/unittests/SourceCodeTests.cpp (+2-2)
- (modified) clang/include/clang/Tooling/Refactoring/Rename/RenamingAction.h (+21)
- (modified) clang/include/clang/Tooling/Refactoring/Rename/SymbolName.h (+40-10)
- (modified) clang/include/clang/Tooling/Syntax/Tokens.h (+13)
- (modified) clang/lib/Tooling/Refactoring/CMakeLists.txt (+2)
- (modified) clang/lib/Tooling/Refactoring/Rename/RenamingAction.cpp (+96-2)
- (modified) clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp (+2-2)
- (added) clang/lib/Tooling/Refactoring/SymbolName.cpp (+70)
- (modified) clang/lib/Tooling/Syntax/Tokens.cpp (+7)
- (modified) clang/tools/clang-refactor/CMakeLists.txt (+1)
- (modified) clang/tools/clang-rename/CMakeLists.txt (+1)
- (modified) clang/tools/libclang/CMakeLists.txt (+1)
- (modified) clang/unittests/Tooling/RefactoringActionRulesTest.cpp (+3-3)
``````````diff
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp
index a87da252b7a7e9b..5dfd12045b65738 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -844,14 +844,15 @@ void ClangdLSPServer::onWorkspaceSymbol(
}
void ClangdLSPServer::onPrepareRename(const TextDocumentPositionParams &Params,
- Callback<std::optional<Range>> Reply) {
+ Callback<PrepareRenameResult> Reply) {
Server->prepareRename(
Params.textDocument.uri.file(), Params.position, /*NewName*/ std::nullopt,
Opts.Rename,
[Reply = std::move(Reply)](llvm::Expected<RenameResult> Result) mutable {
if (!Result)
return Reply(Result.takeError());
- return Reply(std::move(Result->Target));
+ PrepareRenameResult PrepareResult{Result->Target, Result->OldName};
+ return Reply(std::move(PrepareResult));
});
}
diff --git a/clang-tools-extra/clangd/ClangdLSPServer.h b/clang-tools-extra/clangd/ClangdLSPServer.h
index 79579c22b788a9c..6a9f097d551ae6d 100644
--- a/clang-tools-extra/clangd/ClangdLSPServer.h
+++ b/clang-tools-extra/clangd/ClangdLSPServer.h
@@ -134,7 +134,7 @@ class ClangdLSPServer : private ClangdServer::Callbacks,
void onWorkspaceSymbol(const WorkspaceSymbolParams &,
Callback<std::vector<SymbolInformation>>);
void onPrepareRename(const TextDocumentPositionParams &,
- Callback<std::optional<Range>>);
+ Callback<PrepareRenameResult>);
void onRename(const RenameParams &, Callback<WorkspaceEdit>);
void onHover(const TextDocumentPositionParams &,
Callback<std::optional<Hover>>);
diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp
index 6fb2641e8793db1..b04ebc7049c6619 100644
--- a/clang-tools-extra/clangd/ClangdServer.cpp
+++ b/clang-tools-extra/clangd/ClangdServer.cpp
@@ -578,8 +578,7 @@ void ClangdServer::prepareRename(PathRef File, Position Pos,
// prepareRename is latency-sensitive: we don't query the index, as we
// only need main-file references
auto Results =
- clangd::rename({Pos, NewName.value_or("__clangd_rename_placeholder"),
- InpAST->AST, File, /*FS=*/nullptr,
+ clangd::rename({Pos, NewName, InpAST->AST, File, /*FS=*/nullptr,
/*Index=*/nullptr, RenameOpts});
if (!Results) {
// LSP says to return null on failure, but that will result in a generic
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index a6370649f5ad1cf..0291e5d71d65c88 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -905,6 +905,10 @@ llvm::json::Value toJSON(const DocumentSymbol &S) {
return std::move(Result);
}
+llvm::json::Value toJSON(const PrepareRenameResult &R) {
+ return llvm::json::Object{{"range", R.range}, {"placeholder", R.placeholder}};
+}
+
llvm::json::Value toJSON(const WorkspaceEdit &WE) {
llvm::json::Object Result;
if (WE.changes) {
diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h
index e88c804692568f5..274c7fe4391062b 100644
--- a/clang-tools-extra/clangd/Protocol.h
+++ b/clang-tools-extra/clangd/Protocol.h
@@ -1004,6 +1004,17 @@ struct CodeActionParams {
};
bool fromJSON(const llvm::json::Value &, CodeActionParams &, llvm::json::Path);
+struct PrepareRenameResult {
+ /// The range of the string to rename.
+ Range range;
+ /// A placeholder text of the string content to be renamed.
+ ///
+ /// This is usueful to populate the rename field with an Objective-C selector
+ /// name (eg. `performAction:with:`) when renaming Objective-C methods.
+ std::string placeholder;
+};
+llvm::json::Value toJSON(const PrepareRenameResult &WE);
+
/// The edit should either provide changes or documentChanges. If the client
/// can handle versioned document edits and if documentChanges are present,
/// the latter are preferred over changes.
diff --git a/clang-tools-extra/clangd/SourceCode.cpp b/clang-tools-extra/clangd/SourceCode.cpp
index 835038423fdf372..0081a69357b02f4 100644
--- a/clang-tools-extra/clangd/SourceCode.cpp
+++ b/clang-tools-extra/clangd/SourceCode.cpp
@@ -625,16 +625,16 @@ llvm::StringMap<unsigned> collectIdentifiers(llvm::StringRef Content,
return Identifiers;
}
-std::vector<Range> collectIdentifierRanges(llvm::StringRef Identifier,
- llvm::StringRef Content,
- const LangOptions &LangOpts) {
+std::vector<Range>
+collectIdentifierRanges(llvm::StringRef Identifier,
+ const syntax::UnexpandedTokenBuffer &Tokens) {
std::vector<Range> Ranges;
- lex(Content, LangOpts,
- [&](const syntax::Token &Tok, const SourceManager &SM) {
- if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
- return;
- Ranges.push_back(halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
- });
+ const SourceManager &SM = Tokens.sourceManager();
+ for (const syntax::Token &Tok : Tokens.tokens()) {
+ if (Tok.kind() != tok::identifier || Tok.text(SM) != Identifier)
+ continue;
+ Ranges.push_back(halfOpenToRange(SM, Tok.range(SM).toCharRange(SM)));
+ }
return Ranges;
}
diff --git a/clang-tools-extra/clangd/SourceCode.h b/clang-tools-extra/clangd/SourceCode.h
index a1bb44c17612021..f60683fc4f21c35 100644
--- a/clang-tools-extra/clangd/SourceCode.h
+++ b/clang-tools-extra/clangd/SourceCode.h
@@ -217,9 +217,9 @@ llvm::StringMap<unsigned> collectIdentifiers(llvm::StringRef Content,
const format::FormatStyle &Style);
/// Collects all ranges of the given identifier in the source code.
-std::vector<Range> collectIdentifierRanges(llvm::StringRef Identifier,
- llvm::StringRef Content,
- const LangOptions &LangOpts);
+std::vector<Range>
+collectIdentifierRanges(llvm::StringRef Identifier,
+ const syntax::UnexpandedTokenBuffer &Tokens);
/// Collects words from the source code.
/// Unlike collectIdentifiers:
diff --git a/clang-tools-extra/clangd/index/SymbolCollector.cpp b/clang-tools-extra/clangd/index/SymbolCollector.cpp
index bf838e53f2a21e7..24bd3c426b3e86b 100644
--- a/clang-tools-extra/clangd/index/SymbolCollector.cpp
+++ b/clang-tools-extra/clangd/index/SymbolCollector.cpp
@@ -173,9 +173,20 @@ std::optional<RelationKind> indexableRelation(const index::SymbolRelation &R) {
bool isSpelled(SourceLocation Loc, const NamedDecl &ND) {
auto Name = ND.getDeclName();
const auto NameKind = Name.getNameKind();
- if (NameKind != DeclarationName::Identifier &&
- NameKind != DeclarationName::CXXConstructorName)
+ bool PrefixComparison;
+ switch (NameKind) {
+ case DeclarationName::Identifier:
+ case DeclarationName::CXXConstructorName:
+ case DeclarationName::ObjCZeroArgSelector:
+ PrefixComparison = false;
+ break;
+ case DeclarationName::ObjCOneArgSelector:
+ case DeclarationName::ObjCMultiArgSelector:
+ PrefixComparison = true;
+ break;
+ default:
return false;
+ }
const auto &AST = ND.getASTContext();
const auto &SM = AST.getSourceManager();
const auto &LO = AST.getLangOpts();
@@ -183,7 +194,17 @@ bool isSpelled(SourceLocation Loc, const NamedDecl &ND) {
if (clang::Lexer::getRawToken(Loc, Tok, SM, LO))
return false;
auto StrName = Name.getAsString();
- return clang::Lexer::getSpelling(Tok, SM, LO) == StrName;
+ std::string LexerSpelling = clang::Lexer::getSpelling(Tok, SM, LO);
+ if (PrefixComparison) {
+ // The lexer spelling at the source location is only the first label of an
+ // Objective-C selector, eg. if `StrName` is `performAction:with:`, then the
+ // token at the requested location is `performAction`. Re-building the
+ // entire selector from the lexer is too complicated here, so just perform
+ // a prefix comparison.
+ return StringRef(StrName).starts_with(LexerSpelling);
+ } else {
+ return StrName == LexerSpelling;
+ }
}
} // namespace
diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp
index 11f9e4627af7609..f639cbc9b4b1273 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -26,6 +26,8 @@
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
+#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
+#include "clang/Tooling/Refactoring/Rename/SymbolName.h"
#include "clang/Tooling/Syntax/Tokens.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
@@ -40,6 +42,8 @@ namespace clang {
namespace clangd {
namespace {
+using tooling::SymbolName;
+
std::optional<std::string> filePath(const SymbolLocation &Loc,
llvm::StringRef HintFilePath) {
if (!Loc)
@@ -517,22 +521,27 @@ static bool mayBeValidIdentifier(llvm::StringRef Ident) {
// Check if we can rename the given RenameDecl into NewName.
// Return details if the rename would produce a conflict.
std::optional<InvalidName> checkName(const NamedDecl &RenameDecl,
- llvm::StringRef NewName) {
+ const SymbolName &NewName) {
trace::Span Tracer("CheckName");
static constexpr trace::Metric InvalidNameMetric(
"rename_name_invalid", trace::Metric::Counter, "invalid_kind");
auto &ASTCtx = RenameDecl.getASTContext();
+ auto Identifier = NewName.getSinglePiece();
+ if (!Identifier) {
+ return std::nullopt;
+ }
std::optional<InvalidName> Result;
- if (isKeyword(NewName, ASTCtx.getLangOpts()))
- Result = InvalidName{InvalidName::Keywords, NewName.str()};
- else if (!mayBeValidIdentifier(NewName))
- Result = InvalidName{InvalidName::BadIdentifier, NewName.str()};
- else {
+ if (isKeyword(*Identifier, ASTCtx.getLangOpts())) {
+ Result = InvalidName{InvalidName::Keywords, *Identifier};
+ } else if (!mayBeValidIdentifier(*Identifier)) {
+ Result = InvalidName{InvalidName::BadIdentifier, *Identifier};
+ } else {
// Name conflict detection.
// Function conflicts are subtle (overloading), so ignore them.
if (RenameDecl.getKind() != Decl::Function &&
RenameDecl.getKind() != Decl::CXXMethod) {
- if (auto *Conflict = lookupSiblingWithName(ASTCtx, RenameDecl, NewName))
+ if (auto *Conflict =
+ lookupSiblingWithName(ASTCtx, RenameDecl, *Identifier))
Result = InvalidName{
InvalidName::Conflict,
Conflict->getLocation().printToString(ASTCtx.getSourceManager())};
@@ -546,7 +555,7 @@ std::optional<InvalidName> checkName(const NamedDecl &RenameDecl,
// AST-based rename, it renames all occurrences in the main file.
llvm::Expected<tooling::Replacements>
renameWithinFile(ParsedAST &AST, const NamedDecl &RenameDecl,
- llvm::StringRef NewName) {
+ const SymbolName &OldName, const SymbolName &NewName) {
trace::Span Tracer("RenameWithinFile");
const SourceManager &SM = AST.getSourceManager();
@@ -569,9 +578,30 @@ renameWithinFile(ParsedAST &AST, const NamedDecl &RenameDecl,
// }
if (!isInsideMainFile(RenameLoc, SM))
continue;
- if (auto Err = FilteredChanges.add(tooling::Replacement(
- SM, CharSourceRange::getTokenRange(RenameLoc), NewName)))
- return std::move(Err);
+ if (std::optional<std::string> Identifier = NewName.getSinglePiece()) {
+ tooling::Replacement NewReplacement(
+ SM, CharSourceRange::getTokenRange(RenameLoc), *Identifier);
+ if (auto Err = FilteredChanges.add(NewReplacement)) {
+ return std::move(Err);
+ }
+ continue;
+ }
+ SmallVector<SourceLocation> PieceLocations;
+ llvm::Error Error = findObjCSymbolSelectorPieces(
+ AST.getTokens().expandedTokens(), SM, RenameLoc, OldName,
+ tooling::ObjCSymbolSelectorKind::Unknown, PieceLocations);
+ if (Error) {
+ // Ignore the error. We simply skip over all selectors that didn't match.
+ consumeError(std::move(Error));
+ continue;
+ }
+ for (auto [Location, NewPiece] :
+ llvm::zip_equal(PieceLocations, NewName.getNamePieces())) {
+ tooling::Replacement NewReplacement(
+ SM, CharSourceRange::getTokenRange(Location), NewPiece);
+ if (auto Err = FilteredChanges.add(NewReplacement))
+ return std::move(Err);
+ }
}
return FilteredChanges;
}
@@ -664,8 +694,9 @@ findOccurrencesOutsideFile(const NamedDecl &RenameDecl,
// there is no dirty buffer.
llvm::Expected<FileEdits>
renameOutsideFile(const NamedDecl &RenameDecl, llvm::StringRef MainFilePath,
- llvm::StringRef NewName, const SymbolIndex &Index,
- size_t MaxLimitFiles, llvm::vfs::FileSystem &FS) {
+ SymbolName OldName, SymbolName NewName,
+ const SymbolIndex &Index, size_t MaxLimitFiles,
+ llvm::vfs::FileSystem &FS) {
trace::Span Tracer("RenameOutsideFile");
auto AffectedFiles = findOccurrencesOutsideFile(RenameDecl, MainFilePath,
Index, MaxLimitFiles);
@@ -683,10 +714,11 @@ renameOutsideFile(const NamedDecl &RenameDecl, llvm::StringRef MainFilePath,
}
auto AffectedFileCode = (*ExpBuffer)->getBuffer();
- auto RenameRanges =
- adjustRenameRanges(AffectedFileCode, RenameDecl.getNameAsString(),
- std::move(FileAndOccurrences.second),
- RenameDecl.getASTContext().getLangOpts());
+ syntax::UnexpandedTokenBuffer Tokens(
+ AffectedFileCode, RenameDecl.getASTContext().getLangOpts());
+ std::optional<std::vector<Range>> RenameRanges =
+ adjustRenameRanges(Tokens, OldName.getNamePieces().front(),
+ std::move(FileAndOccurrences.second));
if (!RenameRanges) {
// Our heuristics fails to adjust rename ranges to the current state of
// the file, it is most likely the index is stale, so we give up the
@@ -695,8 +727,8 @@ renameOutsideFile(const NamedDecl &RenameDecl, llvm::StringRef MainFilePath,
"(the index may be stale)",
FilePath);
}
- auto RenameEdit =
- buildRenameEdit(FilePath, AffectedFileCode, *RenameRanges, NewName);
+ auto RenameEdit = buildRenameEdit(FilePath, AffectedFileCode, *RenameRanges,
+ OldName, NewName, Tokens);
if (!RenameEdit)
return error("failed to rename in file {0}: {1}", FilePath,
RenameEdit.takeError());
@@ -779,12 +811,25 @@ llvm::Expected<RenameResult> rename(const RenameInputs &RInputs) {
if (DeclsUnderCursor.size() > 1)
return makeError(ReasonToReject::AmbiguousSymbol);
const auto &RenameDecl = **DeclsUnderCursor.begin();
- const auto *ID = RenameDecl.getIdentifier();
- if (!ID)
+ DeclarationName Name = RenameDecl.getDeclName();
+ if (!Name)
return makeError(ReasonToReject::UnsupportedSymbol);
- if (ID->getName() == RInputs.NewName)
+ SymbolName OldSymbolName(Name);
+ SymbolName NewSymbolName;
+ if (RInputs.NewName) {
+ NewSymbolName = SymbolName(*RInputs.NewName, AST.getLangOpts());
+ } else {
+ // If no new name is given, we are perfoming a pseudo rename for the
+ // prepareRename request to check if the rename is possible. Construct a
+ // new symbol name that has as many name pieces as the old name and is thus
+ // a valid new name.
+ std::vector<std::string> NewNamePieces = OldSymbolName.getNamePieces();
+ NewNamePieces[0] += "__clangd_rename_placeholder";
+ NewSymbolName = SymbolName(NewNamePieces);
+ }
+ if (OldSymbolName == NewSymbolName)
return makeError(ReasonToReject::SameName);
- auto Invalid = checkName(RenameDecl, RInputs.NewName);
+ auto Invalid = checkName(RenameDecl, NewSymbolName);
if (Invalid)
return makeError(std::move(*Invalid));
@@ -802,7 +847,8 @@ llvm::Expected<RenameResult> rename(const RenameInputs &RInputs) {
// To make cross-file rename work for local symbol, we use a hybrid solution:
// - run AST-based rename on the main file;
// - run index-based rename on other affected files;
- auto MainFileRenameEdit = renameWithinFile(AST, RenameDecl, RInputs.NewName);
+ auto MainFileRenameEdit =
+ renameWithinFile(AST, RenameDecl, OldSymbolName, NewSymbolName);
if (!MainFileRenameEdit)
return MainFileRenameEdit.takeError();
@@ -827,6 +873,7 @@ llvm::Expected<RenameResult> rename(const RenameInputs &RInputs) {
}
RenameResult Result;
Result.Target = CurrentIdentifier;
+ Result.OldName = RenameDecl.getNameAsString();
Edit MainFileEdits = Edit(MainFileCode, std::move(*MainFileRenameEdit));
for (const TextEdit &TE : MainFileEdits.asTextEdits())
Result.LocalChanges.push_back(TE.range);
@@ -847,7 +894,8 @@ llvm::Expected<RenameResult> rename(const RenameInputs &RInputs) {
}
auto OtherFilesEdits = renameOutsideFile(
- RenameDecl, RInputs.MainFilePath, RInputs.NewName, *RInputs.Index,
+ RenameDecl, RInputs.MainFilePath, OldSymbolName, NewSymbolName,
+ *RInputs.Index,
Opts.LimitFiles == 0 ? std::numeric_limits<size_t>::max()
: Opts.LimitFiles,
*RInputs.FS);
@@ -860,10 +908,11 @@ llvm::Expected<RenameResult> rename(const RenameInputs &RInputs) {
return Result;
}
-llvm::Expected<Edit> buildRenameEdit(llvm::StringRef AbsFilePath,
- llvm::StringRef InitialCode,
- std::vector<Range> Occurrences,
- llvm::StringRef NewName) {
+llvm::Expected<Edit>
+buildRenameEdit(llvm::StringRef AbsFilePath, llvm::StringRef InitialCode,
+ std::vector<Range> Occurrences, SymbolName OldName,
+ SymbolName NewName,
+ const syntax::UnexpandedTokenBuffer &Tokens) {
trace::Span Tracer("BuildRenameEdit");
SPAN_ATTACH(Tracer, "file_path", AbsFilePath);
SPAN_ATTACH(Tracer, "rename_occurrences",
@@ -904,12 +953,36 @@ llvm::Expected<Edit> buildRenameEdit(llvm::StringRef AbsFilePath,
OccurrencesOffsets.push_back({*StartOffset, *EndOffset});
}
+ const SourceManager &SM = Tokens.sourceManager();
+
tooling::Replacements RenameEdit;
for (const auto &R : OccurrencesOffsets) {
- auto ByteLength = R.second - R.first;
- if (auto Err = RenameEdit.add(
- tooling::Replacement(AbsFilePath, R.first, ByteLength, NewName)))
- return std::move(Err);
+ if (std::optional<std::string> Identifier = NewName.getSinglePiece()) {
+ auto ByteLength = R.second - R.first;
+ if (auto Err = RenameEdit.add(tooling::Replacement(
+ AbsFilePath, R.first, ByteLength, *Identifier)))
+ return std::move(Err);
+ } else {
+ SmallVector<SourceLocation> PieceLocations;
+ llvm::Error Error = findObjCSymbolSelectorPieces(
+ Tokens.tokens(), SM,
+ SM.getLocForStartOfFile(SM.getMainFileID()).getLocWithOffset(R.first),
+ OldName, tooling::ObjCSymbolSelectorKind::Unknown, PieceLocations);
+ if (Error) {
+ // Ignore the error. We simply skip over all selectors that didn't
+ // match.
+ consumeError(std::move(Error));
+ continue;
+ }
+ assert(PieceLocations.size() == NewName.getNamePieces().size());
+ for (auto [Location, NewPiece] :
+ llvm::zip_equal(PieceLocations, NewName.getNamePieces())) {
+ tooling::Replacement NewReplacement(
+ SM, CharSourceRange::getTokenRange(Location), NewPiece);
+ if (auto Err = RenameEdit.add(NewReplacement))
+ return std::move(Err);
+ }
+ }
}
return Edit(InitialCode, std::move(RenameEdit));
}
@@ -928,13 +1001,13 @@ llvm::Expected<Edit> buildRenameEdit(llvm::StringRef AbsFilePath,
// were inserted). If such a "near miss" is found, the rename is still
// possible
std::optional<std::vector<Range>>
-adjustRenameRanges(llvm::St...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/78872
More information about the cfe-commits
mailing list