From 7790706ffdfbfcf62d9a6c7c41a382110bcf7be9 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 25 Jul 2024 14:09:24 -0700 Subject: [PATCH] Log the options with which SourceKit-LSP is launched --- .../CustomLogStringConvertible.swift | 52 +++++++++++++++++++ Sources/SKOptions/SourceKitLSPOptions.swift | 50 ++++++++++++++++-- Sources/SourceKitLSP/SourceKitLSPServer.swift | 3 ++ Tests/SKLoggingTests/LoggingTests.swift | 26 ++++++++++ 4 files changed, 126 insertions(+), 5 deletions(-) diff --git a/Sources/SKLogging/CustomLogStringConvertible.swift b/Sources/SKLogging/CustomLogStringConvertible.swift index 4bc0363af..43ab56c42 100644 --- a/Sources/SKLogging/CustomLogStringConvertible.swift +++ b/Sources/SKLogging/CustomLogStringConvertible.swift @@ -85,3 +85,55 @@ extension Optional where Wrapped: CustomLogStringConvertible { return CustomLogStringConvertibleWrapper(OptionalWrapper(optional: self)) } } + +/// A JSON-like description of the object. +public func recursiveDescription(of subject: Any) -> String { + return "{" + + Mirror(reflecting: subject).children.map { (key, value) in + "\(key ?? ""): \(String(describing: value))" + }.joined(separator: ", ") + + "}" +} + +fileprivate protocol OptionalProtocol { + associatedtype Wrapped + var asOptional: Wrapped? { get } +} +extension Optional: OptionalProtocol { + var asOptional: Wrapped? { self } +} + +/// A JSON-like description of the object that shows trivial values but redacts values that might contain sensitive +/// information. +public func recursiveRedactedDescription(of value: Any) -> String { + switch value { + case let value as Bool: + return value.description + case let value as Int: + return value.description + case let value as Double: + return value.description + case let value as String: + return value.hashForLogging + case let value as any CustomLogStringConvertible: + return value.redactedDescription + case let value as any OptionalProtocol: + if let value = value.asOptional { + return recursiveRedactedDescription(of: value) + } else { + return "nil" + } + default: + break + } + + let children = Mirror(reflecting: value).children.sorted { $0.label ?? "" < $1.label ?? "" } + if !children.isEmpty { + return "{" + + children.map { (label, value) -> String in + "\(label ?? ""): \(recursiveRedactedDescription(of: value))" + }.joined(separator: ", ") + + "}" + } + return "" +} diff --git a/Sources/SKOptions/SourceKitLSPOptions.swift b/Sources/SKOptions/SourceKitLSPOptions.swift index ccd00528b..2ac7ff954 100644 --- a/Sources/SKOptions/SourceKitLSPOptions.swift +++ b/Sources/SKOptions/SourceKitLSPOptions.swift @@ -20,8 +20,8 @@ import struct TSCBasic.AbsolutePath /// Options that can be used to modify SourceKit-LSP's behavior. /// /// See `ConfigurationFile.md` for a description of the configuration file's behavior. -public struct SourceKitLSPOptions: Sendable, Codable { - public struct SwiftPMOptions: Sendable, Codable { +public struct SourceKitLSPOptions: Sendable, Codable, CustomLogStringConvertible { + public struct SwiftPMOptions: Sendable, Codable, CustomLogStringConvertible { /// Build configuration (debug|release). /// /// Equivalent to SwiftPM's `--configuration` option. @@ -88,9 +88,17 @@ public struct SourceKitLSPOptions: Sendable, Codable { linkerFlags: override?.linkerFlags ?? base.linkerFlags ) } + + public var description: String { + recursiveDescription(of: self) + } + + public var redactedDescription: String { + recursiveRedactedDescription(of: self) + } } - public struct CompilationDatabaseOptions: Sendable, Codable { + public struct CompilationDatabaseOptions: Sendable, Codable, CustomLogStringConvertible { /// Additional paths to search for a compilation database, relative to a workspace root. public var searchPaths: [String]? @@ -104,9 +112,17 @@ public struct SourceKitLSPOptions: Sendable, Codable { ) -> CompilationDatabaseOptions { return CompilationDatabaseOptions(searchPaths: override?.searchPaths ?? base.searchPaths) } + + public var description: String { + recursiveDescription(of: self) + } + + public var redactedDescription: String { + recursiveRedactedDescription(of: self) + } } - public struct FallbackBuildSystemOptions: Sendable, Codable { + public struct FallbackBuildSystemOptions: Sendable, Codable, CustomLogStringConvertible { public var cCompilerFlags: [String]? public var cxxCompilerFlags: [String]? public var swiftCompilerFlags: [String]? @@ -131,9 +147,17 @@ public struct SourceKitLSPOptions: Sendable, Codable { swiftCompilerFlags: override?.swiftCompilerFlags ?? base.swiftCompilerFlags ) } + + public var description: String { + recursiveDescription(of: self) + } + + public var redactedDescription: String { + recursiveRedactedDescription(of: self) + } } - public struct IndexOptions: Sendable, Codable { + public struct IndexOptions: Sendable, Codable, CustomLogStringConvertible { public var indexStorePath: String? public var indexDatabasePath: String? public var indexPrefixMap: [String: String]? @@ -176,6 +200,14 @@ public struct SourceKitLSPOptions: Sendable, Codable { updateIndexStoreTimeout: override?.updateIndexStoreTimeout ?? base.updateIndexStoreTimeout ) } + + public var description: String { + recursiveDescription(of: self) + } + + public var redactedDescription: String { + recursiveRedactedDescription(of: self) + } } public enum BackgroundPreparationMode: String { @@ -392,4 +424,12 @@ public struct SourceKitLSPOptions: Sendable, Codable { forKey: CodingKeys.workDoneProgressDebounceDuration ) } + + public var description: String { + recursiveDescription(of: self) + } + + public var redactedDescription: String { + recursiveRedactedDescription(of: self) + } } diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 8038e7676..31320bb62 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -897,6 +897,7 @@ extension SourceKitLSPServer { .appendingPathComponent("config.json") ) ) + logger.log("Creating workspace at \(workspaceFolder.uri.forLogging) with options: \(options.forLogging)") let buildSystem = await createBuildSystem( rootUri: workspaceFolder.uri, options: options, @@ -998,6 +999,8 @@ extension SourceKitLSPServer { override: orLog("Parsing SourceKitLSPOptions", { try SourceKitLSPOptions(fromLSPAny: req.initializationOptions) }) ) + logger.log("Initialized SourceKit-LSP with options: \(self.options.forLogging)") + await workspaceQueue.async { [testHooks] in if let workspaceFolders = req.workspaceFolders { self.workspacesAndIsImplicit += await workspaceFolders.asyncCompactMap { diff --git a/Tests/SKLoggingTests/LoggingTests.swift b/Tests/SKLoggingTests/LoggingTests.swift index 92f571e86..1cafa0f8b 100644 --- a/Tests/SKLoggingTests/LoggingTests.swift +++ b/Tests/SKLoggingTests/LoggingTests.swift @@ -212,4 +212,30 @@ final class LoggingTests: XCTestCase { $0.log("got \(LogStringConvertible().forLogging)") } } + + func testRecursiveRedactedDescription() { + struct Outer { + struct Inner { + var publicValue: Int + var redactedValue: String + } + var inner: Inner + } + + XCTAssertEqual( + recursiveRedactedDescription(of: Outer(inner: Outer.Inner(publicValue: 42, redactedValue: "password"))), + """ + {inner: {publicValue: 42, redactedValue: MD5 digest: 5f4dcc3b5aa765d61d8327deb882cf99}} + """ + ) + + XCTAssertEqual(recursiveRedactedDescription(of: (nil as Int?) as Any), "nil") + + XCTAssertEqual(recursiveRedactedDescription(of: (42 as Int?) as Any), "42") + + XCTAssertEqual( + recursiveRedactedDescription(of: ("abc" as String?) as Any), + "MD5 digest: 900150983cd24fb0d6963f7d28e17f72" + ) + } }