Skip to content

Commit 9cd9bc3

Browse files
authored
Add solutions to remove invalid metadata directives in documentation comments (#1277)
* Add solutions to remove invalid metadata directives in documentation comments - Implement solutions for org.swift.docc.Metadata.Invalid<directive>InDocumentationComment diagnostics - Each invalid directive now has a solution that removes the directive - Follows the same pattern as other directive removal solutions in the codebase - Added comprehensive tests to verify solutions are provided correctly - Fixes issue #1111 * Fix code duplication in validateForUseInDocumentationComment method - Create diagnostic once instead of duplicating it - Use conditional solutions array based on range availability - Single Problem creation path for better maintainability * Add range verification test for invalid metadata directive solutions - Verify replacement range matches problem's diagnostic range - Ensures solution targets the correct content - Improves test coverage for range accuracy * Move test comments into assertion messages for better clarity - Move explanatory comments into assertion message parameters - Provides better context when tests fail - Makes tests more self-documenting * Fix code duplication in validateForUseInDocumentationComment method - Create diagnostic once instead of duplicating it - Use conditional solutions array based on range availability - Single Problem creation path for better maintainability
1 parent a4b9902 commit 9cd9bc3

File tree

2 files changed

+61
-11
lines changed

2 files changed

+61
-11
lines changed

Sources/SwiftDocC/Semantics/Metadata/Metadata.swift

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -224,18 +224,25 @@ public final class Metadata: Semantic, AutomaticDirectiveConvertible {
224224

225225
problems.append(
226226
contentsOf: namesAndRanges.map { (name, range) in
227-
Problem(
228-
diagnostic: Diagnostic(
229-
source: symbolSource,
230-
severity: .warning,
231-
range: range,
232-
identifier: "org.swift.docc.\(Metadata.directiveName).Invalid\(name)InDocumentationComment",
233-
summary: "Invalid use of \(name.singleQuoted) directive in documentation comment; configuration will be ignored",
234-
explanation: "Specify this configuration in a documentation extension file"
235-
236-
// TODO: It would be nice to offer a solution here that removes the directive for you (#1111, rdar://140846407)
237-
)
227+
let diagnostic = Diagnostic(
228+
source: symbolSource,
229+
severity: .warning,
230+
range: range,
231+
identifier: "org.swift.docc.\(Metadata.directiveName).Invalid\(name)InDocumentationComment",
232+
summary: "Invalid use of \(name.singleQuoted) directive in documentation comment; configuration will be ignored",
233+
explanation: "Specify this configuration in a documentation extension file"
238234
)
235+
236+
let solutions: [Solution] = range.map { range in
237+
[Solution(
238+
summary: "Remove invalid \(name.singleQuoted) directive",
239+
replacements: [
240+
Replacement(range: range, replacement: "")
241+
]
242+
)]
243+
} ?? []
244+
245+
return Problem(diagnostic: diagnostic, possibleSolutions: solutions)
239246
}
240247
)
241248

Tests/SwiftDocCTests/Semantics/SymbolTests.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,19 @@ class SymbolTests: XCTestCase {
13111311
"org.swift.docc.Metadata.InvalidRedirectedInDocumentationComment",
13121312
]
13131313
)
1314+
1315+
// Verify that each problem has exactly one solution to remove the directive
1316+
for problem in problems {
1317+
XCTAssertEqual(problem.possibleSolutions.count, 1, "Each invalid metadata directive should have exactly one solution")
1318+
1319+
let solution = try XCTUnwrap(problem.possibleSolutions.first)
1320+
XCTAssertTrue(solution.summary.hasPrefix("Remove invalid"), "Solution summary should start with 'Remove invalid'")
1321+
XCTAssertEqual(solution.replacements.count, 1, "Solution should have exactly one replacement")
1322+
1323+
let replacement = try XCTUnwrap(solution.replacements.first)
1324+
XCTAssertEqual(replacement.replacement, "", "Replacement should be empty string to remove the directive")
1325+
XCTAssertNotNil(replacement.range, "Replacement should have a valid range")
1326+
}
13141327
}
13151328

13161329
func testParsesDeprecationSummaryDirectiveFromDocComment() async throws {
@@ -1352,6 +1365,36 @@ class SymbolTests: XCTestCase {
13521365
XCTAssert(problems.isEmpty)
13531366
}
13541367

1368+
func testSolutionForInvalidMetadataDirectiveRemovesDirective() async throws {
1369+
let (_, problems) = try await makeDocumentationNodeForSymbol(
1370+
docComment: """
1371+
The symbol's abstract.
1372+
1373+
@Metadata {
1374+
@DisplayName("Invalid Display Name")
1375+
}
1376+
""",
1377+
articleContent: nil
1378+
)
1379+
1380+
XCTAssertEqual(problems.count, 1)
1381+
let problem = try XCTUnwrap(problems.first)
1382+
1383+
XCTAssertEqual(problem.diagnostic.identifier, "org.swift.docc.Metadata.InvalidDisplayNameInDocumentationComment")
1384+
XCTAssertEqual(problem.possibleSolutions.count, 1)
1385+
1386+
let solution = try XCTUnwrap(problem.possibleSolutions.first)
1387+
XCTAssertEqual(solution.summary, "Remove invalid 'DisplayName' directive")
1388+
XCTAssertEqual(solution.replacements.count, 1)
1389+
1390+
let replacement = try XCTUnwrap(solution.replacements.first)
1391+
XCTAssertEqual(replacement.replacement, "", "Replacement should be empty string to remove the directive")
1392+
XCTAssertNotNil(replacement.range, "Replacement should have a valid range")
1393+
1394+
// Verify that the replacement range covers the expected content
1395+
XCTAssertEqual(replacement.range, problem.diagnostic.range, "Replacement range should match the problem's diagnostic range to ensure it removes the entire @DisplayName directive")
1396+
}
1397+
13551398
// MARK: - Leading Whitespace in Doc Comments
13561399

13571400
func testWithoutLeadingWhitespace() {

0 commit comments

Comments
 (0)