Skip to content

Commit 8eeb3d9

Browse files
sam-mccalltstellar
authored andcommitted
[clangd] Rename: merge index/AST refs path-insensitively where needed
If you have c:\foo open, and C:\foo indexed (case difference) then these need to be considered the same file. Otherwise we emit edits to both, and editors do... something that isn't pretty. Maybe more centralized normalization is called for, but it's not trivial to do this while also being case-preserving. see clangd/clangd#108 Fixes clangd/clangd#665 Differential Revision: https://reviews.llvm.org/D95759 (cherry picked from commit b63cd4d)
1 parent 76e4c93 commit 8eeb3d9

File tree

6 files changed

+85
-16
lines changed

6 files changed

+85
-16
lines changed

clang-tools-extra/clangd/GlobalCompilationDatabase.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -396,20 +396,6 @@ DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const {
396396
return None;
397397
}
398398

399-
// For platforms where paths are case-insensitive (but case-preserving),
400-
// we need to do case-insensitive comparisons and use lowercase keys.
401-
// FIXME: Make Path a real class with desired semantics instead.
402-
// This class is not the only place this problem exists.
403-
// FIXME: Mac filesystems default to case-insensitive, but may be sensitive.
404-
405-
static std::string maybeCaseFoldPath(PathRef Path) {
406-
#if defined(_WIN32) || defined(__APPLE__)
407-
return Path.lower();
408-
#else
409-
return std::string(Path);
410-
#endif
411-
}
412-
413399
std::vector<DirectoryBasedGlobalCompilationDatabase::DirectoryCache *>
414400
DirectoryBasedGlobalCompilationDatabase::getDirectoryCaches(
415401
llvm::ArrayRef<llvm::StringRef> Dirs) const {

clang-tools-extra/clangd/refactor/Rename.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ llvm::Optional<std::string> getOtherRefFile(const Decl &D, StringRef MainFile,
6868
if (OtherFile)
6969
return;
7070
if (auto RefFilePath = filePath(R.Location, /*HintFilePath=*/MainFile)) {
71-
if (*RefFilePath != MainFile)
71+
if (!pathEqual(*RefFilePath, MainFile))
7272
OtherFile = *RefFilePath;
7373
}
7474
});
@@ -474,7 +474,7 @@ findOccurrencesOutsideFile(const NamedDecl &RenameDecl,
474474
if ((R.Kind & RefKind::Spelled) == RefKind::Unknown)
475475
return;
476476
if (auto RefFilePath = filePath(R.Location, /*HintFilePath=*/MainFile)) {
477-
if (*RefFilePath != MainFile)
477+
if (!pathEqual(*RefFilePath, MainFile))
478478
AffectedFiles[*RefFilePath].push_back(toRange(R.Location));
479479
}
480480
});

clang-tools-extra/clangd/support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_clang_library(clangdSupport
2323
Logger.cpp
2424
Markup.cpp
2525
MemoryTree.cpp
26+
Path.cpp
2627
Shutdown.cpp
2728
Threading.cpp
2829
ThreadsafeFS.cpp
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- Path.cpp -------------------------------------------*- C++-*------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "support/Path.h"
10+
namespace clang {
11+
namespace clangd {
12+
13+
std::string maybeCaseFoldPath(PathRef Path) {
14+
#if defined(_WIN32) || defined(__APPLE__)
15+
return Path.lower();
16+
#else
17+
return std::string(Path);
18+
#endif
19+
}
20+
21+
bool pathEqual(PathRef A, PathRef B) {
22+
#if defined(_WIN32) || defined(__APPLE__)
23+
return A.equals_lower(B);
24+
#else
25+
return A == B;
26+
#endif
27+
}
28+
29+
} // namespace clangd
30+
} // namespace clang

clang-tools-extra/clangd/support/Path.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ using Path = std::string;
2222
/// signatures.
2323
using PathRef = llvm::StringRef;
2424

25+
// For platforms where paths are case-insensitive (but case-preserving),
26+
// we need to do case-insensitive comparisons and use lowercase keys.
27+
// FIXME: Make Path a real class with desired semantics instead.
28+
std::string maybeCaseFoldPath(PathRef Path);
29+
bool pathEqual(PathRef, PathRef);
30+
2531
} // namespace clangd
2632
} // namespace clang
2733

clang-tools-extra/clangd/unittests/RenameTests.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,52 @@ TEST(RenameTest, Renameable) {
10671067
}
10681068
}
10691069

1070+
MATCHER_P(newText, T, "") { return arg.newText == T; }
1071+
1072+
TEST(RenameTest, IndexMergeMainFile) {
1073+
Annotations Code("int ^x();");
1074+
TestTU TU = TestTU::withCode(Code.code());
1075+
TU.Filename = "main.cc";
1076+
auto AST = TU.build();
1077+
1078+
auto Main = testPath("main.cc");
1079+
1080+
auto Rename = [&](const SymbolIndex *Idx) {
1081+
auto GetDirtyBuffer = [&](PathRef Path) -> llvm::Optional<std::string> {
1082+
return Code.code().str(); // Every file has the same content.
1083+
};
1084+
RenameOptions Opts;
1085+
Opts.AllowCrossFile = true;
1086+
RenameInputs Inputs{Code.point(), "xPrime", AST, Main,
1087+
Idx, Opts, GetDirtyBuffer};
1088+
auto Results = rename(Inputs);
1089+
EXPECT_TRUE(bool(Results)) << llvm::toString(Results.takeError());
1090+
return std::move(*Results);
1091+
};
1092+
1093+
// We do not expect to see duplicated edits from AST vs index.
1094+
auto Results = Rename(TU.index().get());
1095+
EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
1096+
EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
1097+
ElementsAre(newText("xPrime")));
1098+
1099+
// Sanity check: we do expect to see index results!
1100+
TU.Filename = "other.cc";
1101+
Results = Rename(TU.index().get());
1102+
EXPECT_THAT(Results.GlobalChanges.keys(),
1103+
UnorderedElementsAre(Main, testPath("other.cc")));
1104+
1105+
#if defined(_WIN32) || defined(__APPLE__)
1106+
// On case-insensitive systems, no duplicates if AST vs index case differs.
1107+
// https://github.com/clangd/clangd/issues/665
1108+
TU.Filename = "MAIN.CC";
1109+
Results = Rename(TU.index().get());
1110+
EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
1111+
EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
1112+
ElementsAre(newText("xPrime")));
1113+
#endif
1114+
}
1115+
10701116
TEST(RenameTest, MainFileReferencesOnly) {
10711117
// filter out references not from main file.
10721118
llvm::StringRef Test =

0 commit comments

Comments
 (0)