Skip to content

Commit e2c790d

Browse files
author
Jesse Haigh
committed
validate array values in code block options for highlight and strikeout
1 parent aaf2b97 commit e2c790d

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

Sources/SwiftDocC/Checker/Checkers/InvalidCodeBlockOption.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,36 @@ public struct InvalidCodeBlockOption: Checker {
5858
}
5959
}
6060

61+
func validateArrayIndices(token: RenderBlockContent.CodeListing.OptionName, value: String?) {
62+
guard token == .highlight || token == .strikeout, let value = value else { return }
63+
// code property ends in a newline. this gives us a bogus extra line.
64+
let lineCount: Int = codeBlock.code.split(omittingEmptySubsequences: false, whereSeparator: { $0.isNewline }).count - 1
65+
66+
guard let indices = parseCodeBlockOptionArray(value) else {
67+
let diagnostic = Diagnostic(source: sourceFile, severity: .warning, range: codeBlock.range, identifier: "org.swift.docc.InvalidCodeBlockOption", summary: "Could not parse \(token.rawValue.singleQuoted) indices from \(value.singleQuoted). Expected an integer (e.g. 3) or an array (e.g. [1, 3, 5])")
68+
problems.append(Problem(diagnostic: diagnostic, possibleSolutions: []))
69+
return
70+
}
71+
72+
let invalid = indices.filter { $0 < 1 || $0 > lineCount }
73+
guard !invalid.isEmpty else { return }
74+
75+
let diagnostic = Diagnostic(source: sourceFile, severity: .warning, range: codeBlock.range, identifier: "org.swift.docc.InvalidCodeBlockOption", summary: "Invalid \(token.rawValue.singleQuoted) index\(invalid.count == 1 ? "" : "es") in \(value.singleQuoted) for a code block with \(lineCount) line\(lineCount == 1 ? "" : "s"). Valid range is 1...\(lineCount).")
76+
let solutions: [Solution] = {
77+
if invalid.contains(where: {$0 == lineCount + 1}) {
78+
return [Solution(
79+
summary: "If you intended the last line, change '\(lineCount + 1)' to \(lineCount).",
80+
replacements: []
81+
)]
82+
}
83+
return []
84+
}()
85+
problems.append(Problem(diagnostic: diagnostic, possibleSolutions: solutions))
86+
}
87+
6188
for (token, value) in tokens {
6289
matches(token: token, value: value)
90+
validateArrayIndices(token: token, value: value)
6391
}
6492
// check if first token (lang) might be a typo
6593
matches(token: .unknown, value: lang)

Tests/SwiftDocCTests/Checker/Checkers/InvalidCodeBlockOptionTests.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,41 @@ let b = 2
122122
XCTAssertEqual(problem.possibleSolutions.map(\.summary), ["If 'swift' is the language for this code block, then write 'swift' as the first option."])
123123
}
124124
}
125+
126+
func testInvalidHighlightIndex() throws {
127+
let markupSource = """
128+
```swift, nocopy, highlight=[2]
129+
let b = 2
130+
```
131+
"""
132+
let document = Document(parsing: markupSource, options: [])
133+
var checker = InvalidCodeBlockOption(sourceFile: URL(fileURLWithPath: #file))
134+
checker.visit(document)
135+
XCTAssertEqual(1, checker.problems.count)
136+
let problem = try XCTUnwrap(checker.problems.first)
137+
138+
XCTAssertEqual("org.swift.docc.InvalidCodeBlockOption", problem.diagnostic.identifier)
139+
XCTAssertEqual(problem.diagnostic.summary, "Invalid 'highlight' index in '[2]' for a code block with 1 line. Valid range is 1...1.")
140+
XCTAssertEqual(problem.possibleSolutions.map(\.summary), ["If you intended the last line, change '2' to 1."])
141+
}
142+
143+
func testInvalidHighlightandStrikeoutIndex() throws {
144+
let markupSource = """
145+
```swift, nocopy, highlight=[0], strikeout=[-1, 4]
146+
let a = 1
147+
let b = 2
148+
let c = 3
149+
```
150+
"""
151+
let document = Document(parsing: markupSource, options: [])
152+
var checker = InvalidCodeBlockOption(sourceFile: URL(fileURLWithPath: #file))
153+
checker.visit(document)
154+
XCTAssertEqual(2, checker.problems.count)
155+
156+
XCTAssertEqual("org.swift.docc.InvalidCodeBlockOption", checker.problems[0].diagnostic.identifier)
157+
XCTAssertEqual(checker.problems[0].diagnostic.summary, "Invalid 'highlight' index in '[0]' for a code block with 3 lines. Valid range is 1...3.")
158+
XCTAssertEqual(checker.problems[1].diagnostic.summary, "Invalid 'strikeout' indexes in '[-1, 4]' for a code block with 3 lines. Valid range is 1...3.")
159+
XCTAssertEqual(checker.problems[1].possibleSolutions.map(\.summary), ["If you intended the last line, change '4' to 3."])
160+
}
125161
}
126162

0 commit comments

Comments
 (0)