Skip to content

Commit a7a8e83

Browse files
authored
Merge pull request swiftlang#258 from google/format-doc-comments
Fix parameter validation for functions with labeled arguments; add su…
2 parents b99c6e7 + 33c6251 commit a7a8e83

File tree

4 files changed

+126
-11
lines changed

4 files changed

+126
-11
lines changed

Sources/SwiftFormat/Pipelines+Generated.swift

+1
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ extension LintPipeline {
158158
_ = AllPublicDeclarationsHaveDocumentation(context: context).visit(node)
159159
_ = BeginDocumentationCommentWithOneLineSummary(context: context).visit(node)
160160
_ = UseTripleSlashForDocumentationComments(context: context).visit(node)
161+
_ = ValidateDocumentationComments(context: context).visit(node)
161162
return .visitChildren
162163
}
163164

Sources/SwiftFormatRules/ValidateDocumentationComments.swift

+31-11
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,23 @@ public struct ValidateDocumentationComments: SyntaxLintRule {
2929
self.context = context
3030
}
3131

32+
public func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind {
33+
return checkFunctionLikeDocumentation(
34+
node, name: "init", parameters: node.parameters.parameterList)
35+
}
36+
3237
public func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
38+
return checkFunctionLikeDocumentation(
39+
node, name: node.identifier.text, parameters: node.signature.input.parameterList,
40+
returnClause: node.signature.output)
41+
}
42+
43+
private func checkFunctionLikeDocumentation(
44+
_ node: DeclSyntax,
45+
name: String,
46+
parameters: FunctionParameterListSyntax,
47+
returnClause: ReturnClauseSyntax? = nil
48+
) -> SyntaxVisitorContinueKind {
3349
guard let declComment = node.docComment else { return .skipChildren }
3450
guard let commentInfo = node.docCommentInfo else { return .skipChildren }
3551
guard let params = commentInfo.parameters else { return .skipChildren }
@@ -48,8 +64,8 @@ public struct ValidateDocumentationComments: SyntaxLintRule {
4864
let hasPluralDesc = declComment.components(separatedBy: .newlines)
4965
.contains { $0.trimmingCharacters(in: .whitespaces).starts(with: "- Parameters") }
5066

51-
validateReturn(node, returnDesc: commentInfo.returnsDescription)
52-
let funcParameters = funcParametersIdentifiers(in: node.signature.input.parameterList)
67+
validateReturn(returnClause, name: name, returnDesc: commentInfo.returnsDescription)
68+
let funcParameters = funcParametersIdentifiers(in: parameters)
5369

5470
// If the documentation of the parameters is wrong 'docCommentInfo' won't
5571
// parse the parameters correctly. First the documentation has to be fix
@@ -67,20 +83,20 @@ public struct ValidateDocumentationComments: SyntaxLintRule {
6783
// are the same.
6884
if (params.count != funcParameters.count) ||
6985
!parametersAreEqual(params: params, funcParam: funcParameters) {
70-
diagnose(.parametersDontMatch(funcName: node.identifier.text), on: node)
86+
diagnose(.parametersDontMatch(funcName: name), on: node)
7187
}
7288

7389
return .skipChildren
7490
}
7591

7692
/// Ensures the function has a return documentation if it actually returns
7793
/// a value.
78-
func validateReturn(_ node: FunctionDeclSyntax, returnDesc: String?) {
79-
if node.signature.output == nil && returnDesc != nil {
80-
diagnose(.removeReturnComment(funcName: node.identifier.text), on: node)
94+
func validateReturn(_ returnClause: ReturnClauseSyntax?, name: String, returnDesc: String?) {
95+
if returnClause == nil && returnDesc != nil {
96+
diagnose(.removeReturnComment(funcName: name), on: returnClause)
8197
}
82-
else if node.signature.output != nil && returnDesc == nil {
83-
diagnose(.documentReturnValue(funcName: node.identifier.text), on: node)
98+
else if returnClause != nil && returnDesc == nil {
99+
diagnose(.documentReturnValue(funcName: name), on: returnClause)
84100
}
85101
}
86102
}
@@ -89,9 +105,13 @@ public struct ValidateDocumentationComments: SyntaxLintRule {
89105
/// paramters identifiers.
90106
func funcParametersIdentifiers(in paramList: FunctionParameterListSyntax) -> [String] {
91107
var funcParameters = [String]()
92-
for parameter in paramList
93-
{
94-
guard let parameterIdentifier = parameter.firstName else { continue }
108+
for parameter in paramList {
109+
// If there is a label and an identifier, then the identifier (`secondName`) is the name that
110+
// should be documented. Otherwise, the label and identifier are the same, occupying
111+
// `firstName`.
112+
guard let parameterIdentifier = parameter.secondName ?? parameter.firstName else {
113+
continue
114+
}
95115
funcParameters.append(parameterIdentifier.text)
96116
}
97117
return funcParameters

Tests/SwiftFormatRulesTests/ValidateDocumentationCommentsTests.swift

+91
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,95 @@ public class ValidateDocumentationCommentsTests: DiagnosingTestCase {
135135
XCTAssertNotDiagnosed(.removeReturnComment(funcName: "ommitedFunc"))
136136
XCTAssertNotDiagnosed(.parametersDontMatch(funcName: "ommitedFunc"))
137137
}
138+
139+
public func testSeparateLabelAndIdentifier() {
140+
let input =
141+
"""
142+
/// Returns the output generated by executing a command.
143+
///
144+
/// - Parameter command: The command to execute in the shell environment.
145+
/// - Returns: A string containing the contents of the invoked process's
146+
/// standard output.
147+
func incorrectParam(label commando: String) -> String {
148+
// ...
149+
}
150+
151+
/// Returns the output generated by executing a command.
152+
///
153+
/// - Parameter command: The command to execute in the shell environment.
154+
/// - Returns: A string containing the contents of the invoked process's
155+
/// standard output.
156+
func singularParam(label command: String) -> String {
157+
// ...
158+
}
159+
160+
/// Returns the output generated by executing a command with the given string
161+
/// used as standard input.
162+
///
163+
/// - Parameters:
164+
/// - command: The command to execute in the shell environment.
165+
/// - stdin: The string to use as standard input.
166+
/// - Returns: A string containing the contents of the invoked process's
167+
/// standard output.
168+
func pluralParam(label command: String, label2 stdin: String) -> String {
169+
// ...
170+
}
171+
"""
172+
performLint(ValidateDocumentationComments.self, input: input)
173+
XCTAssertNotDiagnosed(.useSingularParameter)
174+
XCTAssertNotDiagnosed(.usePluralParameters)
175+
176+
XCTAssertDiagnosed(.parametersDontMatch(funcName: "incorrectParam"))
177+
178+
XCTAssertNotDiagnosed(.documentReturnValue(funcName: "singularParam"))
179+
XCTAssertNotDiagnosed(.removeReturnComment(funcName: "singularParam"))
180+
XCTAssertNotDiagnosed(.parametersDontMatch(funcName: "singularParam"))
181+
182+
XCTAssertNotDiagnosed(.documentReturnValue(funcName: "pluralParam"))
183+
XCTAssertNotDiagnosed(.removeReturnComment(funcName: "pluralParam"))
184+
XCTAssertNotDiagnosed(.parametersDontMatch(funcName: "pluralParam"))
185+
}
186+
187+
public func testInitializer() {
188+
let input =
189+
"""
190+
/// Brief summary.
191+
///
192+
/// - Parameter command: The command to execute in the shell environment.
193+
/// - Returns: Shouldn't be here.
194+
init(label commando: String) {
195+
// ...
196+
}
197+
198+
/// Brief summary.
199+
///
200+
/// - Parameter command: The command to execute in the shell environment.
201+
init(label command: String) {
202+
// ...
203+
}
204+
205+
/// Brief summary.
206+
///
207+
/// - Parameters:
208+
/// - command: The command to execute in the shell environment.
209+
/// - stdin: The string to use as standard input.
210+
init(label command: String, label2 stdin: String) {
211+
// ...
212+
}
213+
"""
214+
performLint(ValidateDocumentationComments.self, input: input)
215+
XCTAssertNotDiagnosed(.useSingularParameter)
216+
XCTAssertNotDiagnosed(.usePluralParameters)
217+
218+
XCTAssertDiagnosed(.parametersDontMatch(funcName: "init"))
219+
XCTAssertDiagnosed(.removeReturnComment(funcName: "init"))
220+
221+
XCTAssertNotDiagnosed(.documentReturnValue(funcName: "init"))
222+
XCTAssertNotDiagnosed(.removeReturnComment(funcName: "init"))
223+
XCTAssertNotDiagnosed(.parametersDontMatch(funcName: "init"))
224+
225+
XCTAssertNotDiagnosed(.documentReturnValue(funcName: "init"))
226+
XCTAssertNotDiagnosed(.removeReturnComment(funcName: "init"))
227+
XCTAssertNotDiagnosed(.parametersDontMatch(funcName: "init"))
228+
}
138229
}

Tests/SwiftFormatRulesTests/XCTestManifests.swift

+3
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ extension BlankLineBetweenMembersTests {
5353
// `swift test --generate-linuxmain`
5454
// to regenerate.
5555
static let __allTests__BlankLineBetweenMembersTests = [
56+
("testBlankLineBeforeFirstChildOrNot", testBlankLineBeforeFirstChildOrNot),
5657
("testInvalidBlankLineBetweenMembers", testInvalidBlankLineBetweenMembers),
5758
("testNestedMembers", testNestedMembers),
5859
("testTwoMembers", testTwoMembers),
@@ -381,9 +382,11 @@ extension ValidateDocumentationCommentsTests {
381382
// `swift test --generate-linuxmain`
382383
// to regenerate.
383384
static let __allTests__ValidateDocumentationCommentsTests = [
385+
("testInitializer", testInitializer),
384386
("testParameterDocumentation", testParameterDocumentation),
385387
("testParametersName", testParametersName),
386388
("testReturnDocumentation", testReturnDocumentation),
389+
("testSeparateLabelAndIdentifier", testSeparateLabelAndIdentifier),
387390
("testValidDocumentation", testValidDocumentation),
388391
]
389392
}

0 commit comments

Comments
 (0)