Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion include/swift/AST/DiagnosticsFrontend.def
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,12 @@ ERROR(error_option_missing_required_argument, none,

ERROR(cannot_open_file,none,
"cannot open file '%0' (%1)", (StringRef, StringRef))
ERROR(cannot_open_serialized_file,none,
ERROR(cannot_open_serialized_file, none,
"cannot open file '%0' for diagnostics emission (%1)",
(StringRef, StringRef))
ERROR(cannot_serialize_diagnostics_to_sarif, none,
"cannot serialize diagnostics in SARIF format to file '%0': %1",
(StringRef, StringRef))
ERROR(error_open_input_file,none,
"error opening input file '%0' (%1)", (StringRef, StringRef))
ERROR(error_clang_importer_create_fail,none,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Bridging/ASTGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ void swift_ASTGen_renderSingleDiagnostic(
void swift_ASTGen_renderQueuedDiagnostics(
void *_Nonnull queued, ssize_t contextSize, ssize_t colorize,
BridgedStringRef *_Nonnull renderedString);
void swift_ASTGen_renderQueuedDiagnosticsAsSARIF(
void *_Nonnull queuedDiagnosticsPtr, BridgedStringRef compilerVersion,
BridgedStringRef ouptutPath, BridgedStringRef *_Nullable errorMessage);

void *_Nonnull swift_ASTGen_createPerFrontendDiagnosticState();
void swift_ASTGen_destroyPerFrontendDiagnosticState(void * _Nonnull state);
Expand Down
45 changes: 45 additions & 0 deletions include/swift/Frontend/SARIFDiagnosticConsumer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//===--- SARIFDiagnosticConsumer.h - Export Diagnostics as SARIF -*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the SARIFDiagnosticConsumer class, which exports
// diagnostics to SARIF v2.1.0 JSON format.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_SARIFDIAGNOSTICCONSUMER_H
#define SWIFT_SARIFDIAGNOSTICCONSUMER_H

#if SWIFT_BUILD_SWIFT_SYNTAX

#include <memory>

namespace llvm {
class StringRef;
}

namespace swift {

class DiagnosticConsumer;

namespace sarif_diagnostics {
/// Create a DiagnosticConsumer that exports diagnostics to SARIF format.
///
/// \param outputPath the file path to write the SARIF JSON to.
///
/// \returns A new diagnostic consumer that exports diagnostics to SARIF.
std::unique_ptr<DiagnosticConsumer> createConsumer(llvm::StringRef outputPath);
} // namespace sarif_diagnostics
} // namespace swift

#endif // SWIFT_BUILD_SWIFT_SYNTAX

#endif
2 changes: 2 additions & 0 deletions lib/ASTGen/Sources/ASTGen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ add_pure_swift_host_library(swiftASTGen STATIC CXX_INTEROP
DeclAttrs.swift
Decls.swift
Diagnostics.swift
SARIFSchema.swift
SARIFConversion.swift
DiagnosticsBridge.swift
EmbeddedSupport.swift
Exprs.swift
Expand Down
63 changes: 63 additions & 0 deletions lib/ASTGen/Sources/ASTGen/DiagnosticsBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import ASTBridging
import BasicBridging
import Foundation
import SwiftDiagnostics
import SwiftSyntax

Expand Down Expand Up @@ -139,6 +140,9 @@ struct QueuedDiagnostics {

/// The known source files
var sourceFiles: [ExportedSourceFile] = []

/// Diagnostics grouped by file name for SARIF export
var diagnosticsMap: [String: DiagnosticInfo] = [:]
}

/// Create a grouped diagnostics structure in which we can add osou
Expand Down Expand Up @@ -417,7 +421,36 @@ public func addQueuedDiagnostic(
fixIts: fixIts
)

// Add to grouped diagnostics for console rendering
queuedDiagnostics.pointee.grouped.addDiagnostic(diagnostic)

updateMap(
queuedDiagnostics: queuedDiagnostics,
sourceFile: sourceFile,
diagnostic: diagnostic
)
}

fileprivate func updateMap(
queuedDiagnostics: UnsafeMutablePointer<QueuedDiagnostics>,
sourceFile: ExportedSourceFile,
diagnostic: Diagnostic
) {
let filePath = sourceFile.fileName

// Only process if we have a SourceFileSyntax
guard let tree = sourceFile.syntax.as(SourceFileSyntax.self) else {
return
}

queuedDiagnostics.pointee.diagnosticsMap[
filePath,
default: DiagnosticInfo(
filePath: filePath,
tree: tree,
diagnostics: []
)
].add(diagnostic: diagnostic)
}

/// Render a single diagnostic that has no source location information.
Expand Down Expand Up @@ -561,3 +594,33 @@ public func renderCategoryFootnotes(
// Clear out categories so we start fresh.
state.pointee.referencedCategories = []
}

/// Export queued diagnostics as SARIF JSON.
@_cdecl("swift_ASTGen_renderQueuedDiagnosticsAsSARIF")
public func renderQueuedDiagnosticsAsSARIF(
queuedDiagnosticsPtr: UnsafeMutableRawPointer,
bridgedCompilerVersion: BridgedStringRef,
bridgedOutputPath: BridgedStringRef,
errorMessageOutPtr: UnsafeMutablePointer<BridgedStringRef>
) {

let queuedDiagnostics = queuedDiagnosticsPtr.assumingMemoryBound(to: QueuedDiagnostics.self)

let diagnosticInfoArray = Array(queuedDiagnostics.pointee.diagnosticsMap.values)

let toolInfo = ToolInfo(
name: "Swift Compiler",
version: String(bridged: bridgedCompilerVersion),
informationUri: "https://swift.org",
organization: "Swift Project"
)

let sarifLog = convertToSARIFLog(toolInfo: toolInfo, diagnosticInfoArray: diagnosticInfoArray)

do {
try sarifLog.toJSONFile(url: URL(fileURLWithPath: String(bridged: bridgedOutputPath)))
} catch {
let errorMessage = "\(error)"
errorMessageOutPtr.pointee = allocateBridgedString(errorMessage)
}
}
Loading