Improve detection of comment-only lines in file_length rule (#6231)

This commit is contained in:
Danny Mösch
2025-09-04 10:27:26 +02:00
committed by GitHub
parent 764a9bbeb1
commit b58b8401a0
3 changed files with 11 additions and 48 deletions
+4
View File
@@ -59,6 +59,10 @@
[#6220](https://github.com/realm/SwiftLint/issues/6220)
[#6219](https://github.com/realm/SwiftLint/issues/6219)
* Improve detection of comment-only lines in `file_length` rule.
[SimplyDanny](https://github.com/SimplyDanny)
[#6219](https://github.com/realm/SwiftLint/issues/6219)
### Bug Fixes
* Fix `closure_end_indentation` rule reporting violations when the called base
@@ -23,7 +23,10 @@ struct FileLengthRule: Rule {
private extension FileLengthRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
override func visitPost(_ node: SourceFileSyntax) {
let lineCount = configuration.ignoreCommentOnlyLines ? countNonCommentLines(in: node) : file.lines.count
let lineCount = configuration.ignoreCommentOnlyLines
? CommentLinesVisitor(locationConverter: locationConverter)
.walk(tree: node, handler: \.linesWithCode).count
: file.lines.count
let severity: ViolationSeverity, upperBound: Int
if let error = configuration.severityConfiguration.error, lineCount > error {
@@ -52,51 +55,5 @@ private extension FileLengthRule {
)
violations.append(violation)
}
private func countNonCommentLines(in node: SourceFileSyntax) -> Int {
var linesWithActualContent = Set<Int>()
for token in node.tokens(viewMode: .sourceAccurate) {
addTokenContentLines(token, to: &linesWithActualContent)
// Process leading trivia
addTriviaLines(token.leadingTrivia, startingAt: token.position, to: &linesWithActualContent)
}
return linesWithActualContent.count
}
private func addTokenContentLines(_ token: TokenSyntax, to lines: inout Set<Int>) {
// Skip tokens whose text is empty or only whitespace
// (e.g., EOF token, or an unlikely malformed token).
guard !token.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
let startLocation = locationConverter.location(for: token.positionAfterSkippingLeadingTrivia)
let endLocation = locationConverter.location(for: token.endPositionBeforeTrailingTrivia)
addLinesInRange(from: startLocation.line, to: endLocation.line, to: &lines)
}
private func addTriviaLines(
_ trivia: Trivia,
startingAt startPosition: AbsolutePosition,
to lines: inout Set<Int>
) {
var currentPosition = startPosition
for piece in trivia {
if !piece.isComment, !piece.isWhitespace {
let startLocation = locationConverter.location(for: currentPosition)
let endLocation = locationConverter.location(for: currentPosition + piece.sourceLength)
addLinesInRange(from: startLocation.line, to: endLocation.line, to: &lines)
}
currentPosition += piece.sourceLength
}
}
private func addLinesInRange(from startLine: Int, to endLine: Int, to lines: inout Set<Int>) {
guard startLine > 0, startLine <= endLine else { return }
for line in startLine...endLine {
lines.insert(line)
}
}
}
}
@@ -5,7 +5,9 @@ public final class CommentLinesVisitor: SyntaxVisitor {
private let locationConverter: SourceLocationConverter
private var linesWithComments = Set<Int>()
private var linesWithCode = Set<Int>()
/// Lines that contain actual code (not comments).
public private(set) var linesWithCode = Set<Int>()
/// Lines that contain only comments (and whitespace).
public var commentOnlyLines: Set<Int> {