Skip to content

Commit 06e9f18

Browse files
committed
Added DeclComments, updated tests
1 parent 8f8896f commit 06e9f18

File tree

7 files changed

+222
-154
lines changed

7 files changed

+222
-154
lines changed

Sources/SwiftDocCoverage/AccessLevel.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ public enum AccessLevel: Int {
4848

4949
init(modifiers: DeclModifierListSyntax) {
5050
for modifier in modifiers {
51+
// Skip private(set) etc.
52+
guard modifier.detail == nil else {
53+
continue
54+
}
55+
5156
if let accessLevel = AccessLevel(token: modifier.name) {
5257
self = accessLevel
5358
return
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// DeclComment.swift
2+
//
3+
// Created by Iurii Khvorost <iurii.khvorost@gmail.com> on 11.02.2024.
4+
// Copyright © 2022 Iurii Khvorost. All rights reserved.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files (the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions:
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
24+
import SwiftSyntax
25+
26+
27+
struct DeclComment {
28+
let piece: TriviaPiece
29+
30+
var text: String {
31+
switch piece {
32+
case .lineComment(let text): return text
33+
case .blockComment(let text): return text
34+
case .docLineComment(let text): return text
35+
case .docBlockComment(let text): return text
36+
default: return ""
37+
}
38+
}
39+
40+
var isDoc: Bool {
41+
switch piece {
42+
case .docLineComment:
43+
fallthrough
44+
case .docBlockComment:
45+
return true
46+
default:
47+
return true
48+
}
49+
}
50+
51+
init(piece: TriviaPiece) {
52+
self.piece = piece
53+
}
54+
}
55+
56+
struct DeclComments {
57+
private let pieces: [DeclComment]
58+
59+
var count: Int { pieces.count }
60+
61+
var documented: Bool {
62+
pieces.first { $0.isDoc } != nil
63+
}
64+
65+
subscript(index: Int) -> DeclComment { pieces[index] }
66+
67+
init(trivia: Trivia) {
68+
pieces = trivia.compactMap {
69+
switch $0 {
70+
case .lineComment:
71+
fallthrough
72+
case .blockComment:
73+
fallthrough
74+
case .docLineComment:
75+
fallthrough
76+
case .docBlockComment:
77+
return DeclComment(piece: $0)
78+
default:
79+
return nil
80+
}
81+
}
82+
}
83+
}

Sources/SwiftDocCoverage/DeclKeyword.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ enum DeclKeyword: String {
6565
case .`init`: self = .`init`
6666
case .let: self = .let
6767
case .macro: self = .macro
68-
case .operator: self = .operator
68+
//case .operator:
6969
case .precedencegroup: self = .precedencegroup
7070
case .protocol: self = .protocol
7171
case .struct: self = .struct

Sources/SwiftDocCoverage/DeclProtocol.swift

Lines changed: 12 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,71 +24,26 @@
2424
import SwiftSyntax
2525

2626

27-
struct Comment {
28-
enum Kind {
29-
case line
30-
case block
31-
case docLine
32-
case docBlock
33-
}
34-
35-
let kind: Kind
36-
let text: String
37-
38-
var isDoc: Bool { kind == .docLine || kind == .docBlock }
39-
}
40-
4127
protocol DeclProtocol: SyntaxProtocol {
4228

4329
// var attributes: AttributeListSyntax
4430
var modifiers: DeclModifierListSyntax { get }
4531
var keyword: DeclKeyword { get }
4632
var name: TokenSyntax { get }
33+
4734
var genericParameterClause: GenericParameterClauseSyntax? { get }
4835
var inheritanceClause: InheritanceClauseSyntax? { get }
4936
var funcSignature: FunctionSignatureSyntax? { get }
50-
//var genericWhereClause: GenericWhereClauseSyntax? { get }
51-
52-
var id: String { get }
37+
var genericWhereClause: GenericWhereClauseSyntax? { get }
5338
}
5439

5540
extension DeclProtocol {
56-
5741
var genericParameterClause: GenericParameterClauseSyntax? { nil }
5842
var inheritanceClause: InheritanceClauseSyntax? { nil }
5943
var funcSignature: FunctionSignatureSyntax? { nil }
44+
var genericWhereClause: GenericWhereClauseSyntax? { nil }
6045

61-
var id: String {
62-
63-
let generic = genericParameterClause?.trimmedDescription ?? ""
64-
let base = "\(keyword.rawValue) \(name.trimmedDescription)\(generic)"
65-
66-
if let inheritance = inheritanceClause?.trimmedDescription {
67-
return "\(base): \(inheritance)"
68-
}
69-
else if let funcSignature = funcSignature?.trimmedDescription {
70-
return "\(base)\(funcSignature)"
71-
}
72-
return base
73-
}
74-
75-
var comments: [Comment] {
76-
return leadingTrivia.compactMap {
77-
switch $0 {
78-
case let .lineComment(text):
79-
return Comment(kind: .line, text: text)
80-
case let .blockComment(text):
81-
return Comment(kind: .block, text: text)
82-
case let .docLineComment(text):
83-
return Comment(kind: .docLine, text: text)
84-
case let .docBlockComment(text):
85-
return Comment(kind: .docBlock, text: text)
86-
default:
87-
return nil
88-
}
89-
}
90-
}
91-
46+
var comments: DeclComments { DeclComments(trivia: leadingTrivia) }
9247
var accessLevel: AccessLevel { AccessLevel(modifiers: modifiers) }
9348
}
9449

@@ -133,15 +88,21 @@ extension InitializerDeclSyntax: DeclProtocol {
13388

13489
var name: TokenSyntax {
13590
let optionalMark = optionalMark?.trimmedDescription ?? ""
136-
return TokenSyntax(.identifier(optionalMark), presence: .present)
91+
return TokenSyntax(.identifier("init\(optionalMark)"), presence: .present)
13792
}
93+
94+
var funcSignature: FunctionSignatureSyntax? { signature }
13895
}
13996

14097
extension SubscriptDeclSyntax: DeclProtocol {
14198
var keyword: DeclKeyword { .subscript }
14299

143100
var name: TokenSyntax {
144-
TokenSyntax(.identifier("\(parameterClause.trimmedDescription) \(returnClause.trimmedDescription)"), presence: .present)
101+
TokenSyntax(.identifier("subscript"), presence: .present)
102+
}
103+
104+
var funcSignature: FunctionSignatureSyntax? {
105+
FunctionSignatureSyntax(parameterClause: parameterClause, effectSpecifiers: nil, returnClause: returnClause)
145106
}
146107
}
147108

Sources/SwiftDocCoverage/Declaration.swift

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,54 @@
2121
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2222
// THE SOFTWARE.
2323

24-
import Foundation
2524
import SwiftSyntax
2625

27-
fileprivate extension String {
28-
29-
static let regexNewLine = try! NSRegularExpression(pattern: #"\n\s*"#, options: [])
26+
27+
@resultBuilder
28+
fileprivate struct StringBuilder {
29+
static func buildBlock(_ parts: String...) -> String {
30+
parts.joined()
31+
}
3032

31-
func refine() -> String {
32-
let range = NSRange(startIndex..., in: self)
33-
return Self.regexNewLine.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: "")
33+
static func buildOptional(_ component: String?) -> String {
34+
component ?? ""
3435
}
3536
}
3637

37-
3838
class Declaration {
39-
let decl: DeclProtocol
40-
let context: [DeclProtocol]
39+
private let decl: DeclProtocol
40+
private let context: [DeclProtocol]
41+
4142
let line: Int
4243
let column: Int
4344

44-
lazy var accessLevel: AccessLevel = { decl.accessLevel }()
45-
46-
lazy var comments: [Comment] = { decl.comments }()
47-
lazy var hasDoc: Bool = { comments.contains { $0.isDoc } }()
45+
private(set) lazy var accessLevel: AccessLevel = { decl.accessLevel }()
46+
private(set) lazy var comments: DeclComments = { decl.comments }()
4847

49-
lazy var name: String = {
50-
let name = decl.id.refine()
51-
let parent = context.map { $0.id }.joined(separator: ".")
52-
return parent.isEmpty ? name : "\(parent).\(name)"
53-
}()
48+
private(set) lazy var name: String = { buildName() }()
49+
50+
@StringBuilder
51+
private func buildName() -> String {
52+
if decl.keyword != .`init`, decl.keyword != .subscript {
53+
decl.keyword.rawValue
54+
" "
55+
}
56+
57+
if context.count > 0 {
58+
context.map { $0.name.trimmedDescription }.joined(separator: ".")
59+
"."
60+
}
61+
62+
decl.name.trimmedDescription
63+
decl.genericParameterClause?.trimmedDescription ?? ""
64+
decl.inheritanceClause?.trimmedDescription ?? ""
65+
decl.funcSignature?.trimmedDescription ?? ""
66+
67+
if let genericWhere = decl.genericWhereClause?.trimmedDescription {
68+
" "
69+
genericWhere
70+
}
71+
}
5472

5573
init(decl: DeclProtocol, context: [DeclProtocol], location: SourceLocation) {
5674
self.decl = decl

Sources/SwiftDocCoverage/Source.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ struct Source {
182182
let declarations: [Declaration]
183183

184184
var undocumented: [Declaration] {
185-
declarations.filter { $0.hasDoc == false }
185+
declarations.filter { $0.comments.documented == false }
186186
}
187187

188188
private init(_ sourceFile: SourceFileSyntax, minAccessLevel: AccessLevel) throws {

0 commit comments

Comments
 (0)