Skip to content

Commit 96d8330

Browse files
committed
Replace isSwiftInt with canRepresentBasicType()
canRepresentBasicType() can match against any canonical type named by only dots and identifiers.
1 parent 7eeaf2b commit 96d8330

File tree

2 files changed

+126
-14
lines changed

2 files changed

+126
-14
lines changed

Sources/SwiftSyntax/Utils.swift

+50-14
Original file line numberDiff line numberDiff line change
@@ -103,28 +103,64 @@ extension RawUnexpectedNodesSyntax {
103103
}
104104
}
105105

106-
extension TypeSyntax {
106+
extension TypeSyntaxProtocol {
107+
/// Check if this syntax matches any of the standard names for `Void`:
108+
/// * Void
109+
/// * Swift.Void
110+
/// * ()
107111
public var isVoid: Bool {
108-
switch self.as(TypeSyntaxEnum.self) {
109-
case .identifierType(let identifierType) where identifierType.name.text == "Void": return true
110-
case .tupleType(let tupleType) where tupleType.elements.isEmpty: return true
111-
case .memberType(let memberType) where memberType.name.text == "Void": return memberType.baseType.isSwiftCoreModule
112-
default: return false
112+
if let identifierType = self.as(IdentifierTypeSyntax.self) {
113+
return identifierType.name.text == "Void"
113114
}
114-
}
115-
116-
public var isSwiftInt: Bool {
117-
switch self.as(TypeSyntaxEnum.self) {
118-
case .identifierType(let identifierType) where identifierType.name.text == "Int": return true
119-
case .memberType(let memberType) where memberType.name.text == "Int": return memberType.baseType.isSwiftCoreModule
120-
default: return false
115+
if let memberType = self.as(MemberTypeSyntax.self) {
116+
return memberType.baseType.isSwiftCoreModule && memberType.name.text == "Void"
117+
}
118+
if let tupleType = self.as(TupleTypeSyntax.self) {
119+
return tupleType.elements.isEmpty
121120
}
121+
return false
122122
}
123123

124-
public var isSwiftCoreModule: Bool {
124+
var isSwiftCoreModule: Bool {
125125
guard let identifierType = self.as(IdentifierTypeSyntax.self) else {
126126
return false
127127
}
128128
return identifierType.name.text == "Swift"
129129
}
130+
131+
/// Check if this syntax could resolve to the type passed. Only supports types where the canonical type
132+
/// can be named using only IdentifierTypeSyntax and MemberTypeSyntax. A non-exhaustive list of unsupported
133+
/// types includes:
134+
/// * array types
135+
/// * function types
136+
/// * optional types
137+
/// * tuple types (including Void!)
138+
/// The type syntax is allowed to use any level of qualified name for the type, e.g. Swift.Int.self
139+
/// will match against both "Swift.Int" and "Int".
140+
///
141+
/// - Parameter type: Type to check against. NB: if passing a type alias, the canonical type will be used.
142+
/// - Returns: true if `self` spells out some suffix of the fully qualified name of `type`, otherwise false
143+
public func canRepresentBasicType(type: Any.Type) -> Bool {
144+
let qualifiedTypeName = String(reflecting: type)
145+
var typeNames = qualifiedTypeName.split(separator: ".")
146+
var currType: TypeSyntaxProtocol = self
147+
148+
while !typeNames.isEmpty {
149+
let typeName = typeNames.popLast()!
150+
if let identifierType = currType.as(IdentifierTypeSyntax.self) {
151+
// It doesn't matter whether this is the final element of typeNames, because we don't know
152+
// surrounding context - the Foo.Bar.Baz type can be referred to as `Baz` inside Foo.Bar
153+
return identifierType.name.text == typeName
154+
} else if let memberType = currType.as(MemberTypeSyntax.self) {
155+
if memberType.name.text != typeName {
156+
return false
157+
}
158+
currType = memberType.baseType
159+
} else {
160+
return false
161+
}
162+
}
163+
164+
return false
165+
}
130166
}
+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftSyntax
14+
import XCTest
15+
16+
class UtilsTests: XCTestCase {
17+
18+
public func testIsVoid() {
19+
XCTAssertTrue(TypeSyntax("Void").isVoid)
20+
XCTAssertTrue(TypeSyntax("Swift.Void").isVoid)
21+
XCTAssertTrue(TypeSyntax("()").isVoid)
22+
23+
XCTAssertFalse(TypeSyntax("(Int, Int)").isVoid)
24+
XCTAssertFalse(TypeSyntax("Swift").isVoid)
25+
XCTAssertFalse(TypeSyntax("Swift.()").isVoid)
26+
XCTAssertFalse(TypeSyntax("Int").isVoid)
27+
XCTAssertFalse(TypeSyntax("(())").isVoid)
28+
XCTAssertFalse(TypeSyntax("(Void)").isVoid)
29+
}
30+
31+
public func testIsInt() {
32+
XCTAssertTrue(TypeSyntax("Int").canRepresentBasicType(type: Int.self))
33+
XCTAssertTrue(TypeSyntax("Swift.Int").canRepresentBasicType(type: Int.self))
34+
XCTAssertTrue(TypeSyntax("Int").canRepresentBasicType(type: Swift.Int.self))
35+
XCTAssertTrue(TypeSyntax("Swift.Int").canRepresentBasicType(type: Swift.Int.self))
36+
37+
// Only the canonical type syntax matches
38+
XCTAssertFalse(TypeSyntax("CInt").canRepresentBasicType(type: Int.self))
39+
XCTAssertFalse(TypeSyntax("Swift.CInt").canRepresentBasicType(type: Int.self))
40+
XCTAssertFalse(TypeSyntax("CInt").canRepresentBasicType(type: Swift.Int.self))
41+
XCTAssertFalse(TypeSyntax("Swift.CInt").canRepresentBasicType(type: Swift.Int.self))
42+
}
43+
44+
public func testIsCInt() {
45+
// Match against the canonical type (platform dependent)
46+
XCTAssertEqual(TypeSyntax("Swift.Int").canRepresentBasicType(type: Swift.CInt.self), CInt.self == Int.self)
47+
XCTAssertEqual(TypeSyntax("Int").canRepresentBasicType(type: Swift.CInt.self), CInt.self == Int.self)
48+
XCTAssertEqual(TypeSyntax("Int").canRepresentBasicType(type: CInt.self), CInt.self == Int.self)
49+
XCTAssertEqual(TypeSyntax("Swift.Int").canRepresentBasicType(type: CInt.self), CInt.self == Int.self)
50+
51+
XCTAssertFalse(TypeSyntax("Swift.CInt").canRepresentBasicType(type: Swift.CInt.self))
52+
XCTAssertFalse(TypeSyntax("CInt").canRepresentBasicType(type: Swift.CInt.self))
53+
XCTAssertFalse(TypeSyntax("CInt").canRepresentBasicType(type: CInt.self))
54+
XCTAssertFalse(TypeSyntax("Swift.CInt").canRepresentBasicType(type: CInt.self))
55+
}
56+
57+
public func testIsArrayType() {
58+
// Only plain name types are supported
59+
XCTAssertFalse(TypeSyntax("[Int]").canRepresentBasicType(type: [Int].self))
60+
XCTAssertFalse(TypeSyntax("Int").canRepresentBasicType(type: [Int].self))
61+
}
62+
63+
public func testIsOptionalType() {
64+
// Only plain name types are supported
65+
XCTAssertFalse(TypeSyntax("Int?").canRepresentBasicType(type: Int?.self))
66+
XCTAssertFalse(TypeSyntax("Optional<Int>").canRepresentBasicType(type: Int?.self))
67+
XCTAssertFalse(TypeSyntax("Int").canRepresentBasicType(type: [Int].self))
68+
}
69+
70+
public func testIsTupleTypes() {
71+
// Only plain name types are supported
72+
XCTAssertFalse(TypeSyntax("()").canRepresentBasicType(type: Void.self))
73+
XCTAssertFalse(TypeSyntax("Void").canRepresentBasicType(type: Void.self))
74+
XCTAssertFalse(TypeSyntax("(Int, Int)").canRepresentBasicType(type: (Int, Int).self))
75+
}
76+
}

0 commit comments

Comments
 (0)