From 076061f7560771e58b8c1dc36a7350f3de153155 Mon Sep 17 00:00:00 2001 From: aryansk Date: Sun, 23 Aug 2026 18:53:18 +0530 Subject: [PATCH] Fix analyze handling of Xcode 26 @response files Xcode 26 passes swiftc arguments via @response files. The log parser's expandingResponseFiles only split response files by newlines, so space-separated files (Xcode 26) were treated as a single argument and no source files were discovered, causing analyze to report 0 violations with exit 0. Fix by reusing parseCLIArguments for response files so both newline-separated (Xcode 25) and space-separated (Xcode 26) layouts are handled, including quoted paths and nested @ files. Also fix parseCLIArguments to correctly handle leading quotes and to split on whitespace/newlines instead of only spaces, and to filter empty components. Fixes #6877 --- .../CompilerArgumentsExtractor.swift | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/Source/SwiftLintFramework/CompilerArgumentsExtractor.swift b/Source/SwiftLintFramework/CompilerArgumentsExtractor.swift index 4689048d9c..a997aa3ee6 100644 --- a/Source/SwiftLintFramework/CompilerArgumentsExtractor.swift +++ b/Source/SwiftLintFramework/CompilerArgumentsExtractor.swift @@ -22,19 +22,33 @@ private func parseCLIArguments(_ string: String) -> [String] { let scanner = Scanner(string: string) var str = "" var didStart = false - while let result = scanner.scanUpToString("\"") { + while !scanner.isAtEnd { + var result: String? = scanner.scanUpToString("\"") + if result == nil { + result = "" + } if didStart { - str += result.replacingOccurrences(of: " ", with: escapedSpacePlaceholder) + str += result!.replacingOccurrences(of: " ", with: escapedSpacePlaceholder) str += " " } else { - str += result + str += result! + } + if scanner.scanString("\"") != nil { + didStart.toggle() + } else { + let remaining = String(scanner.string[scanner.currentIndex...]) + if didStart { + str += remaining.replacingOccurrences(of: " ", with: escapedSpacePlaceholder) + } else { + str += remaining + } + break } - _ = scanner.scanString("\"") - didStart.toggle() } - return str.trimmingCharacters(in: .whitespaces) + return str.trimmingCharacters(in: .whitespacesAndNewlines) .replacingOccurrences(of: "\\ ", with: escapedSpacePlaceholder) - .components(separatedBy: " ") + .components(separatedBy: .whitespacesAndNewlines) + .filter { !$0.isEmpty } .map { $0.replacingOccurrences(of: escapedSpacePlaceholder, with: " ") } } @@ -65,9 +79,12 @@ extension Array where Element == String { } let responseFile = String(arg.dropFirst()) return (try? String(contentsOf: URL(filePath: responseFile, directoryHint: .notDirectory))).flatMap { - $0.trimmingCharacters(in: .newlines) - .components(separatedBy: "\n") - .expandingResponseFiles + // Response files may contain arguments separated by whitespace + // (spaces, newlines) with quoting for paths containing spaces. + // Reuse the CLI parser so both Xcode 25 (newline-separated) and + // Xcode 26 (space-separated via @response files) layouts are + // handled, including nested @ files. + parseCLIArguments($0).expandingResponseFiles } ?? [arg] } }