expo-modules-macros 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/swift-format.yml +76 -0
- package/AGENTS.md +9 -4
- package/README.md +15 -2
- package/apple/ExpoModulesMacros +0 -0
- package/apple/Package.resolved +3 -3
- package/apple/Package.swift +1 -2
- package/apple/Sources/ExpoModulesMacros/ClosureArguments.swift +39 -14
- package/apple/Sources/ExpoModulesMacros/DecorateModuleBuilder.swift +113 -26
- package/apple/Sources/ExpoModulesMacros/EventMacro.swift +29 -15
- package/apple/Sources/ExpoModulesMacros/ExpoModuleMacro.swift +53 -45
- package/apple/Sources/ExpoModulesMacros/ExpoModulesOptimizedMacro.swift +2 -1
- package/apple/Sources/ExpoModulesMacros/FreeFormTypes.swift +2 -61
- package/apple/Sources/ExpoModulesMacros/JSConstructor.swift +6 -3
- package/apple/Sources/ExpoModulesMacros/JSMacro.swift +180 -44
- package/apple/Sources/ExpoModulesMacros/JSNameCollisions.swift +26 -6
- package/apple/Sources/ExpoModulesMacros/MacroHelpers.swift +163 -42
- package/apple/Sources/ExpoModulesMacros/RecordMacro.swift +285 -221
- package/apple/Sources/ExpoModulesMacros/SharedObjectMacro.swift +45 -32
- package/apple/Sources/ExpoModulesMacros/TypeConformanceAssertion.swift +57 -27
- package/apple/Sources/ExpoModulesMacros/UnionMacro.swift +23 -9
- package/apple/Sources/ExpoModulesMacros/ViewPropsMacro.swift +10 -5
- package/apple/Sources/ExpoModulesScanner/CLI.swift +3 -1
- package/apple/Sources/ExpoModulesScanner/Core/DetectionVisitor.swift +11 -7
- package/apple/Sources/ExpoModulesScanner/Core/ScanBuildConfiguration.swift +2 -1
- package/apple/Sources/ExpoModulesScanner/Core/SourceScan.swift +10 -7
- package/apple/Sources/ExpoModulesScanner/Exports/Availability.swift +6 -3
- package/apple/Sources/ExpoModulesScanner/Exports/ExportedSurface.swift +3 -2
- package/apple/Sources/ExpoModulesScanner/Exports/ResolveRefs.swift +15 -5
- package/apple/Sources/ExpoModulesScanner/Exports/ScanExports.swift +8 -3
- package/apple/Sources/ExpoModulesScanner/Exports/SurfaceVisitor.swift +127 -45
- package/apple/Sources/ExpoModulesScanner/Exports/TypeNode.swift +2 -1
- package/apple/Sources/ExpoModulesScanner/Modules/ScanModules.swift +8 -2
- package/build/types.d.ts +7 -1
- package/package.json +7 -5
|
@@ -35,6 +35,7 @@ public struct JSMacro: PeerMacro {
|
|
|
35
35
|
diagnoseFreeFormTypes(in: declaration, in: context)
|
|
36
36
|
diagnoseClosureTypes(in: declaration, in: context)
|
|
37
37
|
diagnoseConcurrentOption(of: node, on: declaration, in: context)
|
|
38
|
+
diagnoseAsyncVariantOption(of: node, on: declaration, in: context)
|
|
38
39
|
|
|
39
40
|
guard let member = boundaryMember(of: declaration),
|
|
40
41
|
let assertion = directionalConformanceAssertion(
|
|
@@ -43,7 +44,8 @@ public struct JSMacro: PeerMacro {
|
|
|
43
44
|
encodableType: member.encodableType,
|
|
44
45
|
callbackArgumentTypes: member.callbackArgumentTypes,
|
|
45
46
|
isStatic: member.isStatic
|
|
46
|
-
)
|
|
47
|
+
)
|
|
48
|
+
else {
|
|
47
49
|
return []
|
|
48
50
|
}
|
|
49
51
|
return [assertion]
|
|
@@ -74,7 +76,9 @@ private func diagnoseConcurrentOption(
|
|
|
74
76
|
id: "js-concurrent-requires-async",
|
|
75
77
|
severity: .error
|
|
76
78
|
),
|
|
77
|
-
fixIts: [insertAsyncFixIt(for: funcDecl)]
|
|
79
|
+
fixIts: [insertAsyncFixIt(for: funcDecl)]
|
|
80
|
+
)
|
|
81
|
+
)
|
|
78
82
|
return
|
|
79
83
|
}
|
|
80
84
|
context.diagnose(
|
|
@@ -84,7 +88,108 @@ private func diagnoseConcurrentOption(
|
|
|
84
88
|
"'.concurrent' applies only to an 'async' @JS function, not to a property or initializer.",
|
|
85
89
|
id: "js-concurrent-requires-function",
|
|
86
90
|
severity: .error
|
|
87
|
-
)
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// Emits the diagnostics for `@JS(.withAsync("name"))` on a member that can't take it. The option
|
|
97
|
+
/// adds a promise-returning variant that calls the function on the concurrent pool, so it needs a
|
|
98
|
+
/// synchronous function without a global actor, and the variant's JavaScript name as a string literal.
|
|
99
|
+
private func diagnoseAsyncVariantOption(
|
|
100
|
+
of node: AttributeSyntax,
|
|
101
|
+
on declaration: some DeclSyntaxProtocol,
|
|
102
|
+
in context: some MacroExpansionContext
|
|
103
|
+
) {
|
|
104
|
+
guard let option = asyncVariantOption(of: node) else {
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
guard let funcDecl = declaration.as(FunctionDeclSyntax.self) else {
|
|
108
|
+
context.diagnose(
|
|
109
|
+
Diagnostic(
|
|
110
|
+
node: node,
|
|
111
|
+
message: JSDiagnosticMessage(
|
|
112
|
+
"'.withAsync' applies only to a synchronous @JS function, not to a property or initializer.",
|
|
113
|
+
id: "js-async-variant-requires-function",
|
|
114
|
+
severity: .error
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
if funcDecl.signature.effectSpecifiers?.asyncSpecifier != nil {
|
|
121
|
+
context.diagnose(
|
|
122
|
+
Diagnostic(
|
|
123
|
+
node: node,
|
|
124
|
+
message: JSDiagnosticMessage(
|
|
125
|
+
"'.withAsync' adds a promise-returning variant to a synchronous function, and this function is already 'async'. Remove '.withAsync', and use '.concurrent' to run the function off the JavaScript thread.",
|
|
126
|
+
id: "js-async-variant-on-async-function",
|
|
127
|
+
severity: .error
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
for duplicate in asyncVariantOptions(of: node).dropFirst() {
|
|
134
|
+
context.diagnose(
|
|
135
|
+
Diagnostic(
|
|
136
|
+
node: duplicate,
|
|
137
|
+
message: JSDiagnosticMessage(
|
|
138
|
+
"'.withAsync' can be listed only once: a function gets one promise-returning variant.",
|
|
139
|
+
id: "js-async-variant-duplicate",
|
|
140
|
+
severity: .error
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
if asyncVariantName(of: option) == nil {
|
|
146
|
+
context.diagnose(
|
|
147
|
+
Diagnostic(
|
|
148
|
+
node: option,
|
|
149
|
+
message: JSDiagnosticMessage(
|
|
150
|
+
"'.withAsync' takes the JavaScript name of the variant as a string literal, for example '.withAsync(\"loadAsync\")'.",
|
|
151
|
+
id: "js-async-variant-name",
|
|
152
|
+
severity: .error
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
if let isolation = globalActorIsolation(of: funcDecl, in: context) {
|
|
158
|
+
context.diagnose(
|
|
159
|
+
Diagnostic(
|
|
160
|
+
node: node,
|
|
161
|
+
message: JSDiagnosticMessage(
|
|
162
|
+
isolation.enclosingTypeName.map {
|
|
163
|
+
"'.withAsync' calls the function on the concurrent thread pool, so the function can't inherit '\(isolation.attribute)' from '\($0)'. Mark the function 'nonisolated'."
|
|
164
|
+
}
|
|
165
|
+
?? "'.withAsync' calls the function on the concurrent thread pool, so the function can't be isolated to '\(isolation.attribute)'.",
|
|
166
|
+
id: "js-async-variant-isolated",
|
|
167
|
+
severity: .error
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// The global actor a function is isolated to, written on the function itself or inherited from its
|
|
175
|
+
/// enclosing type, or `nil` when it has none or is `nonisolated`. `enclosingTypeName` is set when the
|
|
176
|
+
/// isolation is inherited.
|
|
177
|
+
private func globalActorIsolation(
|
|
178
|
+
of funcDecl: FunctionDeclSyntax,
|
|
179
|
+
in context: some MacroExpansionContext
|
|
180
|
+
) -> (attribute: String, enclosingTypeName: String?)? {
|
|
181
|
+
if let attribute = funcDecl.attributes.first(where: hasGlobalActorShape) {
|
|
182
|
+
return (attribute.trimmedDescription, nil)
|
|
183
|
+
}
|
|
184
|
+
if funcDecl.modifiers.contains(where: { $0.name.text == "nonisolated" }) {
|
|
185
|
+
return nil
|
|
186
|
+
}
|
|
187
|
+
guard let enclosing = context.lexicalContext.first,
|
|
188
|
+
let attribute = enclosing.asProtocol(WithAttributesSyntax.self)?.attributes.first(where: hasGlobalActorShape)
|
|
189
|
+
else {
|
|
190
|
+
return nil
|
|
191
|
+
}
|
|
192
|
+
return (attribute.trimmedDescription, enclosing.asProtocol(NamedDeclSyntax.self)?.name.text)
|
|
88
193
|
}
|
|
89
194
|
|
|
90
195
|
/// The fix-it offered alongside the synchronous-function diagnostic: insert `async` into the
|
|
@@ -144,7 +249,9 @@ private func diagnoseParameterSpecifiers(
|
|
|
144
249
|
"A @JS parameter can't be '\(specifier)': its argument is a value decoded from JavaScript, which can't be written back or carry an isolation.",
|
|
145
250
|
id: "js-parameter-specifier",
|
|
146
251
|
severity: .error
|
|
147
|
-
)
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
)
|
|
148
255
|
}
|
|
149
256
|
}
|
|
150
257
|
|
|
@@ -173,14 +280,16 @@ private func diagnoseFreeFormTypes(
|
|
|
173
280
|
// A constructor decodes its arguments exactly like a function and has no return value.
|
|
174
281
|
types = initDecl.signature.parameterClause.parameters.map { SpecifiedType($0.type).type }
|
|
175
282
|
} else if let varDecl = declaration.as(VariableDeclSyntax.self),
|
|
176
|
-
let type = varDecl.bindings.first?.typeAnnotation?.type
|
|
283
|
+
let type = varDecl.bindings.first?.typeAnnotation?.type
|
|
284
|
+
{
|
|
177
285
|
types = [type]
|
|
178
286
|
}
|
|
179
287
|
|
|
180
288
|
for type in types
|
|
181
289
|
where mentionsFreeFormAny(type) && !isConvertibleFreeFormType(type) && !containsFunctionType(type) {
|
|
182
290
|
context.diagnose(
|
|
183
|
-
Diagnostic(node: type, message: unconvertibleFreeFormError(for: type.trimmedDescription))
|
|
291
|
+
Diagnostic(node: type, message: unconvertibleFreeFormError(for: type.trimmedDescription))
|
|
292
|
+
)
|
|
184
293
|
}
|
|
185
294
|
}
|
|
186
295
|
|
|
@@ -234,61 +343,49 @@ private struct BoundaryMember {
|
|
|
234
343
|
|
|
235
344
|
/// Reads the boundary member off a `@JS` declaration, splitting its types by conversion direction. A
|
|
236
345
|
/// function contributes its parameter types (decodable) and its return type when non-Void (encodable);
|
|
237
|
-
///
|
|
238
|
-
/// (the setter). Composed types (`[Int]`, `String?`,
|
|
239
|
-
/// conformances transitively constraining the elements. A type
|
|
240
|
-
/// converts through the free-form methods, or is diagnosed by
|
|
241
|
-
/// for declaration kinds `@JS` doesn't read types from, a
|
|
242
|
-
/// syntactic macro can't recover it), or one whose type
|
|
346
|
+
/// an initializer contributes its parameter types; a property contributes its value type as encodable
|
|
347
|
+
/// (the getter) and also as decodable when settable (the setter). Composed types (`[Int]`, `String?`,
|
|
348
|
+
/// …) are kept verbatim, their conditional conformances transitively constraining the elements. A type
|
|
349
|
+
/// that mentions `Any` is left out: it converts through the free-form methods, or is diagnosed by
|
|
350
|
+
/// `diagnoseFreeFormTypes`. Returns `nil` for declaration kinds `@JS` doesn't read types from, a
|
|
351
|
+
/// property whose type isn't spelled out (a syntactic macro can't recover it), or one whose type
|
|
352
|
+
/// mentions `Any`.
|
|
243
353
|
private func boundaryMember(of declaration: some DeclSyntaxProtocol) -> BoundaryMember? {
|
|
244
354
|
if let funcDecl = declaration.as(FunctionDeclSyntax.self) {
|
|
245
|
-
|
|
246
|
-
// A closure in any other position is diagnosed by `diagnoseClosureTypes` and left out here, since
|
|
247
|
-
// its metatype isn't a valid expression in the assertion.
|
|
248
|
-
var decodableTypes: [String] = []
|
|
249
|
-
var callbackArgumentTypes: [String] = []
|
|
250
|
-
for parameter in funcDecl.signature.parameterClause.parameters {
|
|
251
|
-
// The specifier isn't part of the asserted type, and a parameter with one the binding can't honor
|
|
252
|
-
// is reported by `diagnoseParameterSpecifiers` and not asserted.
|
|
253
|
-
let specified = SpecifiedType(parameter.type)
|
|
254
|
-
guard specified.unsupportedSpecifier == nil else {
|
|
255
|
-
continue
|
|
256
|
-
}
|
|
257
|
-
guard let closure = JSClosureType(specified.type) else {
|
|
258
|
-
if !containsFunctionType(specified.type) && !mentionsFreeFormAny(specified.type) {
|
|
259
|
-
decodableTypes.append(specified.type.trimmedDescription)
|
|
260
|
-
}
|
|
261
|
-
continue
|
|
262
|
-
}
|
|
263
|
-
// A closure type that mentions `Any` is reported by `diagnoseClosureTypes` instead.
|
|
264
|
-
for parameter in closure.parameters
|
|
265
|
-
where !containsFunctionType(parameter.type) && !mentionsFreeFormAny(parameter.type) {
|
|
266
|
-
callbackArgumentTypes.append(parameter.type.trimmedDescription)
|
|
267
|
-
}
|
|
268
|
-
let closureReturnType = closure.functionType.returnClause.type
|
|
269
|
-
if let returnType = closure.returnType,
|
|
270
|
-
!containsFunctionType(closureReturnType) && !mentionsFreeFormAny(closureReturnType) {
|
|
271
|
-
decodableTypes.append(returnType)
|
|
272
|
-
}
|
|
273
|
-
}
|
|
355
|
+
let argumentTypes = argumentBoundaryTypes(of: funcDecl.signature.parameterClause.parameters)
|
|
274
356
|
let returnType = funcDecl.signature.returnClause?.type
|
|
275
357
|
let encodableType = returnType.flatMap {
|
|
276
358
|
isVoidType($0) || containsFunctionType($0) || mentionsFreeFormAny($0) ? nil : $0.trimmedDescription
|
|
277
359
|
}
|
|
278
360
|
return BoundaryMember(
|
|
279
361
|
name: funcDecl.name.text,
|
|
280
|
-
decodableTypes:
|
|
362
|
+
decodableTypes: argumentTypes.decodable,
|
|
281
363
|
encodableType: encodableType,
|
|
282
|
-
callbackArgumentTypes:
|
|
364
|
+
callbackArgumentTypes: argumentTypes.callbackArguments,
|
|
283
365
|
isStatic: isTypeLevel(funcDecl.modifiers)
|
|
284
366
|
)
|
|
285
367
|
}
|
|
286
368
|
|
|
369
|
+
if let initDecl = declaration.as(InitializerDeclSyntax.self) {
|
|
370
|
+
// A constructor decodes its arguments like a function does, and the instance it returns isn't
|
|
371
|
+
// encoded. The name is escaped because it also names the assertion's nested helper, and `init` is
|
|
372
|
+
// a keyword.
|
|
373
|
+
let argumentTypes = argumentBoundaryTypes(of: initDecl.signature.parameterClause.parameters)
|
|
374
|
+
return BoundaryMember(
|
|
375
|
+
name: "`init`",
|
|
376
|
+
decodableTypes: argumentTypes.decodable,
|
|
377
|
+
encodableType: nil,
|
|
378
|
+
callbackArgumentTypes: argumentTypes.callbackArguments,
|
|
379
|
+
isStatic: false
|
|
380
|
+
)
|
|
381
|
+
}
|
|
382
|
+
|
|
287
383
|
if let varDecl = declaration.as(VariableDeclSyntax.self),
|
|
288
384
|
let binding = varDecl.bindings.first,
|
|
289
385
|
let identifier = binding.pattern.as(IdentifierPatternSyntax.self),
|
|
290
386
|
let type = binding.typeAnnotation?.type,
|
|
291
|
-
!containsFunctionType(type)
|
|
387
|
+
!containsFunctionType(type)
|
|
388
|
+
{
|
|
292
389
|
// A `let`, or a `var` with no setter, is read-only (encodable only). A settable `var` is also
|
|
293
390
|
// decoded on write, so its value type is asserted in both directions. A type that mentions `Any`
|
|
294
391
|
// converts through the free-form methods instead, so it isn't asserted at all.
|
|
@@ -309,6 +406,45 @@ private func boundaryMember(of declaration: some DeclSyntaxProtocol) -> Boundary
|
|
|
309
406
|
return nil
|
|
310
407
|
}
|
|
311
408
|
|
|
409
|
+
/// The boundary types of a function's or constructor's arguments: an argument is decoded from JS, and
|
|
410
|
+
/// a closure argument instead contributes its parameter types (encoded when native code calls it) and
|
|
411
|
+
/// its return type (decoded).
|
|
412
|
+
private func argumentBoundaryTypes(
|
|
413
|
+
of parameters: FunctionParameterListSyntax
|
|
414
|
+
) -> (decodable: [String], callbackArguments: [String]) {
|
|
415
|
+
// A closure argument moves data the other way: JS receives its parameters and returns its result.
|
|
416
|
+
// A closure in any other position is diagnosed by `diagnoseClosureTypes` and left out here, since
|
|
417
|
+
// its metatype isn't a valid expression in the assertion.
|
|
418
|
+
var decodableTypes: [String] = []
|
|
419
|
+
var callbackArgumentTypes: [String] = []
|
|
420
|
+
for parameter in parameters {
|
|
421
|
+
// The specifier isn't part of the asserted type, and a parameter with one the binding can't honor
|
|
422
|
+
// is reported by `diagnoseParameterSpecifiers` and not asserted.
|
|
423
|
+
let specified = SpecifiedType(parameter.type)
|
|
424
|
+
guard specified.unsupportedSpecifier == nil else {
|
|
425
|
+
continue
|
|
426
|
+
}
|
|
427
|
+
guard let closure = JSClosureType(specified.type) else {
|
|
428
|
+
if !containsFunctionType(specified.type) && !mentionsFreeFormAny(specified.type) {
|
|
429
|
+
decodableTypes.append(specified.type.trimmedDescription)
|
|
430
|
+
}
|
|
431
|
+
continue
|
|
432
|
+
}
|
|
433
|
+
// A closure type that mentions `Any` is reported by `diagnoseClosureTypes` instead.
|
|
434
|
+
for parameter in closure.parameters
|
|
435
|
+
where !containsFunctionType(parameter.type) && !mentionsFreeFormAny(parameter.type) {
|
|
436
|
+
callbackArgumentTypes.append(parameter.type.trimmedDescription)
|
|
437
|
+
}
|
|
438
|
+
let closureReturnType = closure.functionType.returnClause.type
|
|
439
|
+
if let returnType = closure.returnType,
|
|
440
|
+
!containsFunctionType(closureReturnType) && !mentionsFreeFormAny(closureReturnType)
|
|
441
|
+
{
|
|
442
|
+
decodableTypes.append(returnType)
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return (decodableTypes, callbackArgumentTypes)
|
|
446
|
+
}
|
|
447
|
+
|
|
312
448
|
/// True when a return clause is written as `Void` / `()` — nothing crosses the boundary, so it needs
|
|
313
449
|
/// no conformance assertion. (A missing return clause never reaches here: `returnClause` is `nil`.)
|
|
314
450
|
private func isVoidType(_ type: TypeSyntax) -> Bool {
|
|
@@ -13,9 +13,7 @@ internal func diagnoseJSNameCollisions(
|
|
|
13
13
|
// Each member binds with its own `setProperty` or `defineProperty` call, so of two members with one
|
|
14
14
|
// name, JavaScript would reach only one. Members are compared in source order, so the error lands on
|
|
15
15
|
// every member after the first, whichever kind each one is.
|
|
16
|
-
let members =
|
|
17
|
-
functions.map { BoundMember(jsName: $0.jsName, kind: "function", nameToken: $0.nameToken) }
|
|
18
|
-
+ properties.map { BoundMember(jsName: $0.jsName, kind: "property", nameToken: $0.nameToken) }
|
|
16
|
+
let members = functions.map(BoundMember.init(function:)) + properties.map(BoundMember.init(property:))
|
|
19
17
|
var firstMembers: [String: BoundMember] = [:]
|
|
20
18
|
|
|
21
19
|
for member in members.sorted(by: { $0.nameToken.position < $1.nameToken.position }) {
|
|
@@ -23,25 +21,47 @@ internal func diagnoseJSNameCollisions(
|
|
|
23
21
|
firstMembers[member.jsName] = member
|
|
24
22
|
continue
|
|
25
23
|
}
|
|
24
|
+
let renaming =
|
|
25
|
+
first.renaming == member.renaming ? first.renaming : "\(first.renaming) or \(member.renaming)"
|
|
26
26
|
context.diagnose(
|
|
27
27
|
Diagnostic(
|
|
28
28
|
node: member.nameToken,
|
|
29
29
|
message: JSNameCollisionMessage(
|
|
30
|
-
"The JavaScript name '\(member.jsName)' is already bound by an earlier
|
|
30
|
+
"The JavaScript name '\(member.jsName)' is already bound by an earlier \(first.kind). A JavaScript object holds one member per name, so JavaScript could reach only one of them. Give one of them another name with \(renaming)."
|
|
31
31
|
),
|
|
32
32
|
notes: [
|
|
33
33
|
Note(
|
|
34
34
|
node: Syntax(first.nameToken),
|
|
35
|
-
message: JSNameCollisionNote("'\(member.jsName)' is first bound here")
|
|
35
|
+
message: JSNameCollisionNote("'\(member.jsName)' is first bound here")
|
|
36
|
+
)
|
|
36
37
|
]
|
|
37
|
-
)
|
|
38
|
+
)
|
|
39
|
+
)
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
private struct BoundMember {
|
|
42
44
|
let jsName: String
|
|
45
|
+
/// How the message names the member, such as `@JS function`.
|
|
43
46
|
let kind: String
|
|
47
|
+
/// The spelling that gives the member another JavaScript name.
|
|
48
|
+
let renaming: String
|
|
44
49
|
let nameToken: TokenSyntax
|
|
50
|
+
|
|
51
|
+
init(function: JSFunction) {
|
|
52
|
+
// A `.withAsync` variant takes its JavaScript name from the option, not from `@JS("...")`.
|
|
53
|
+
self.jsName = function.jsName
|
|
54
|
+
self.kind = function.isAsyncVariant ? "'.withAsync' variant" : "@JS function"
|
|
55
|
+
self.renaming = function.isAsyncVariant ? ".withAsync(\"...\")" : "@JS(\"...\")"
|
|
56
|
+
self.nameToken = function.nameToken
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
init(property: JSProperty) {
|
|
60
|
+
self.jsName = property.jsName
|
|
61
|
+
self.kind = "@JS property"
|
|
62
|
+
self.renaming = "@JS(\"...\")"
|
|
63
|
+
self.nameToken = property.nameToken
|
|
64
|
+
}
|
|
45
65
|
}
|
|
46
66
|
|
|
47
67
|
private struct JSNameCollisionMessage: DiagnosticMessage {
|