mirror of
https://github.com/realm/SwiftLint.git
synced 2026-05-07 20:12:49 +00:00
114 lines
3.5 KiB
Swift
114 lines
3.5 KiB
Swift
import SwiftSyntax
|
|
|
|
@SwiftSyntaxRule
|
|
struct OrphanedDocCommentRule: Rule {
|
|
var configuration = SeverityConfiguration<Self>(.warning)
|
|
|
|
static let description = RuleDescription(
|
|
identifier: "orphaned_doc_comment",
|
|
name: "Orphaned Doc Comment",
|
|
description: "A doc comment should be attached to a declaration",
|
|
kind: .lint,
|
|
nonTriggeringExamples: [
|
|
Example("""
|
|
/// My great property
|
|
var myGreatProperty: String!
|
|
"""),
|
|
Example("""
|
|
//////////////////////////////////////
|
|
//
|
|
// Copyright header.
|
|
//
|
|
//////////////////////////////////////
|
|
"""),
|
|
Example("""
|
|
/// Look here for more info: https://github.com.
|
|
var myGreatProperty: String!
|
|
"""),
|
|
Example("""
|
|
/// Look here for more info:
|
|
/// https://github.com.
|
|
var myGreatProperty: String!
|
|
"""),
|
|
],
|
|
triggeringExamples: [
|
|
Example("""
|
|
↓/// My great property
|
|
// 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
|
|
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!
|
|
}
|
|
"""),
|
|
]
|
|
)
|
|
}
|
|
|
|
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
|
|
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))
|
|
}
|
|
}
|
|
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|