mirror of
https://github.com/realm/SwiftLint.git
synced 2026-05-07 20:12:49 +00:00
Add rules to SARIF exporter (#6502)
Co-authored-by: Danny Mösch <danny.moesch@icloud.com>
This commit is contained in:
@@ -20,6 +20,11 @@
|
||||
[claudeaceae](https://github.com/claudeaceae)
|
||||
[#6487](https://github.com/realm/SwiftLint/issues/6487)
|
||||
|
||||
* Add `rules` array to SARIF reporter output, providing metadata for all
|
||||
built-in rules in accordance with the SARIF specification.
|
||||
[ahmadalfy](https://github.com/ahmadalfy)
|
||||
[#6499](https://github.com/realm/SwiftLint/issues/6499)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Ensure that disable commands work for `redundant_nil_coalescing` rule.
|
||||
|
||||
@@ -23,6 +23,9 @@ struct SARIFReporter: Reporter {
|
||||
"name": "SwiftLint",
|
||||
"semanticVersion": Version.current.value,
|
||||
"informationUri": swiftlintVersion,
|
||||
"rules": RuleRegistry.shared.list.list.values
|
||||
.sorted { $0.identifier < $1.identifier }
|
||||
.map(dictionary(for:)),
|
||||
],
|
||||
],
|
||||
"results": violations.map(dictionary(for:)),
|
||||
@@ -35,6 +38,20 @@ struct SARIFReporter: Reporter {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private static func dictionary(for ruleType: any Rule.Type) -> [String: Any] {
|
||||
let description = ruleType.description
|
||||
return [
|
||||
"id": description.identifier,
|
||||
"shortDescription": [
|
||||
"text": description.name
|
||||
],
|
||||
"fullDescription": [
|
||||
"text": description.description
|
||||
],
|
||||
"helpUri": "https://realm.github.io/SwiftLint/\(description.identifier).html",
|
||||
]
|
||||
}
|
||||
|
||||
private static func dictionary(for violation: StyleViolation) -> [String: Any] {
|
||||
[
|
||||
"level": violation.severity.rawValue,
|
||||
|
||||
@@ -117,11 +117,46 @@ final class ReporterTests: SwiftLintTestCase {
|
||||
|
||||
func testSARIFReporter() throws {
|
||||
try assertEqualContent(
|
||||
referenceFile: "CannedSARIFReporterOutput.json",
|
||||
reporterType: SARIFReporter.self
|
||||
referenceFile: "CannedSARIFReporterOutput.sarif",
|
||||
reporterType: SARIFReporter.self,
|
||||
stringConverter: { try sarifValueWithoutRules($0) }
|
||||
)
|
||||
}
|
||||
|
||||
private func sarifValueWithoutRules(_ jsonString: String) throws -> NSObject {
|
||||
let data = jsonString.data(using: .utf8)!
|
||||
guard var json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
|
||||
queuedFatalError("Expected dictionary in SARIF JSON")
|
||||
}
|
||||
|
||||
// Extract and verify rules before removing them
|
||||
if var runs = json["runs"] as? [[String: Any]],
|
||||
var firstRun = runs.first,
|
||||
var tool = firstRun["tool"] as? [String: Any],
|
||||
var driver = tool["driver"] as? [String: Any],
|
||||
let rules = driver["rules"] as? [[String: Any]] {
|
||||
// Verify rules array is not empty
|
||||
XCTAssertFalse(rules.isEmpty, "Rules array should not be empty")
|
||||
|
||||
// Verify rule structure
|
||||
if let firstRule = rules.first {
|
||||
XCTAssertNotNil(firstRule["id"], "Rule should have id")
|
||||
XCTAssertNotNil(firstRule["shortDescription"], "Rule should have shortDescription")
|
||||
XCTAssertNotNil(firstRule["fullDescription"], "Rule should have fullDescription")
|
||||
XCTAssertNotNil(firstRule["helpUri"], "Rule should have helpUri")
|
||||
}
|
||||
|
||||
// Remove rules array for comparison
|
||||
driver.removeValue(forKey: "rules")
|
||||
tool["driver"] = driver
|
||||
firstRun["tool"] = tool
|
||||
runs[0] = firstRun
|
||||
json["runs"] = runs
|
||||
}
|
||||
|
||||
return json.bridge()
|
||||
}
|
||||
|
||||
func testJunitReporter() throws {
|
||||
try assertEqualContent(
|
||||
referenceFile: "CannedJunitReporterOutput.xml",
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
{
|
||||
"$schema" : "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
|
||||
"runs" : [
|
||||
{
|
||||
"results" : [
|
||||
{
|
||||
"level" : "warning",
|
||||
"locations" : [
|
||||
{
|
||||
"physicalLocation" : {
|
||||
"artifactLocation" : {
|
||||
"uri" : "filename"
|
||||
},
|
||||
"region" : {
|
||||
"startColumn" : 1,
|
||||
"startLine" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message" : {
|
||||
"text" : "Violation Reason 1"
|
||||
},
|
||||
"ruleId" : "line_length"
|
||||
},
|
||||
{
|
||||
"level" : "error",
|
||||
"locations" : [
|
||||
{
|
||||
"physicalLocation" : {
|
||||
"artifactLocation" : {
|
||||
"uri" : "filename"
|
||||
},
|
||||
"region" : {
|
||||
"startColumn" : 1,
|
||||
"startLine" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message" : {
|
||||
"text" : "Violation Reason 2"
|
||||
},
|
||||
"ruleId" : "line_length"
|
||||
},
|
||||
{
|
||||
"level" : "error",
|
||||
"locations" : [
|
||||
{
|
||||
"physicalLocation" : {
|
||||
"artifactLocation" : {
|
||||
"uri" : "path/file.swift"
|
||||
},
|
||||
"region" : {
|
||||
"startColumn" : 2,
|
||||
"startLine" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message" : {
|
||||
"text" : "Shorthand syntactic sugar should be used, i.e. [Int] instead of Array<Int>"
|
||||
},
|
||||
"ruleId" : "syntactic_sugar"
|
||||
},
|
||||
{
|
||||
"level" : "error",
|
||||
"locations" : [
|
||||
{
|
||||
"physicalLocation" : {
|
||||
"artifactLocation" : {
|
||||
"uri" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message" : {
|
||||
"text" : "Colons should be next to the identifier when specifying a type and next to the key in dictionary literals"
|
||||
},
|
||||
"ruleId" : "colon"
|
||||
}
|
||||
],
|
||||
"tool" : {
|
||||
"driver" : {
|
||||
"informationUri" : "https://github.com/realm/SwiftLint/blob/${SWIFTLINT_VERSION}/README.md",
|
||||
"name" : "SwiftLint",
|
||||
"semanticVersion" : "${SWIFTLINT_VERSION}"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : "2.1.0"
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
{
|
||||
"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json",
|
||||
"runs": [
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"level": "warning",
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "filename"
|
||||
},
|
||||
"region": {
|
||||
"startColumn": 1,
|
||||
"startLine": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": {
|
||||
"text": "Violation Reason 1"
|
||||
},
|
||||
"ruleId": "line_length"
|
||||
},
|
||||
{
|
||||
"level": "error",
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "filename"
|
||||
},
|
||||
"region": {
|
||||
"startColumn": 1,
|
||||
"startLine": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": {
|
||||
"text": "Violation Reason 2"
|
||||
},
|
||||
"ruleId": "line_length"
|
||||
},
|
||||
{
|
||||
"level": "error",
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "path/file.swift"
|
||||
},
|
||||
"region": {
|
||||
"startColumn": 2,
|
||||
"startLine": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": {
|
||||
"text": "Shorthand syntactic sugar should be used, i.e. [Int] instead of Array<Int>"
|
||||
},
|
||||
"ruleId": "syntactic_sugar"
|
||||
},
|
||||
{
|
||||
"level": "error",
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": {
|
||||
"text": "Colons should be next to the identifier when specifying a type and next to the key in dictionary literals"
|
||||
},
|
||||
"ruleId": "colon"
|
||||
}
|
||||
],
|
||||
"tool": {
|
||||
"driver": {
|
||||
"informationUri": "https://github.com/realm/SwiftLint/blob/${SWIFTLINT_VERSION}/README.md",
|
||||
"name": "SwiftLint",
|
||||
"semanticVersion": "${SWIFTLINT_VERSION}"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": "2.1.0"
|
||||
}
|
||||
Reference in New Issue
Block a user