forked from qt-creator/qt-creator
LanguageClient: Prevent duplicate references due to file aliasing
This problem has been observed with clangd, but it's probably a good idea to apply the check generally. Note that in the case of renaming, omitting the filtering can lead to file corruption. Task-number: QTCREATORBUG-30546 Change-Id: I007edbae2cba5f59e427ab07e183162df9e99367 Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
committed by
David Schulz
parent
e3302afd09
commit
5afdc47760
@@ -291,12 +291,17 @@ void ClangdFindReferences::Private::handleFindUsagesResult(const QList<Location>
|
|||||||
|
|
||||||
for (const Location &loc : locations)
|
for (const Location &loc : locations)
|
||||||
fileData[loc.uri()].rangesAndLineText.push_back({loc.range(), {}});
|
fileData[loc.uri()].rangesAndLineText.push_back({loc.range(), {}});
|
||||||
|
QSet<FilePath> canonicalFilePaths;
|
||||||
for (auto it = fileData.begin(); it != fileData.end();) {
|
for (auto it = fileData.begin(); it != fileData.end();) {
|
||||||
const Utils::FilePath filePath = client()->serverUriToHostPath(it.key());
|
const Utils::FilePath filePath = client()->serverUriToHostPath(it.key());
|
||||||
if (!filePath.exists()) { // https://github.com/clangd/clangd/issues/935
|
if (!filePath.exists()) { // https://github.com/clangd/clangd/issues/935
|
||||||
it = fileData.erase(it);
|
it = fileData.erase(it);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!Utils::insert(canonicalFilePaths, filePath.canonicalPath())) { // QTCREATORBUG-30546
|
||||||
|
it = fileData.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const QStringList lines = SymbolSupport::getFileContents(filePath);
|
const QStringList lines = SymbolSupport::getFileContents(filePath);
|
||||||
it->fileContent = lines.join('\n');
|
it->fileContent = lines.join('\n');
|
||||||
for (auto &rangeWithText : it.value().rangesAndLineText) {
|
for (auto &rangeWithText : it.value().rangesAndLineText) {
|
||||||
|
@@ -274,6 +274,10 @@ struct ItemData
|
|||||||
Utils::Text::Range range;
|
Utils::Text::Range range;
|
||||||
QVariant userData;
|
QVariant userData;
|
||||||
};
|
};
|
||||||
|
bool operator==(const ItemData &id1, const ItemData &id2)
|
||||||
|
{
|
||||||
|
return id1.range == id2.range && id1.userData == id2.userData;
|
||||||
|
}
|
||||||
|
|
||||||
QStringList SymbolSupport::getFileContents(const Utils::FilePath &filePath)
|
QStringList SymbolSupport::getFileContents(const Utils::FilePath &filePath)
|
||||||
{
|
{
|
||||||
@@ -342,15 +346,32 @@ Utils::SearchResultItems generateSearchResultItems(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using ItemDataPerPath = QMap<Utils::FilePath, QList<ItemData>>;
|
||||||
|
void filterFileAliases(ItemDataPerPath &itemDataPerPath)
|
||||||
|
{
|
||||||
|
QSet<Utils::FilePath> canonicalPaths;
|
||||||
|
for (auto it = itemDataPerPath.begin(); it != itemDataPerPath.end(); ) {
|
||||||
|
const Utils::FilePath canonicalPath = it.key().canonicalPath();
|
||||||
|
if (!Utils::insert(canonicalPaths, canonicalPath)
|
||||||
|
&& it.value() == itemDataPerPath.value(canonicalPath)) { // QTCREATORBUG-30546
|
||||||
|
it = itemDataPerPath.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Utils::SearchResultItems generateSearchResultItems(
|
Utils::SearchResultItems generateSearchResultItems(
|
||||||
const LanguageClientArray<Location> &locations, const DocumentUri::PathMapper &pathMapper)
|
const LanguageClientArray<Location> &locations, const DocumentUri::PathMapper &pathMapper)
|
||||||
{
|
{
|
||||||
if (locations.isNull())
|
if (locations.isNull())
|
||||||
return {};
|
return {};
|
||||||
QMap<Utils::FilePath, QList<ItemData>> rangesInDocument;
|
ItemDataPerPath rangesInDocument;
|
||||||
for (const Location &location : locations.toList())
|
for (const Location &location : locations.toList()) {
|
||||||
rangesInDocument[location.uri().toFilePath(pathMapper)]
|
rangesInDocument[location.uri().toFilePath(pathMapper)]
|
||||||
<< ItemData{SymbolSupport::convertRange(location.range()), {}};
|
<< ItemData{SymbolSupport::convertRange(location.range()), {}};
|
||||||
|
}
|
||||||
|
filterFileAliases(rangesInDocument);
|
||||||
return generateSearchResultItems(rangesInDocument);
|
return generateSearchResultItems(rangesInDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,7 +573,7 @@ Utils::SearchResultItems generateReplaceItems(const WorkspaceEdit &edits,
|
|||||||
return ItemData{SymbolSupport::convertRange(edit.range()), QVariant(edit)};
|
return ItemData{SymbolSupport::convertRange(edit.range()), QVariant(edit)};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
QMap<Utils::FilePath, QList<ItemData>> rangesInDocument;
|
ItemDataPerPath rangesInDocument;
|
||||||
auto documentChanges = edits.documentChanges().value_or(QList<DocumentChange>());
|
auto documentChanges = edits.documentChanges().value_or(QList<DocumentChange>());
|
||||||
if (!documentChanges.isEmpty()) {
|
if (!documentChanges.isEmpty()) {
|
||||||
for (const DocumentChange &documentChange : std::as_const(documentChanges)) {
|
for (const DocumentChange &documentChange : std::as_const(documentChanges)) {
|
||||||
@@ -588,6 +609,7 @@ Utils::SearchResultItems generateReplaceItems(const WorkspaceEdit &edits,
|
|||||||
for (auto it = changes.begin(), end = changes.end(); it != end; ++it)
|
for (auto it = changes.begin(), end = changes.end(); it != end; ++it)
|
||||||
rangesInDocument[it.key().toFilePath(pathMapper)] = convertEdits(it.value());
|
rangesInDocument[it.key().toFilePath(pathMapper)] = convertEdits(it.value());
|
||||||
}
|
}
|
||||||
|
filterFileAliases(rangesInDocument);
|
||||||
items += generateSearchResultItems(rangesInDocument, search, limitToProjects);
|
items += generateSearchResultItems(rangesInDocument, search, limitToProjects);
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user