From e0524731fa4e66ecbd74c5fb4f7ff4e0112c3d79 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 21 Feb 2026 09:52:19 -0800 Subject: [PATCH] Update `parseType` to support Swift 6.4 `::` module selector syntax (#2399) Co-authored-by: calda <1811727+calda@users.noreply.github.com> --- Sources/ParsingHelpers.swift | 11 +++++++++++ Tests/ParsingHelpersTests.swift | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/Sources/ParsingHelpers.swift b/Sources/ParsingHelpers.swift index 448e39c4..8869c5ad 100644 --- a/Sources/ParsingHelpers.swift +++ b/Sources/ParsingHelpers.swift @@ -1490,6 +1490,17 @@ extension Formatter { return TypeName(range: startOfTypeIndex ... followingType.range.upperBound, formatter: self) } + // `::` can also continue a type (e.g. `Module::Type`), but unlike `.` and `&`, + // there must be no newline between `::` and the following identifier. + if let nextTokenIndex = index(of: .nonSpaceOrCommentOrLinebreak, after: baseType.range.upperBound), + tokens[nextTokenIndex] == .operator("::", .infix), + let afterDoubleColonIndex = index(of: .nonSpaceOrComment, after: nextTokenIndex), + !tokens[afterDoubleColonIndex].isLinebreak, + let followingType = parseType(at: afterDoubleColonIndex, excludeLowercaseIdentifiers: excludeLowercaseIdentifiers) + { + return TypeName(range: startOfTypeIndex ... followingType.range.upperBound, formatter: self) + } + return baseType } diff --git a/Tests/ParsingHelpersTests.swift b/Tests/ParsingHelpersTests.swift index 5196841a..d4fd687b 100644 --- a/Tests/ParsingHelpersTests.swift +++ b/Tests/ParsingHelpersTests.swift @@ -2397,6 +2397,36 @@ final class ParsingHelpersTests: XCTestCase { XCTAssertEqual(formatter.parseType(at: 2)?.string, "Foo.Bar.Baaz.Quux.InnerType1.InnerType2") } + func testParseModuleQualifiedType() { + let formatter = Formatter(tokenize(""" + let foo: Module::Type + """)) + XCTAssertEqual(formatter.parseType(at: 5)?.string, "Module::Type") + } + + func testParseNestedModuleQualifiedType() { + let formatter = Formatter(tokenize(""" + let foo: Module::Type.Module::Subtype + """)) + XCTAssertEqual(formatter.parseType(at: 5)?.string, "Module::Type.Module::Subtype") + } + + func testParseModuleQualifiedTypeWithNewlineBeforeDoubleColon() { + let formatter = Formatter(tokenize(""" + let foo: NationalAeronauticsAndSpaceAdministration + ::RocketEngine + """)) + XCTAssertEqual(formatter.parseType(at: 5)?.string, "NationalAeronauticsAndSpaceAdministration::RocketEngine") + } + + func testDoesntParseModuleQualifiedTypeWithNewlineAfterDoubleColon() { + let formatter = Formatter(tokenize(""" + let foo: NationalAeronauticsAndSpaceAdministration:: + RocketEngine + """)) + XCTAssertEqual(formatter.parseType(at: 5)?.string, "NationalAeronauticsAndSpaceAdministration") + } + func testParseTuples() { let input = """ let tuple: (foo: Foo, bar: Bar)