Skip to content

Add API to get the language in which a symbol is defined and the symbol provider for a source file #177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 2, 2024
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
45 changes: 45 additions & 0 deletions Sources/IndexStoreDB/IndexStoreDB.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public struct PathMapping {
}
}

public enum SymbolProviderKind {
case clang
case swift
}

/// IndexStoreDB index.
public final class IndexStoreDB {

Expand Down Expand Up @@ -148,13 +153,18 @@ public final class IndexStoreDB {
return indexstoredb_index_remove_unit_out_file_paths(impl, cPaths, cPaths.count, waitForProcessing)
}

/// Invoke `body` with every occurrance of `usr` in one of the specified roles.
///
/// Stop iteration if `body` returns `false`.
/// - Returns: `false` if iteration was terminated by `body` returning `true` or `true` if iteration finished.
@discardableResult
public func forEachSymbolOccurrence(byUSR usr: String, roles: SymbolRole, _ body: @escaping (SymbolOccurrence) -> Bool) -> Bool {
return indexstoredb_index_symbol_occurrences_by_usr(impl, usr, roles.rawValue) { occur in
return body(SymbolOccurrence(occur))
}
}

/// Returns all occurrences of `usr` in one of the specified roles.
public func occurrences(ofUSR usr: String, roles: SymbolRole) -> [SymbolOccurrence] {
var result: [SymbolOccurrence] = []
forEachSymbolOccurrence(byUSR: usr, roles: roles) { occur in
Expand Down Expand Up @@ -275,6 +285,41 @@ public final class IndexStoreDB {
return result
}

public func symbolProvider(for sourceFilePath: String) -> SymbolProviderKind? {
var result: SymbolProviderKind? = nil
indexstoredb_index_units_containing_file(impl, sourceFilePath) { unit in
let providerKind: SymbolProviderKind? = switch indexstoredb_unit_info_symbol_provider_kind(unit) {
case INDEXSTOREDB_SYMBOL_PROVIDER_KIND_SWIFT:
.swift
case INDEXSTOREDB_SYMBOL_PROVIDER_KIND_CLANG:
.clang
case INDEXSTOREDB_SYMBOL_PROVIDER_KIND_UNKNOWN:
nil
default:
preconditionFailure("Unknown enum case in indexstoredb_symbol_provider_kind_t")
}

let mainFilePath = String(cString: indexstoredb_unit_info_main_file_path(unit))
if providerKind == .swift && mainFilePath != sourceFilePath {
// We have a unit that is "included" from Swift. This happens for header
// files that Swift files depend on. But Swift doesn't have includes and
// we shouldn't infer the header file's language to Swift based on this unit.
// Ignore it.
return true
}

if result == nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this worth it? Would be a pretty weird state to be in, may as well just return the first.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don’t have strong opinions either way. indexstoredb_index_units_containing_file also shouldn’t return many files so not sure if exiting early really makes a difference.

result = providerKind
} else if result != providerKind {
// Found two conflicting provider kinds. Return nil as we don't know the provider in this case.
result = nil
return false
}
return true
}
return result
}

@discardableResult
public func foreachFileIncludedByFile(path: String, body: @escaping (String) -> Bool) -> Bool {
return indexstoredb_index_files_included_by_file(impl, path) { targetPath, line in
Expand Down
55 changes: 48 additions & 7 deletions Sources/IndexStoreDB/Symbol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,33 @@ public enum IndexSymbolKind: Hashable {
case commentTag
}

public enum Language: Hashable {
case c
case cxx
case objc
case swift
}

public struct Symbol: Equatable {

public var usr: String
public var name: String
public var kind: IndexSymbolKind
public var properties: SymbolProperty
public var language: Language

public init(usr: String, name: String, kind: IndexSymbolKind, properties: SymbolProperty = SymbolProperty()) {
public init(
usr: String,
name: String,
kind: IndexSymbolKind,
properties: SymbolProperty = SymbolProperty(),
language: Language
) {
self.usr = usr
self.name = name
self.kind = kind
self.properties = properties
self.language = language
}
}

Expand All @@ -69,9 +84,9 @@ extension Symbol: Comparable {
extension Symbol: CustomStringConvertible {
public var description: String {
if properties.isEmpty {
return "\(name) | \(kind) | \(usr)"
return "\(name) | \(kind) | \(usr) | \(language)"
}
return "\(name) | \(kind) (\(properties)) | \(usr)"
return "\(name) | \(kind) (\(properties)) | \(usr) | \(language)"
}
}

Expand All @@ -82,9 +97,16 @@ extension Symbol {
name: String? = nil,
usr: String? = nil,
kind: IndexSymbolKind? = nil,
properties: SymbolProperty? = nil) -> Symbol
{
return Symbol(usr: usr ?? self.usr, name: name ?? self.name, kind: kind ?? self.kind, properties: properties ?? self.properties)
properties: SymbolProperty? = nil,
language: Language? = nil
) -> Symbol {
return Symbol(
usr: usr ?? self.usr,
name: name ?? self.name,
kind: kind ?? self.kind,
properties: properties ?? self.properties,
language: language ?? self.language
)
}

/// Returns a SymbolOccurrence with the given location and roles.
Expand All @@ -95,6 +117,23 @@ extension Symbol {

// MARK: CIndexStoreDB conversions

fileprivate extension Language {
init(_ value: indexstoredb_language_t) {
switch value {
case INDEXSTOREDB_LANGUAGE_C:
self = .c
case INDEXSTOREDB_LANGUAGE_CXX:
self = .cxx
case INDEXSTOREDB_LANGUAGE_OBJC:
self = .objc
case INDEXSTOREDB_LANGUAGE_SWIFT:
self = .swift
default:
preconditionFailure("Unhandled case from C enum indexstoredb_language_t")
}
}
}

extension Symbol {

/// Note: `value` is expected to be passed +1.
Expand All @@ -103,7 +142,9 @@ extension Symbol {
usr: String(cString: indexstoredb_symbol_usr(value)),
name: String(cString: indexstoredb_symbol_name(value)),
kind: IndexSymbolKind(indexstoredb_symbol_kind(value)),
properties: SymbolProperty(rawValue: indexstoredb_symbol_properties(value)))
properties: SymbolProperty(rawValue: indexstoredb_symbol_properties(value)),
language: Language(indexstoredb_symbol_language(value))
)
}
}

Expand Down
Loading