Skip to content
This repository was archived by the owner on Jun 1, 2023. It is now read-only.

Commit 89f5e83

Browse files
committed
Refactor isExternalSymbol to perform more general symbol resolution
1 parent 688ef0f commit 89f5e83

File tree

3 files changed

+44
-39
lines changed

3 files changed

+44
-39
lines changed

Sources/SwiftDoc/Interface.swift

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -162,35 +162,21 @@ public final class Interface {
162162

163163
// MARK: -
164164

165-
/// Whether the symbol with the given qualified name is declared inside this module or not.
166-
///
167-
/// Please note that this tool only operates on the syntax tree and does not have a semantic understanding of the
168-
/// symbols, so this method might produce false positives.
169-
///
170-
/// - Parameter name: A qualified name of a symbol.
171-
/// - Returns: Whether or not the symbol is external, meaning that it is not declared in the module.
172-
public func isExternalSymbol(named name: String) -> Bool {
173-
var nameComponents: [String] = name.split(separator: ".").map(String.init).reversed()
174-
var path: [String] = []
175-
while let nextComponent = nameComponents.popLast() {
176-
guard let symbol = findSymbol(named: nextComponent, path: path) else { return true }
177-
path.append(symbol.name)
165+
public func symbols(named name: String, resolvingTypealiases: Bool) -> [Symbol] {
166+
var pathComponents: [String] = []
167+
for component in name.split(separator: ".", omittingEmptySubsequences: true) {
168+
pathComponents.append("\(component)")
169+
guard resolvingTypealiases else { continue }
170+
171+
if let symbols = symbolsGroupedByQualifiedName[pathComponents.joined(separator: ".")],
172+
let symbol = symbols.first(where: { $0.api is Typealias }),
173+
let `typealias` = symbol.api as? Typealias,
174+
let initializedType = `typealias`.initializedType
175+
{
176+
pathComponents = (symbol.context.compactMap { ($0 as? Symbol)?.name ?? ($0 as? Extension)?.extendedType }) + [initializedType]
177+
}
178178
}
179-
return false
180-
}
181179

182-
private func findSymbol(named name: String, path immutablePath: [String]) -> Symbol? {
183-
var path = immutablePath
184-
repeat {
185-
let fullPath = (path + CollectionOfOne(name)).joined(separator: ".")
186-
if let symbol = symbolsGroupedByQualifiedName[fullPath]?.first {
187-
guard let typealiasDeclaration = symbol.api as? Typealias, let initializedName = typealiasDeclaration.initializedType else { return symbol }
188-
guard !symbol.context.isEmpty else {
189-
return findSymbol(named: initializedName, path: [])
190-
}
191-
return findSymbol(named: initializedName, path: symbol.context.compactMap { ($0 as? Symbol)?.name ?? ($0 as? Extension)?.extendedType })
192-
}
193-
} while path.popLast() != nil
194-
return nil
180+
return symbolsGroupedByQualifiedName[pathComponents.joined(separator: ".")] ?? []
195181
}
196182
}

Sources/swift-doc/Subcommands/Generate.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ extension SwiftDoc {
7979
var symbolsByExternalType: [String: [Symbol]] = [:]
8080
for symbol in module.interface.symbols.filter(symbolFilter) {
8181
guard let extensionDeclaration = symbol.context.first as? Extension, symbol.context.count == 1 else { continue }
82-
guard module.interface.isExternalSymbol(named: extensionDeclaration.extendedType) else { continue }
82+
guard module.interface.symbols(named: extensionDeclaration.extendedType, resolvingTypealiases: true).isEmpty else { continue }
8383
symbolsByExternalType[extensionDeclaration.extendedType, default: []] += [symbol]
8484
}
8585
for (typeName, symbols) in symbolsByExternalType {

Tests/SwiftDocTests/InterfaceTypeTests.swift

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -291,15 +291,34 @@ final class InterfaceTypeTests: XCTestCase {
291291
let sourceFile = try SourceFile(file: url, relativeTo: url.deletingLastPathComponent())
292292
let module = Module(name: "Module", sourceFiles: [sourceFile])
293293

294-
XCTAssertFalse(module.interface.isExternalSymbol(named: "SomeClass"))
295-
XCTAssertFalse(module.interface.isExternalSymbol(named: "SomeClass.InnerObject"))
296-
XCTAssertFalse(module.interface.isExternalSymbol(named: "MyClass"))
297-
XCTAssertFalse(module.interface.isExternalSymbol(named: "MyClass.InnerObject"))
298-
XCTAssertTrue(module.interface.isExternalSymbol(named: "UIGestureRecognizer"))
299-
XCTAssertTrue(module.interface.isExternalSymbol(named: "UIGestureRecognizer.State"))
300-
XCTAssertTrue(module.interface.isExternalSymbol(named: "ExternalClass"))
301-
XCTAssertTrue(module.interface.isExternalSymbol(named: "ExternalClass.State"))
302-
XCTAssertTrue(module.interface.isExternalSymbol(named: "SomeClass.ActuallyExternal"))
303-
XCTAssertFalse(module.interface.isExternalSymbol(named: "SomeClass.ActuallyInternal"))
294+
XCTAssertEqual(module.interface.symbols(named: "SomeClass", resolvingTypealiases: true).first?.name, "SomeClass")
295+
XCTAssertEqual(module.interface.symbols(named: "SomeClass", resolvingTypealiases: false).first?.name, "SomeClass")
296+
297+
XCTAssertEqual(module.interface.symbols(named: "SomeClass.InnerObject", resolvingTypealiases: true).first?.name, "InnerObject")
298+
XCTAssertEqual(module.interface.symbols(named: "SomeClass.InnerObject", resolvingTypealiases: false).first?.name, "InnerObject")
299+
300+
XCTAssertEqual(module.interface.symbols(named: "SomeClass.ActuallyInternal", resolvingTypealiases: true).first?.name, "InnerStruct")
301+
XCTAssertEqual(module.interface.symbols(named: "SomeClass.ActuallyInternal", resolvingTypealiases: false).first?.name, "ActuallyInternal")
302+
303+
XCTAssertEqual(module.interface.symbols(named: "MyClass", resolvingTypealiases: true).first?.name, "SomeClass")
304+
XCTAssertEqual(module.interface.symbols(named: "MyClass", resolvingTypealiases: false).first?.name, "MyClass")
305+
306+
XCTAssertEqual(module.interface.symbols(named: "MyClass.InnerObject", resolvingTypealiases: true).first?.name, "InnerObject")
307+
XCTAssertNil(module.interface.symbols(named: "MyClass.InnerObject", resolvingTypealiases: false).first)
308+
309+
XCTAssertNil(module.interface.symbols(named: "ExternalClass", resolvingTypealiases: true).first)
310+
XCTAssertTrue(module.interface.symbols(named: "ExternalClass", resolvingTypealiases: false).first?.api is Typealias)
311+
312+
XCTAssertNil(module.interface.symbols(named: "ExternalClass.State", resolvingTypealiases: true).first)
313+
XCTAssertNil(module.interface.symbols(named: "ExternalClass.State", resolvingTypealiases: false).first)
314+
315+
XCTAssertNil(module.interface.symbols(named: "SomeClass.ActuallyExternal", resolvingTypealiases: true).first)
316+
XCTAssertTrue(module.interface.symbols(named: "SomeClass.ActuallyExternal", resolvingTypealiases: false).first?.api is Typealias)
317+
318+
XCTAssertNil(module.interface.symbols(named: "UIGestureRecognizer", resolvingTypealiases: true).first)
319+
XCTAssertNil(module.interface.symbols(named: "UIGestureRecognizer", resolvingTypealiases: false).first)
320+
321+
XCTAssertNil(module.interface.symbols(named: "UIGestureRecognizer.State", resolvingTypealiases: true).first)
322+
XCTAssertNil(module.interface.symbols(named: "UIGestureRecognizer.State", resolvingTypealiases: false).first)
304323
}
305324
}

0 commit comments

Comments
 (0)