Skip to content

Commit f14600c

Browse files
committed
Revert diagnostic parsing and emit directly to outputStream
1 parent c48e606 commit f14600c

File tree

1 file changed

+4
-130
lines changed

1 file changed

+4
-130
lines changed

Sources/SwiftBuildSupport/SwiftBuildSystem.swift

Lines changed: 4 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ extension SwiftBuildMessage.LocationContext {
196196
}
197197

198198
/// Handler for SwiftBuildMessage events sent by the SWBBuildOperation.
199-
final class SwiftBuildSystemMessageHandler {
199+
public final class SwiftBuildSystemMessageHandler {
200200
private let observabilityScope: ObservabilityScope
201201
private let outputStream: OutputByteStream
202202
private let logLevel: Basics.Diagnostic.Severity
@@ -252,7 +252,7 @@ final class SwiftBuildSystemMessageHandler {
252252
targetsByID[target.targetID] = target
253253
}
254254

255-
mutating func target(for task: SwiftBuild.SwiftBuildMessage.TaskStartedInfo) throws -> SwiftBuild.SwiftBuildMessage.TargetStartedInfo? {
255+
func target(for task: SwiftBuild.SwiftBuildMessage.TaskStartedInfo) throws -> SwiftBuild.SwiftBuildMessage.TargetStartedInfo? {
256256
guard let id = task.targetID else {
257257
return nil
258258
}
@@ -308,91 +308,6 @@ final class SwiftBuildSystemMessageHandler {
308308
}
309309
}
310310

311-
/// Represents a parsed diagnostic segment from compiler output
312-
private struct ParsedDiagnostic {
313-
/// The file path if present
314-
let filePath: String?
315-
/// The line number if present
316-
let line: Int?
317-
/// The column number if present
318-
let column: Int?
319-
/// The severity (error, warning, note, remark)
320-
let severity: String
321-
/// The diagnostic message text
322-
let message: String
323-
/// The full text including any multi-line context (code snippets, carets, etc.)
324-
let fullText: String
325-
326-
/// Parse severity string to Diagnostic.Severity
327-
func toDiagnosticSeverity() -> Basics.Diagnostic.Severity {
328-
switch severity.lowercased() {
329-
case "error": return .error
330-
case "warning": return .warning
331-
case "note": return .info
332-
case "remark": return .debug
333-
default: return .info
334-
}
335-
}
336-
}
337-
338-
/// Split compiler output into individual diagnostic segments
339-
/// Format: /path/to/file.swift:line:column: severity: message
340-
private func splitIntoDiagnostics(_ output: String) -> [ParsedDiagnostic] {
341-
var diagnostics: [ParsedDiagnostic] = []
342-
343-
// Regex pattern to match diagnostic lines
344-
// Matches: path:line:column: severity: message (path is required)
345-
// The path must contain at least one character and line must be present
346-
let diagnosticPattern = #"^(.+?):(\d+):(?:(\d+):)?\s*(error|warning|note|remark):\s*(.*)$"#
347-
guard let regex = try? NSRegularExpression(pattern: diagnosticPattern, options: [.anchorsMatchLines]) else {
348-
return []
349-
}
350-
351-
let nsString = output as NSString
352-
let matches = regex.matches(in: output, options: [], range: NSRange(location: 0, length: nsString.length))
353-
354-
// Process each match and gather full text including subsequent lines
355-
for (index, match) in matches.enumerated() {
356-
let matchRange = match.range
357-
358-
// Extract components
359-
let filePathRange = match.range(at: 1)
360-
let lineRange = match.range(at: 2)
361-
let columnRange = match.range(at: 3)
362-
let severityRange = match.range(at: 4)
363-
let messageRange = match.range(at: 5)
364-
365-
let filePath = nsString.substring(with: filePathRange)
366-
let line = Int(nsString.substring(with: lineRange))
367-
let column = columnRange.location != NSNotFound ? Int(nsString.substring(with: columnRange)) : nil
368-
let severity = nsString.substring(with: severityRange)
369-
let message = nsString.substring(with: messageRange)
370-
371-
// Determine the full text range (from this diagnostic to the next one, or end)
372-
let startLocation = matchRange.location
373-
let endLocation: Int
374-
if index + 1 < matches.count {
375-
endLocation = matches[index + 1].range.location
376-
} else {
377-
endLocation = nsString.length
378-
}
379-
380-
let fullTextRange = NSRange(location: startLocation, length: endLocation - startLocation)
381-
let fullText = nsString.substring(with: fullTextRange).trimmingCharacters(in: .whitespacesAndNewlines)
382-
383-
diagnostics.append(ParsedDiagnostic(
384-
filePath: filePath,
385-
line: line,
386-
column: column,
387-
severity: severity,
388-
message: message,
389-
fullText: fullText
390-
))
391-
}
392-
393-
return diagnostics
394-
}
395-
396311
private func emitDiagnosticCompilerOutput(_ info: SwiftBuildMessage.TaskStartedInfo) {
397312
// Don't redundantly emit tasks.
398313
guard !self.tasksEmitted.contains(info.taskSignature) else {
@@ -406,47 +321,8 @@ final class SwiftBuildSystemMessageHandler {
406321
// Decode the buffer to a string
407322
let decodedOutput = String(decoding: buffer, as: UTF8.self)
408323

409-
// Split the output into individual diagnostic segments
410-
let parsedDiagnostics = splitIntoDiagnostics(decodedOutput)
411-
412-
if parsedDiagnostics.isEmpty {
413-
// No structured diagnostics found - emit as-is based on task signature matching
414-
// Fetch the task signature for a SwiftBuildMessage.DiagnosticInfo
415-
func getTaskSignature(from info: SwiftBuildMessage.DiagnosticInfo) -> String? {
416-
if let taskSignature = info.locationContext2.taskSignature {
417-
return taskSignature
418-
} else if let taskID = info.locationContext.taskID,
419-
let taskSignature = self.buildState.taskSignature(for: taskID)
420-
{
421-
return taskSignature
422-
}
423-
return nil
424-
}
425-
426-
// Use existing logic as fallback
427-
if unprocessedDiagnostics.compactMap(getTaskSignature).contains(where: { $0 == info.taskSignature }) {
428-
self.observabilityScope.emit(error: decodedOutput)
429-
} else {
430-
self.observabilityScope.emit(info: decodedOutput)
431-
}
432-
} else {
433-
// Process each parsed diagnostic derived from the decodedOutput
434-
for parsedDiag in parsedDiagnostics {
435-
let severity = parsedDiag.toDiagnosticSeverity()
436-
437-
// Use the appropriate emit method based on severity
438-
switch severity {
439-
case .error:
440-
self.observabilityScope.emit(error: parsedDiag.fullText)
441-
case .warning:
442-
self.observabilityScope.emit(warning: parsedDiag.fullText)
443-
case .info:
444-
self.observabilityScope.emit(info: parsedDiag.fullText)
445-
case .debug:
446-
self.observabilityScope.emit(severity: .debug, message: parsedDiag.fullText)
447-
}
448-
}
449-
}
324+
// Emit to output stream.
325+
outputStream.send(decodedOutput)
450326

451327
// Record that we've emitted the output for a given task signature.
452328
self.tasksEmitted.insert(info.taskSignature)
@@ -515,9 +391,7 @@ final class SwiftBuildSystemMessageHandler {
515391
let startedInfo = try buildState.completed(task: info)
516392

517393
// If we've captured the compiler output with formatted diagnostics, emit them.
518-
// if let buffer = buildState.dataBuffer(for: startedInfo) {
519394
emitDiagnosticCompilerOutput(startedInfo)
520-
// }
521395

522396
if info.result != .success {
523397
self.observabilityScope.emit(severity: .error, message: "\(startedInfo.ruleInfo) failed with a nonzero exit code. Command line: \(startedInfo.commandLineDisplayString ?? "<no command line>")")

0 commit comments

Comments
 (0)