1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
public import SWBUtil
public final class ClangModuleVerifierOutputParser: TaskOutputParser {
private let task: any ExecutableTask
private let payload: (any ClangModuleVerifierPayloadType)?
public let workspaceContext: WorkspaceContext
public let buildRequestContext: BuildRequestContext
public let delegate: any TaskOutputParserDelegate
public init(for task: any ExecutableTask, workspaceContext: WorkspaceContext, buildRequestContext: BuildRequestContext, delegate: any TaskOutputParserDelegate, progressReporter: (any SubtaskProgressReporter)?) {
self.task = task
self.workspaceContext = workspaceContext
self.buildRequestContext = buildRequestContext
self.delegate = delegate
self.payload = task.payload as? (any ClangModuleVerifierPayloadType)
}
public func write(bytes: ByteString) {
// Forward the unparsed bytes immediately (without line buffering).
delegate.emitOutput(bytes)
// Disable diagnostic scraping, since we use serialized diagnostics.
}
public func close(result: TaskResult?) {
defer {
delegate.close()
}
// Don't try to read diagnostics if the process crashed or got cancelled as they were almost certainly not written in this case.
if result.shouldSkipParsingDiagnostics { return }
var serializedDiagnostics = task.type.serializedDiagnosticsPaths(task, workspaceContext.fs).flatMap { delegate.readSerializedDiagnostics(at: $0, workingDirectory: task.workingDirectory, workspaceContext: workspaceContext) }
// Filter unwanted diagnostics.
serializedDiagnostics.removeAll { diag in
// Never discard diagnostics that were originally errors.
if diag.behavior == .error {
return false
}
// Filter diagnostics that are not specific to the verified module.
// Most unrelated warnings are handled by -Wsystem-headers-in-module.
let message = diag.data.description
if case .path(let path, _) = diag.location {
if path.str.contains(#/(/usr/include/|/usr/local/include|/usr/lib)/#) {
return true
}
if path.basename == "Test.h" && message.hasPrefix("missing submodule") {
return true
}
}
if message.contains(#//[\\w.]+\\.xctoolchain/usr/lib/clang/[\\d.]+/include//#) {
return true
}
if message.contains(".sdk/usr/include/c++/") {
return true
}
return false
}
if serializedDiagnostics.isEmpty {
return
}
let filenameMap: ModuleVerifierFilenameMap
if let path = payload?.fileNameMapPath {
filenameMap = ModuleVerifierFilenameMap(from: path, fs: workspaceContext.fs)
} else {
filenameMap = ModuleVerifierFilenameMap()
}
for diag in serializedDiagnostics {
let updated = filenameMap.updateDiagnostic(diag)
delegate.diagnosticsEngine.emit(updated)
}
}
}
extension ModuleVerifierFilenameMap {
func updateDiagnostic(_ diag: Diagnostic) -> Diagnostic {
let childDiagnostics = diag.childDiagnostics.map { updateDiagnostic($0) }
let fixits = diag.fixIts.map { updateFixit($0) }
var location = diag.location
if case .path(let path, fileLocation: let fileLoc) = location,
let mappedFilename = map(filename: path.str) {
location = .path(Path(mappedFilename), fileLocation: fileLoc)
}
return diag.with(location: location, fixIts: fixits, childDiagnostics: childDiagnostics)
}
func updateFixit(_ fixit: Diagnostic.FixIt) -> Diagnostic.FixIt {
if let mappedPath = map(filename: fixit.sourceRange.path.str) {
return Diagnostic.FixIt(sourceRange: fixit.sourceRange.with(path: Path(mappedPath)), newText: fixit.textToInsert)
}
return fixit
}
}
|