Skip to content

Commit 0db8c65

Browse files
committed
Doc coverage 100%
1 parent e134137 commit 0db8c65

File tree

7 files changed

+99
-32
lines changed

7 files changed

+99
-32
lines changed

Sources/SwiftSource/SwiftAccessLevel.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,17 @@
2323

2424
import SwiftSyntax
2525

26-
26+
/// Swift declaration access level.
2727
public enum SwiftAccessLevel: Int, Codable {
28+
/// Classes and methods marked as open can be subclassed and overridden respectively out of their defining module.
2829
case `open`
30+
/// Accessible from everywhere.
2931
case `public`
32+
/// Accessible only within the defined module (default)
3033
case `internal`
34+
/// Accessible only within the current swift file.
3135
case `fileprivate`
36+
/// Accessible only within the defined class or struct.
3237
case `private`
3338

3439
init(modifiers: DeclModifierListSyntax) {

Sources/SwiftSource/SwiftComment.swift

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,41 @@
2323

2424
import SwiftSyntax
2525

26-
26+
/// Swift declaration comment.
2727
public struct SwiftComment: Codable {
28+
/// Text of this comment.
2829
public let text: String
30+
/// Returns `true` if this comment is documentation (starting with '///' or '/**').
2931
public var isDoc: Bool
3032

3133
init?(piece: TriviaPiece) {
3234
switch piece {
3335
case .lineComment(let value):
3436
text = value
37+
.replacingOccurrences(of: "//", with: "")
38+
.trimmingCharacters(in: .whitespaces)
3539
isDoc = false
40+
3641
case .blockComment(let value):
3742
text = value
43+
.replacingOccurrences(of: "/*", with: "")
44+
.replacingOccurrences(of: "*/", with: "")
45+
.trimmingCharacters(in: .whitespaces)
3846
isDoc = false
47+
3948
case .docLineComment(let value):
4049
text = value
41-
isDoc = true
50+
.replacingOccurrences(of: "///", with: "")
51+
.trimmingCharacters(in: .whitespaces)
52+
isDoc = !text.isEmpty
53+
4254
case .docBlockComment(let value):
4355
text = value
44-
isDoc = true
56+
.replacingOccurrences(of: "/**", with: "")
57+
.replacingOccurrences(of: "*/", with: "")
58+
.trimmingCharacters(in: .whitespaces)
59+
isDoc = !text.isEmpty
60+
4561
default:
4662
return nil
4763
}

Sources/SwiftSource/SwiftDeclaration.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,23 @@ fileprivate struct StringBuilder {
4747
}
4848
}
4949

50+
/// Swift declaration.
5051
public struct SwiftDeclaration: Codable {
52+
/// List of comments of this declaration.
5153
public let comments: [SwiftComment]
54+
/// Access level of this declaration.
5255
public let accessLevel: SwiftAccessLevel
56+
/// The keyword for this declaration.
5357
public var keyword: SwiftKeyword
58+
/// The name of this declaration.
5459
public let name: String
60+
/// The line in the file where this declaration resides.
5561
public let line: Int
62+
/// The offset from the beginning of the line where this declaration resides.
5663
public let column: Int
5764

58-
public var isDocumented: Bool {
65+
/// Whether or not this declaration has documentation.
66+
public var hasDoc: Bool {
5967
comments.first { $0.isDoc } != nil
6068
}
6169

Sources/SwiftSource/SwiftKeyword.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,41 @@
2323

2424
import SwiftSyntax
2525

26-
26+
/// Swift declaration keyword.
2727
public enum SwiftKeyword: String, Codable {
28+
/// `actor`
2829
case actor
30+
/// `associatedtype`
2931
case `associatedtype`
32+
/// Enum `case`
3033
case `case`
34+
/// `class`
3135
case `class`
36+
/// `enum`
3237
case `enum`
38+
/// `extension`
3339
case `extension`
40+
/// `func`
3441
case `func`
35-
//case `import`
42+
// case `import`
43+
/// `init`
3644
case `init`
45+
/// Const variable `let`
3746
case `let`
47+
/// `macro`
3848
case macro
49+
/// `operator`
3950
case `operator`
51+
/// `precedencegroup`
4052
case `precedencegroup`
53+
/// `protocol`
4154
case `protocol`
55+
/// `struct`
4256
case `struct`
57+
/// `subscript`
4358
case `subscript`
59+
/// `typealias`
4460
case `typealias`
61+
/// Variable `var`
4562
case `var`
4663
}

Sources/SwiftSource/SwiftSource.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,32 @@ import Foundation
2525
import SwiftSyntax
2626

2727

28+
/// Swift source
2829
public struct SwiftSource: Codable {
30+
/// Source URL
2931
public let url: URL?
32+
/// List of all parsed declarations
3033
public let declarations: [SwiftDeclaration]
3134

3235
private init(url: URL?, source: String) {
3336
self.url = url
3437
let visitor = Visitor(source: source)
3538
self.declarations = visitor.declarations
3639
}
37-
40+
41+
/// Create from a specified swift code.
3842
public init(source: String) {
3943
self.init(url: nil, source: source)
4044
}
4145

42-
public init(fileURL: URL) throws {
43-
let source = try String(contentsOf: fileURL)
44-
self.init(url: fileURL, source: source)
46+
/// Create from a specified URL.
47+
public init(url: URL) throws {
48+
let source = try String(contentsOf: url)
49+
self.init(url: url, source: source)
4550
}
4651

47-
public func declarations(level: SwiftAccessLevel) -> [SwiftDeclaration] {
48-
declarations.filter { $0.accessLevel.rawValue <= level.rawValue }
52+
/// List of declarations with a specified access level
53+
public func declarations(accessLevel: SwiftAccessLevel) -> [SwiftDeclaration] {
54+
declarations.filter { $0.accessLevel.rawValue <= accessLevel.rawValue }
4955
}
5056
}

Sources/swift-doc-coverage/main.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,10 @@ struct SwiftDocCoverage: ParsableCommand {
122122
sources = try urls.map { url in
123123
let sourceTime = Date()
124124

125-
let source = try SwiftSource(fileURL: url)
125+
let source = try SwiftSource(url: url)
126126

127127
if report != .json {
128-
let declarations = source.declarations(level: minimumAccessLevel.accessLevel)
128+
let declarations = source.declarations(accessLevel: minimumAccessLevel.accessLevel)
129129
if declarations.count > 0 {
130130
index += 1
131131
let filePath = url.absoluteString
@@ -142,12 +142,12 @@ struct SwiftDocCoverage: ParsableCommand {
142142
}
143143

144144
if report == .coverage {
145-
let declarations = sources.flatMap { $0.declarations(level: minimumAccessLevel.accessLevel) }
145+
let declarations = sources.flatMap { $0.declarations(accessLevel: minimumAccessLevel.accessLevel) }
146146
guard declarations.count > 0 else {
147147
throw Errors.declarationsNotFound
148148
}
149149

150-
let undocumented = declarations.filter { $0.isDocumented == false }
150+
let undocumented = declarations.filter { $0.hasDoc == false }
151151

152152
let totalCount = declarations.count
153153
let documentedCount = totalCount - undocumented.count
@@ -246,7 +246,7 @@ struct SwiftDocCoverage: ParsableCommand {
246246
static func coverage(index: Int, time: Date, filePath: String, declarations: [SwiftDeclaration], out: Output?) {
247247
assert(declarations.count > 0)
248248

249-
let undocumented = declarations.filter { $0.isDocumented == false }
249+
let undocumented = declarations.filter { $0.hasDoc == false }
250250

251251
let totalCount = declarations.count
252252
let documentedCount = totalCount - undocumented.count
@@ -269,7 +269,7 @@ struct SwiftDocCoverage: ParsableCommand {
269269
assert(declarations.count > 0)
270270

271271
declarations
272-
.filter { $0.isDocumented == false }
272+
.filter { $0.hasDoc == false }
273273
.forEach {
274274
out?.write("\(filePath):\($0.line):\($0.column): warning: No documentation for '\($0.name)'.")
275275
}

Tests/SwiftDocCoverageTests/SwiftDocCoverageTests.swift

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -421,30 +421,45 @@ final class SourceCodeTests: XCTestCase {
421421
/// A documentation line comment
422422
/** A documentation
423423
block comment */
424-
424+
///
425+
/** */
425426
mutating public func eat(_ food: Food, quantity: Int) throws -> Int { return 0 }
426427
"""
427428
let source = SwiftSource(source: code)
428429
XCTAssert(source.declarations.count == 1)
429-
XCTAssert(source.declarations[0].comments.count == 4)
430-
XCTAssert(source.declarations[0].comments[0].text == "// A developer line comment")
431-
XCTAssert(source.declarations[0].comments[1].text == "/* A developer block comment */")
432-
XCTAssert(source.declarations[0].comments[2].text == "/// A documentation line comment")
433-
XCTAssert(source.declarations[0].comments[3].text == "/** A documentation \nblock comment */")
430+
XCTAssert(source.declarations[0].comments.count == 6)
431+
432+
XCTAssert(source.declarations[0].comments[0].text == "A developer line comment")
433+
XCTAssert(source.declarations[0].comments[0].isDoc == false)
434+
435+
XCTAssert(source.declarations[0].comments[1].text == "A developer block comment")
436+
XCTAssert(source.declarations[0].comments[1].isDoc == false)
437+
438+
XCTAssert(source.declarations[0].comments[2].text == "A documentation line comment")
439+
XCTAssert(source.declarations[0].comments[2].isDoc == true)
440+
441+
XCTAssert(source.declarations[0].comments[3].text == "A documentation \nblock comment")
442+
XCTAssert(source.declarations[0].comments[3].isDoc == true)
443+
444+
XCTAssert(source.declarations[0].comments[4].text == "")
445+
XCTAssert(source.declarations[0].comments[4].isDoc == false)
446+
447+
XCTAssert(source.declarations[0].comments[5].text == "")
448+
XCTAssert(source.declarations[0].comments[5].isDoc == false)
434449
}
435450
}
436451

437452
final class SourceFileTests: XCTestCase {
438453

439454
func test_no_file() {
440455
let fileURL = resourcesUrl.appendingPathComponent("File.swift")
441-
XCTAssertThrowsError(try SwiftSource(fileURL: fileURL)) { error in
456+
XCTAssertThrowsError(try SwiftSource(url: fileURL)) { error in
442457
XCTAssert(error.localizedDescription == "The file “File.swift” couldn’t be opened because there is no such file.")
443458
}
444459
}
445460

446461
func test_file() throws {
447-
let source = try SwiftSource(fileURL: rectUrl)
462+
let source = try SwiftSource(url: rectUrl)
448463
XCTAssert(source.declarations.count == 5)
449464
}
450465
}
@@ -488,23 +503,23 @@ final class SwiftDocCoverageTests: XCTestCase {
488503
var cmd = try SwiftDocCoverage.run(rectUrl.path, "--minimum-access-level", "open")
489504
XCTAssert(cmd.sources.count == 1)
490505
XCTAssert(cmd.sources[0].declarations.count == 5)
491-
XCTAssert(cmd.sources[0].declarations(level: .open).count == 1)
506+
XCTAssert(cmd.sources[0].declarations(accessLevel: .open).count == 1)
492507

493508
cmd = try SwiftDocCoverage.run(rectUrl.path, "--minimum-access-level", "public")
494509
XCTAssert(cmd.sources.count == 1)
495-
XCTAssert(cmd.sources[0].declarations(level: .public).count == 2)
510+
XCTAssert(cmd.sources[0].declarations(accessLevel: .public).count == 2)
496511

497512
cmd = try SwiftDocCoverage.run(rectUrl.path, "--minimum-access-level", "internal")
498513
XCTAssert(cmd.sources.count == 1)
499-
XCTAssert(cmd.sources[0].declarations(level: .internal).count == 3)
514+
XCTAssert(cmd.sources[0].declarations(accessLevel: .internal).count == 3)
500515

501516
cmd = try SwiftDocCoverage.run(rectUrl.path, "--minimum-access-level", "fileprivate")
502517
XCTAssert(cmd.sources.count == 1)
503-
XCTAssert(cmd.sources[0].declarations(level: .fileprivate).count == 4)
518+
XCTAssert(cmd.sources[0].declarations(accessLevel: .fileprivate).count == 4)
504519

505520
cmd = try SwiftDocCoverage.run(rectUrl.path, "--minimum-access-level", "private")
506521
XCTAssert(cmd.sources.count == 1)
507-
XCTAssert(cmd.sources[0].declarations(level: .private).count == 5)
522+
XCTAssert(cmd.sources[0].declarations(accessLevel: .private).count == 5)
508523
}
509524

510525
func test_ignore_filename_regex() throws {

0 commit comments

Comments
 (0)