Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions Sources/SourceKitLSP/SourceKitLSPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2464,21 +2464,32 @@ extension SourceKitLSPServer {
guard let index = await workspaceForDocument(uri: req.textDocument.uri)?.index(checkedFor: .deletedFiles) else {
return nil
}
let usrs =
symbols
.filter {
// Only include references to type. For example, we don't want to find the type hierarchy of a constructor when
// starting the type hierarchy on `Foo()``.
switch $0.kind {
case .class, .enum, .interface, .struct: return true
default: return false
}
let usrs = symbols.filter {
// Only include references to type. For example, we don't want to find the type hierarchy of a constructor when
// starting the type hierarchy on `Foo()`.
// Consider a symbol a class if its kind is `nil`, eg. for a symbol returned by clang's SymbolInfo, which
// doesn't support the `kind` field.
Comment on lines +2470 to +2471
Copy link
Contributor

@bnbarham bnbarham Aug 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it 🤔? This is our extension isn't it? I'm fine with this change regardless, since we still filter from the index.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, would probably be nice if we added the kind field to the clangd request.

switch $0.kind {
case .class, .enum, .interface, .struct, nil: return true
default: return false
}
.compactMap(\.usr)
}.compactMap(\.usr)

let typeHierarchyItems = usrs.compactMap { (usr) -> TypeHierarchyItem? in
guard let info = index.primaryDefinitionOrDeclarationOccurrence(ofUSR: usr) else {
return nil
}
// Filter symbols based on their kind in the index since the filter on the symbol info response might have
// returned `nil` for the kind, preventing us from doing any filtering there.
switch info.symbol.kind {
case .unknown, .macro, .function, .variable, .field, .enumConstant, .instanceMethod, .classMethod, .staticMethod,
.instanceProperty, .classProperty, .staticProperty, .constructor, .destructor, .conversionFunction, .parameter,
.concept, .commentTag:
return nil
case .module, .namespace, .namespaceAlias, .enum, .struct, .class, .protocol, .extension, .union, .typealias,
.using:
break
}
return self.indexToLSPTypeHierarchyItem(
definition: info,
moduleName: info.location.moduleName,
Expand Down
41 changes: 41 additions & 0 deletions Tests/SourceKitLSPTests/TypeHierarchyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import LanguageServerProtocol
import SKTestSupport
import SwiftExtensions
import TSCBasic
import XCTest

Expand Down Expand Up @@ -248,6 +249,46 @@ final class TypeHierarchyTests: XCTestCase {
]
)
}

func testClangTypeHierarchy() async throws {
let project = try await SwiftPMTestProject(
files: [
"MyLibrary/include/empty.h": "",
"MyLibrary/Test.cpp": """
class Foo {};

class Bar: 1️⃣Foo {};
""",
],
enableBackgroundIndexing: true
)
let (uri, positions) = try project.openDocument("Test.cpp", language: .cpp)
let response = try await project.testClient.send(
TypeHierarchyPrepareRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
)
XCTAssertEqual(response?.count, 1)
}

func testClangTypeHierarchyInitiatedFromFunction() async throws {
let project = try await SwiftPMTestProject(
files: [
"MyLibrary/include/empty.h": "",
"MyLibrary/Test.cpp": """
void hello() {}

void test() {
1️⃣hello();
}
""",
],
enableBackgroundIndexing: true
)
let (uri, positions) = try project.openDocument("Test.cpp", language: .cpp)
let response = try await project.testClient.send(
TypeHierarchyPrepareRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
)
XCTAssertNil(response)
}
}

// MARK: - Utilities
Expand Down