Files
SwiftLint/Source/SwiftLintFramework/Rules/Lint/CommentSpacingRule.swift
T
Danny Mösch 449190d324 Verify examples in rules by default and enforce explicit exclusion (#4065)
A rule must conform to ManuallyTestedExamplesRule to skip generation of a test for its examples.
2022-08-09 22:32:09 +02:00

168 lines
5.9 KiB
Swift

import Foundation
import SourceKittenFramework
public struct CommentSpacingRule: ConfigurationProviderRule, SubstitutionCorrectableRule {
public var configuration = SeverityConfiguration(.warning)
public init() {}
public static let description = RuleDescription(
identifier: "comment_spacing",
name: "Comment Spacing",
description: "Prefer at least one space after slashes for comments.",
kind: .lint,
nonTriggeringExamples: [
Example("""
// This is a comment
"""),
Example("""
/// Triple slash comment
"""),
Example("""
// Multiline double-slash
// comment
"""),
Example("""
/// Multiline triple-slash
/// comment
"""),
Example("""
/// Multiline triple-slash
/// - This is indented
"""),
Example("""
// - MARK: Mark comment
"""),
Example("""
//: Swift Playground prose section
"""),
Example("""
///////////////////////////////////////////////
// Comment with some lines of slashes boxing it
///////////////////////////////////////////////
"""),
Example("""
//:#localized(key: "SwiftPlaygroundLocalizedProse")
"""),
Example("""
/* Asterisk comment */
"""),
Example("""
/*
Multiline asterisk comment
*/
"""),
Example("""
/*:
Multiline Swift Playground prose section
*/
"""),
Example("""
/*#-editable-code Swift Platground editable area*/default/*#-end-editable-code*/
""")
],
triggeringExamples: [
Example("""
//↓Something
"""),
Example("""
//↓MARK
"""),
Example("""
//↓👨‍👨‍👦‍👦Something
"""),
Example("""
func a() {
//↓This needs refactoring
print("Something")
}
//↓We should improve above function
"""),
Example("""
///↓This is a comment
"""),
Example("""
/// Multiline triple-slash
///↓This line is incorrect, though
"""),
Example("""
//↓- MARK: Mark comment
"""),
Example("""
//:↓Swift Playground prose section
""")
],
corrections: [
Example("//↓Something"): Example("// Something"),
Example("//↓- MARK: Mark comment"): Example("// - MARK: Mark comment"),
Example("""
/// Multiline triple-slash
///↓This line is incorrect, though
"""): Example("""
/// Multiline triple-slash
/// This line is incorrect, though
"""),
Example("""
func a() {
//↓This needs refactoring
print("Something")
}
//↓We should improve above function
"""): Example("""
func a() {
// This needs refactoring
print("Something")
}
// We should improve above function
""")
]
)
public func violationRanges(in file: SwiftLintFile) -> [NSRange] {
// Find all comment tokens in the file and regex search them for violations
let commentTokens = file.syntaxMap.tokens.filter { token in
guard let kind = token.kind else { return false }
return SyntaxKind.commentKinds.contains(kind)
}
return commentTokens
.compactMap { (token: SwiftLintSyntaxToken) -> [NSRange]? in
return file.stringView
.substringWithByteRange(token.range)
.map(StringView.init)
.map { commentBody in
// Look for 2+ slash characters followed immediately by
// a non-colon, non-whitespace character or by a colon
// followed by a non-whitespace character other than #
regex(#"^(?:\/){2,}+(?:[^\s:]|:[^\s#])"#).matches(in: commentBody, options: .anchored)
.compactMap { result in
// Set the location to be directly before the first non-slash,
// non-whitespace character which was matched
return file.stringView.byteRangeToNSRange(
ByteRange(
// Safe to mix NSRange offsets with byte offsets here because the regex can't
// contain multi-byte characters
location: ByteCount(token.range.lowerBound.value + result.range.upperBound - 1),
length: 0
)
)
}
}
}
.flatMap { $0 }
}
public func validate(file: SwiftLintFile) -> [StyleViolation] {
return violationRanges(in: file).map { range in
StyleViolation(
ruleDescription: Self.description,
severity: configuration.severity,
location: Location(file: file, characterOffset: range.location)
)
}
}
public func substitution(for violationRange: NSRange, in file: SwiftLintFile) -> (NSRange, String)? {
return (violationRange, " ")
}
}