Skip to content

Commit

Permalink
Rewrite orphaned_doc_comment rule with SwiftSyntax (#5417)
Browse files Browse the repository at this point in the history
  • Loading branch information
swiftty authored Jan 21, 2024
1 parent 1e9428a commit 2be8846
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
* `multiline_literal_brackets`
* `nimble_operator`
* `opening_brace`
* `orphaned_doc_comment`
* `trailing_closure`
* `void_return`

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import SwiftIDEUtils
import SwiftSyntax

struct OrphanedDocCommentRule: SourceKitFreeRule {
@SwiftSyntaxRule
struct OrphanedDocCommentRule: Rule {
var configuration = SeverityConfiguration<Self>(.warning)

static let description = RuleDescription(
Expand Down Expand Up @@ -40,36 +41,73 @@ struct OrphanedDocCommentRule: SourceKitFreeRule {
↓/// Look here for more info: https://github.com.
// Not a doc string
var myGreatProperty: String!
"""),
Example("""
↓/// Look here for more info: https://github.com.
// Not a doc string
var myGreatProperty: String!
"""),
Example("""
↓/// Look here for more info: https://github.com.
// Not a doc string
↓/// My great property
// Not a doc string
var myGreatProperty: String!
"""),
Example("""
extension Nested {
↓///
/// Look here for more info: https://github.com.
// Not a doc string
var myGreatProperty: String!
}
""")
]
)
}

func validate(file: SwiftLintFile) -> [StyleViolation] {
file.syntaxClassifications
.filter { $0.kind != .none }
.pairs()
.compactMap { first, second in
let firstByteRange = first.range.toSourceKittenByteRange()
guard
let second,
first.kind == .docLineComment || first.kind == .docBlockComment,
second.kind == .lineComment || second.kind == .blockComment,
let firstString = file.stringView.substringWithByteRange(firstByteRange),
private extension OrphanedDocCommentRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
override func visitPost(_ node: TokenSyntax) {
let pieces = node.leadingTrivia.pieces
var iterator = pieces.enumerated().makeIterator()
while let (index, piece) = iterator.next() {
switch piece {
case .docLineComment(let comment), .docBlockComment(let comment):
// These patterns are often used for "file header" style comments
!firstString.starts(with: "////") && !firstString.starts(with: "/***")
else {
return nil
}
if !comment.hasPrefix("////") && !comment.hasPrefix("/***") {
if isOrphanedDocComment(with: &iterator) {
let utf8Length = pieces[..<index].reduce(0) { $0 + $1.sourceLength.utf8Length }
violations.append(node.position.advanced(by: utf8Length))
}
}

return StyleViolation(ruleDescription: Self.description,
severity: configuration.severity,
location: Location(file: file, byteOffset: firstByteRange.location))
default:
break
}
}
}
}
}

private extension Sequence {
func pairs() -> Zip2Sequence<Self, [Element?]> {
return zip(self, Array(dropFirst()) + [nil])
private func isOrphanedDocComment(
with iterator: inout some IteratorProtocol<(offset: Int, element: TriviaPiece)>
) -> Bool {
while let (_, piece) = iterator.next() {
switch piece {
case .docLineComment, .docBlockComment,
.carriageReturns, .carriageReturnLineFeeds, .newlines, .spaces:
break

case .lineComment, .blockComment:
return true

default:
return false
}
}
return false
}

0 comments on commit 2be8846

Please sign in to comment.