1
0
Fork 0

Fix code quality: add doc comments, document DAG assumption, clarify implicitImports

This commit is contained in:
Brendan Szymanski 2026-07-01 20:14:19 -04:00
parent 9eab7ffc72
commit cf9f762155

View file

@ -1,8 +1,11 @@
import Foundation
/// Result of analyzing multiple GIR repositories together.
///
/// Resolves the full cross-package dependency graph and determines
/// which `import` statements each generated file needs.
/// which `import` statements each generated file needs. Used as input
/// to per-package code generation (via `packageConfigs`) and for
/// producing `@_exported import` umbrella re-exports in each module.
public struct MultiPackageAnalysis {
/// Maps Swift module name parsed Repository.
public let repositories: [String: Repository]
@ -22,11 +25,22 @@ public struct MultiPackageAnalysis {
/// dependencies (i.e., modules accessed transitively through a direct
/// dep that re-exports them). Always empty in the re-export approach
/// because transitive types come through re-exports.
///
/// Reserved for non-re-export approaches where transitive types require
/// explicit imports. Currently empty in the re-export lattice design.
public let implicitImports: [String: Set<String>]
/// Per-package `GenerationConfig` for driving per-type code generation.
public let packageConfigs: [String: GenerationConfig]
/// Creates a new multi-package analysis result.
///
/// - Parameters:
/// - repositories: Parsed GIR repositories keyed by Swift module name.
/// - directDependencies: Direct dependencies derived from `<include>` elements.
/// - transitiveDependencies: Transitive closure for `@_exported import` re-exports.
/// - implicitImports: Implicit imports (empty in the re-export lattice design).
/// - packageConfigs: Per-package `GenerationConfig` for code generation.
public init(repositories: [String: Repository],
directDependencies: [String: Set<String>],
transitiveDependencies: [String: Set<String>],
@ -43,13 +57,27 @@ public struct MultiPackageAnalysis {
/// Analyzes multiple GIR `Repository` instances to produce a
/// `MultiPackageAnalysis` with resolved cross-package imports.
public struct MultiPackageAnalyzer {
/// Configuration describing the monorepo's packages and their GIR paths.
public let config: MonorepoConfig
/// Creates a new multi-package analyzer.
///
/// - Parameter config: The monorepo configuration listing all packages to analyze.
public init(config: MonorepoConfig) {
self.config = config
}
/// Parses all GIR files and resolves the dependency graph.
///
/// Each package's `.gir` file is parsed, direct dependencies are extracted
/// from `<include>` elements, and transitive closures are computed via
/// memoized depth-first traversal assuming a DAG (no dependency cycles).
/// Returns a fully resolved `MultiPackageAnalysis` with per-package
/// `GenerationConfig` instances suitable for code generation.
///
/// - Throws: `ParserError` if any `.gir` file is malformed or unreadable.
/// - Returns: A `MultiPackageAnalysis` with parsed repositories,
/// dependency mappings, and generation configurations.
public func analyze() throws -> MultiPackageAnalysis {
var repositories: [String: Repository] = [:]
var directDeps: [String: Set<String>] = [:]
@ -72,6 +100,22 @@ public struct MultiPackageAnalyzer {
// direct dep's transitive deps, transitively.
var transitiveDeps: [String: Set<String>] = [:]
/// The dependency graph is a DAG (no cycles in GIR include chains).
/// If cycles were present, `visited` detection would cut traversal
/// and the cached result would be incomplete. This implementation
/// is correct for the DAG case only.
///
/// Computes the transitive closure of dependencies for a module.
/// Uses memoization (`transitiveDeps` cache) to avoid recomputation
/// and a `visited` set to detect cycles (belt-and-suspenders for a
/// DAG-only graph). The result is: all modules reachable from
/// `module` by following direct dependencies transitively, minus
/// `module` itself.
///
/// - Parameters:
/// - module: The Swift module name to compute transitive deps for.
/// - visited: Set of modules currently on the DFS stack (for cycle detection).
/// - Returns: The transitive dependency set for `module`.
func computeTransitive(for module: String, visited: inout Set<String>) -> Set<String> {
if let cached = transitiveDeps[module] { return cached }
visited.insert(module)
@ -93,8 +137,9 @@ public struct MultiPackageAnalyzer {
_ = computeTransitive(for: moduleName, visited: &visited)
}
// implicitImports: in a re-export lattice there are no implicit
// imports every needed module is reached via transitive re-exports.
// implicitImports: reserved for non-re-export approaches where
// transitive types require explicit imports. Currently empty in
// the re-export lattice design.
var implicitImports: [String: Set<String>] = [:]
for moduleName in config.packages.map(\.name) {
implicitImports[moduleName] = []