Skip to content
This repository has been archived by the owner on Jun 1, 2023. It is now read-only.

Fix public extensions exposing nested code of all access levels #195

Merged
merged 9 commits into from
Oct 8, 2020
6 changes: 5 additions & 1 deletion Sources/SwiftDoc/Symbol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ public final class Symbol {

if let `extension` = `extension`,
`extension`.modifiers.contains(where: { $0.name == "public" }) {
return true

let isNotLimitedAccessModifier = { (modifier: Modifier) in
modifier.name != "internal" && modifier.name != "fileprivate" && modifier.name != "private"
}
return api.modifiers.allSatisfy(isNotLimitedAccessModifier)
}

if let symbol = context.compactMap({ $0 as? Symbol }).last,
Expand Down
64 changes: 64 additions & 0 deletions Tests/SwiftDocTests/InterfaceTypeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,68 @@ final class InterfaceTypeTests: XCTestCase {
XCTAssertEqual(symbol.api.name, "A")
}
}

func testFunctionsInPublicExtension() throws {
let source = #"""
public extension Int {
func a() {}
public func b() {}
internal func c() {}
fileprivate func d() {}
private func e() {}
}
"""#

let url = try temporaryFile(contents: source)
let sourceFile = try SourceFile(file: url, relativeTo: url.deletingLastPathComponent())
let module = Module(name: "Module", sourceFiles: [sourceFile])

XCTAssertEqual(sourceFile.symbols.count, 5)
XCTAssertTrue(sourceFile.symbols[0].isPublic, "Function `a()` should BE marked as public - its visibility is specified by extension")
XCTAssertTrue(sourceFile.symbols[1].isPublic, "Function `b()` should BE marked as public - its visibility is public")
XCTAssertFalse(sourceFile.symbols[2].isPublic, "Function `c()` should NOT be marked as public - its visibility is internal")
XCTAssertFalse(sourceFile.symbols[3].isPublic, "Function `d()` should NOT be marked as public - its visibility is fileprivate")
XCTAssertFalse(sourceFile.symbols[4].isPublic, "Function `e()` should NOT be marked as public - its visibility is private")

XCTAssertEqual(module.interface.symbols.count, 2)
XCTAssertEqual(module.interface.symbols[0].name, "a()", "Function `a()` should be in documented interface")
XCTAssertEqual(module.interface.symbols[1].name, "b()", "Function `b()` should be in documented interface")
}

func testNestedPropertiesInPublicExtension() throws {
let source = #"""
public class RootController {}

public extension RootController {
class ControllerExtension {
public var public_properties: ExtendedProperties = ExtendedProperties()
internal var internal_properties: InternalProperties = InternalProperties()
}
}

public extension RootController.ControllerExtension {
struct ExtendedProperties {
public var public_prop: Int = 1
}
}

internal extension RootController.ControllerExtension {
struct InternalProperties {
internal var internal_prop: String = "FOO"
}
}
"""#


let url = try temporaryFile(contents: source)
let sourceFile = try SourceFile(file: url, relativeTo: url.deletingLastPathComponent())
let module = Module(name: "Module", sourceFiles: [sourceFile])

XCTAssertEqual(module.interface.symbols.count, 5)
XCTAssertEqual(module.interface.symbols[0].name, "RootController")
XCTAssertEqual(module.interface.symbols[1].name, "ControllerExtension")
XCTAssertEqual(module.interface.symbols[2].name, "public_properties")
XCTAssertEqual(module.interface.symbols[3].name, "ExtendedProperties")
XCTAssertEqual(module.interface.symbols[4].name, "public_prop")
}
}