From d09bc3ecaf5b07d87dbddf431ee430e24d5a797b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20R=C3=B6nnqvist?= Date: Thu, 19 Dec 2024 18:21:40 +0100 Subject: [PATCH] Improve detection of inherited documentation comments for parameter and return validation rdar://134534169 --- .../Model/ParametersAndReturnValidator.swift | 15 +++++---- .../SymbolGraphCreation.swift | 6 +++- .../ParametersAndReturnValidatorTests.swift | 31 +++++++++++++++++-- .../Semantics/SymbolTests.swift | 1 + 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Sources/SwiftDocC/Model/ParametersAndReturnValidator.swift b/Sources/SwiftDocC/Model/ParametersAndReturnValidator.swift index 3b6dfe2ad4..9307286a40 100644 --- a/Sources/SwiftDocC/Model/ParametersAndReturnValidator.swift +++ b/Sources/SwiftDocC/Model/ParametersAndReturnValidator.swift @@ -278,13 +278,16 @@ struct ParametersAndReturnValidator { /// Checks if the symbol's documentation is inherited from another source location. private func hasInheritedDocumentationComment(symbol: UnifiedSymbolGraph.Symbol) -> Bool { - let symbolLocationURI = symbol.documentedSymbol?.mixins.getValueIfPresent(for: SymbolGraph.Symbol.Location.self)?.uri - for case .sourceCode(let location, _) in docChunkSources { - // Check if the symbol has documentation from another source location - return location?.uri != symbolLocationURI + guard let documentedSymbol = symbol.documentedSymbol else { + // If there's no doc comment, any documentation comes from an extension file and isn't inherited from another symbol. + return false } - // If the symbol didn't have any in-source documentation, check if there's a extension file override. - return docChunkSources.isEmpty + + // A symbol has inherited documentation if the doc comment doesn't come from the current module. + let moduleNames: Set = symbol.modules.values.reduce(into: []) { $0.insert($1.name) } + return !moduleNames.contains(where: { moduleName in + documentedSymbol.isDocCommentFromSameModule(symbolModuleName: moduleName) == true + }) } private typealias Signatures = [DocumentationDataVariantsTrait: SymbolGraph.Symbol.FunctionSignature] diff --git a/Sources/SwiftDocCTestUtilities/SymbolGraphCreation.swift b/Sources/SwiftDocCTestUtilities/SymbolGraphCreation.swift index 23067f7543..90fa578bef 100644 --- a/Sources/SwiftDocCTestUtilities/SymbolGraphCreation.swift +++ b/Sources/SwiftDocCTestUtilities/SymbolGraphCreation.swift @@ -47,6 +47,7 @@ extension XCTestCase { package func makeLineList( docComment: String, + moduleName: String?, startOffset: SymbolGraph.LineList.SourceRange.Position = defaultSymbolPosition, url: URL = defaultSymbolURL ) -> SymbolGraph.LineList { @@ -64,7 +65,8 @@ extension XCTestCase { ) }, // We want to include the file:// scheme here - uri: url.absoluteString + uri: url.absoluteString, + moduleName: moduleName ) } @@ -83,6 +85,7 @@ extension XCTestCase { kind kindID: SymbolGraph.Symbol.KindIdentifier, pathComponents: [String], docComment: String? = nil, + moduleName: String? = nil, accessLevel: SymbolGraph.Symbol.AccessControl = .init(rawValue: "public"), // Defined internally in SwiftDocC location: (position: SymbolGraph.LineList.SourceRange.Position, url: URL)? = (defaultSymbolPosition, defaultSymbolURL), signature: SymbolGraph.Symbol.FunctionSignature? = nil, @@ -109,6 +112,7 @@ extension XCTestCase { docComment: docComment.map { makeLineList( docComment: $0, + moduleName: moduleName, startOffset: location?.position ?? defaultSymbolPosition, url: location?.url ?? defaultSymbolURL ) diff --git a/Tests/SwiftDocCTests/Model/ParametersAndReturnValidatorTests.swift b/Tests/SwiftDocCTests/Model/ParametersAndReturnValidatorTests.swift index 97a6b6bfad..84d8290a7b 100644 --- a/Tests/SwiftDocCTests/Model/ParametersAndReturnValidatorTests.swift +++ b/Tests/SwiftDocCTests/Model/ParametersAndReturnValidatorTests.swift @@ -404,6 +404,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { Folder(name: "swift", content: [ JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph( docComment: nil, + docCommentModuleName: "ModuleName", sourceLanguage: .swift, parameters: [], returnValue: .init(kind: .typeIdentifier, spelling: "Void", preciseIdentifier: "s:s4Voida") @@ -416,6 +417,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { - Returns: Some return value description. """, + docCommentModuleName: "ModuleName", sourceLanguage: .objectiveC, parameters: [(name: "error", externalName: nil)], returnValue: .init(kind: .typeIdentifier, spelling: "BOOL", preciseIdentifier: "c:@T@BOOL") @@ -447,6 +449,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { JSONFile(name: "Platform1-ModuleName.symbols.json", content: makeSymbolGraph( platform: .init(operatingSystem: .init(name: "Platform1")), docComment: nil, + docCommentModuleName: "ModuleName", sourceLanguage: .objectiveC, parameters: [(name: "first", externalName: nil)], returnValue: .init(kind: .typeIdentifier, spelling: "void", preciseIdentifier: "c:v") @@ -455,6 +458,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { JSONFile(name: "Platform2-ModuleName.symbols.json", content: makeSymbolGraph( platform: .init(operatingSystem: .init(name: "Platform2")), docComment: nil, + docCommentModuleName: "ModuleName", sourceLanguage: .objectiveC, parameters: [(name: "first", externalName: nil), (name: "second", externalName: nil)], returnValue: .init(kind: .typeIdentifier, spelling: "void", preciseIdentifier: "c:v") @@ -463,6 +467,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { JSONFile(name: "Platform3-ModuleName.symbols.json", content: makeSymbolGraph( platform: .init(operatingSystem: .init(name: "Platform3")), docComment: nil, + docCommentModuleName: "ModuleName", sourceLanguage: .objectiveC, parameters: [(name: "first", externalName: nil),], returnValue: .init(kind: .typeIdentifier, spelling: "BOOL", preciseIdentifier: "c:@T@BOOL") @@ -502,6 +507,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { Folder(name: "swift", content: [ JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph( docComment: nil, + docCommentModuleName: "ModuleName", sourceLanguage: .swift, parameters: [(name: "error", externalName: nil)], returnValue: .init(kind: .typeIdentifier, spelling: "Void", preciseIdentifier: "s:s4Voida") @@ -514,6 +520,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { - Parameter error: Some parameter description. """, + docCommentModuleName: "ModuleName", sourceLanguage: .objectiveC, parameters: [(name: "error", externalName: nil)], returnValue: .init(kind: .typeIdentifier, spelling: "void", preciseIdentifier: "c:v") @@ -661,14 +668,28 @@ class ParametersAndReturnValidatorTests: XCTestCase { 10 + /// - Parameter first: Some parameter description | ╰─suggestion: Document 'second' parameter """) - - + } + + func testDoesNotWarnAboutInheritedDocumentation() throws { + let warningOutput = try warningOutputRaisedFrom( + docComment: """ + Some function description + + - Parameter second: Some parameter description + - Returns: Nothing. + """, + docCommentModuleName: "SomeOtherModule", + parameters: [(name: "first", externalName: "with"), (name: "second", externalName: "and")], + returnValue: .init(kind: .typeIdentifier, spelling: "Void", preciseIdentifier: "s:s4Voida") + ) + XCTAssertEqual(warningOutput, "") } // MARK: Test helpers private func warningOutputRaisedFrom( docComment: String, + docCommentModuleName: String? = "ModuleName", parameters: [(name: String, externalName: String?)], returnValue: SymbolGraph.Symbol.DeclarationFragments.Fragment, file: StaticString = #file, @@ -685,6 +706,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { Folder(name: "unit-test.docc", content: [ JSONFile(name: "ModuleName.symbols.json", content: makeSymbolGraph( docComment: docComment, + docCommentModuleName: docCommentModuleName, sourceLanguage: .swift, parameters: parameters, returnValue: returnValue @@ -712,6 +734,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { private func makeSymbolGraph(docComment: String) -> SymbolGraph { makeSymbolGraph( docComment: docComment, + docCommentModuleName: "ModuleName", sourceLanguage: .swift, parameters: [ ("firstParameter", nil), @@ -726,12 +749,13 @@ class ParametersAndReturnValidatorTests: XCTestCase { private func makeSymbolGraph( platform: SymbolGraph.Platform = .init(), docComment: String?, + docCommentModuleName: String?, sourceLanguage: SourceLanguage, parameters: [(name: String, externalName: String?)], returnValue: SymbolGraph.Symbol.DeclarationFragments.Fragment ) -> SymbolGraph { return makeSymbolGraph( - moduleName: "ModuleName", + moduleName: "ModuleName", // Don't use `docCommentModuleName` here. platform: platform, symbols: [ makeSymbol( @@ -740,6 +764,7 @@ class ParametersAndReturnValidatorTests: XCTestCase { kind: .func, pathComponents: ["functionName(...)"], docComment: docComment, + moduleName: docCommentModuleName, location: (start, symbolURL), signature: .init( parameters: parameters.map { diff --git a/Tests/SwiftDocCTests/Semantics/SymbolTests.swift b/Tests/SwiftDocCTests/Semantics/SymbolTests.swift index 09d3d9c2a0..64aa7eea1c 100644 --- a/Tests/SwiftDocCTests/Semantics/SymbolTests.swift +++ b/Tests/SwiftDocCTests/Semantics/SymbolTests.swift @@ -1368,6 +1368,7 @@ class SymbolTests: XCTestCase { let newDocComment = self.makeLineList( docComment: docComment, + moduleName: nil, startOffset: .init( line: docCommentLineOffset, character: 0