Files
Danny Mösch a6c4fd98bc Move files from SwiftLintCore to SwiftLintFramework
Ideally, SwiftLintCore would some day only contain components
that are needed to define rules. Consequently, it would be the
only bundle required to import for (external) rule development.
2024-12-23 12:51:43 +01:00

76 lines
2.7 KiB
Swift

import Foundation
import SourceKittenFramework
/// To some tools (i.e. Datadog), code quality findings are reported in the SARIF format:
/// - Full Spec: https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html
/// - Samples: https://github.com/microsoft/sarif-tutorials/blob/main/samples/
/// - JSON Validator: https://sarifweb.azurewebsites.net/Validation
struct SARIFReporter: Reporter {
// MARK: - Reporter Conformance
static let identifier = "sarif"
static let isRealtime = false
static let description = "Reports violations in the Static Analysis Results Interchange Format (SARIF)"
static let swiftlintVersion = "https://github.com/realm/SwiftLint/blob/\(Version.current.value)/README.md"
static func generateReport(_ violations: [StyleViolation]) -> String {
let SARIFJson = [
"version": "2.1.0",
"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
"runs": [
[
"tool": [
"driver": [
"name": "SwiftLint",
"semanticVersion": Version.current.value,
"informationUri": swiftlintVersion,
],
],
"results": violations.map(dictionary(for:)),
],
],
] as [String: Any]
return toJSON(SARIFJson, options: [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes])
}
// MARK: - Private
private static func dictionary(for violation: StyleViolation) -> [String: Any] {
[
"level": violation.severity.rawValue,
"ruleId": violation.ruleIdentifier,
"message": [
"text": violation.reason
],
"locations": [
dictionary(for: violation.location)
],
]
}
private static func dictionary(for location: Location) -> [String: Any] {
// According to SARIF specification JSON1008, minimum value for line is 1
if let line = location.line, line > 0 {
return [
"physicalLocation": [
"artifactLocation": [
"uri": location.relativeFile ?? ""
],
"region": [
"startLine": line,
"startColumn": location.character ?? 1,
],
],
]
}
return [
"physicalLocation": [
"artifactLocation": [
"uri": location.file ?? ""
],
],
]
}
}