STUDIO HAZE · topaz
Topaz source
Open by house policy. Topaz is our own language.
Topaz 5.20 · 56 files
compiler/crates/topaz_self_frontend/topaz/checker/call_facts.tpz
Topaz · 43100 B · Original file
1
// Owns typed-call observations and the expected-expression and pipe stacks.2
// call_plan selects signatures; call_complete performs argument solving, keeping3
// the mutually recursive expression service out of both modules.5
import types {6
PreviewCallArgument,7
PreviewCallEvaluation,8
PreviewResolvedReference,9
PreviewTypedCall,10
SyntaxNode,11
}12
import checker_types { TypeValue }13
import unicode { didYouMean }14
import checker.model {15
BuiltinFunction,16
CallSignature,17
CheckerService,18
CheckerState,19
ExpectedExpressionContext,20
ProtocolDefinition,21
constructor,22
emptyType,23
functionType,24
literal,25
primitive,26
rigid,27
serviceInferExpression,28
}29
import checker.type_algebra {30
assignable,31
flattenType,32
flattenedHasHole,33
hasHole,34
typeText,35
widen,36
}37
import checker.syntax_index {38
addDiagnostic,39
addNode,40
child,41
children,42
declarationFor,43
moduleAt,44
protocolDefinition,45
referenceFor,46
replaceNodeType,47
resolvedTypeFromFact,48
scopeFor,49
scopeIsWithin,50
spanKey,51
symbolTypeAt,52
text,53
}54
import checker.protocols_methods {55
receiverMethodTargetIdentity,56
referenceTargetType,57
}58
import checker.members_capabilities {59
builtinNamespaceFamily,60
iterableElement,61
}63
export function argumentValue(argument: SyntaxNode) -> SyntaxNode {64
if argument.kind == "call-argument/named" || argument.kind == "call-argument/spread" {65
match child(argument, "value") {66
case Some(found) => found67
case None => argument68
}69
} else {70
argument71
}72
}74
// A declaration signature belongs to the callable itself. References used75
// while evaluating a receiver or producing a callable are different calls.76
export function calleeReference(77
state: CheckerState,78
moduleIndex: int,79
node: SyntaxNode,80
) -> Option<PreviewResolvedReference> {81
if node.kind == "expression/identifier" {82
return referenceFor(state, moduleIndex, node.lo, node.hi)83
}84
if node.kind == "expression/member" {85
match child(node, "field") {86
case Some(field) => return referenceFor(state, moduleIndex, field.lo, field.hi)87
case None => ()88
}89
}90
None91
}93
function callableReferenceInNode(94
state: CheckerState,95
moduleIndex: int,96
node: SyntaxNode,97
) -> Option<PreviewResolvedReference> {98
if node.kind == "expression/identifier" {99
return referenceFor(state, moduleIndex, node.lo, node.hi)100
}101
if node.kind == "expression/parenthesized" {102
return match child(node, "operand") {103
case Some(operand) => callableReferenceInNode(state, moduleIndex, operand)104
case None => None105
}106
}107
if node.kind == "expression/member" ||108
node.kind == "expression/optional-access" {109
return match child(node, "field") {110
case Some(field) => referenceFor(state, moduleIndex, field.lo, field.hi)111
case None => None112
}113
}114
None115
}117
function referenceTargetIdentity(118
state: CheckerState,119
reference: PreviewResolvedReference,120
) -> string {121
match declarationFor(122
state,123
reference.targetModuleIndex,124
reference.targetLo,125
reference.targetHi,126
) {127
case Some(declaration) => if declaration.targetModule != "" &&128
declaration.targetName != "" {129
"{declaration.targetModule}::{declaration.targetName}"130
} else if reference.targetModule != "" && reference.targetName != "" {131
"{reference.targetModule}::{reference.targetName}"132
} else {133
""134
}135
case None => if reference.targetModule != "" && reference.targetName != "" {136
"{reference.targetModule}::{reference.targetName}"137
} else {138
""139
}140
}141
}143
function callMethodClass(name: string) -> string {144
match name {145
case "map" | "filter" | "reduce" | "flatMap" => "higher-order"146
case "okOrElse" => "lazy-callback"147
case "push" | "insert" | "set" | "remove" | "update" | "clear" |148
"extend" | "add" => "mutator"149
case "read" | "write" | "close" | "open" => "resource"150
case _ => "other"151
}152
}154
export function builtinMutator(receiver: TypeValue, name: string) -> bool {155
if receiver.kind == "union" {156
for member in receiver.members {157
if builtinMutator(member, name) {158
return true159
}160
}161
return false162
}163
if receiver.kind == "constructor" && receiver.name == "Array" {164
return name == "push" || name == "pop" || name == "clear" ||165
name == "reverse" || name == "insert" || name == "removeAt" ||166
name == "sort" || name == "sortBy" || name == "retain"167
}168
if receiver.kind == "constructor" && receiver.name == "Map" {169
return name == "insert" || name == "remove" || name == "clear" ||170
name == "update"171
}172
if receiver.kind == "constructor" && receiver.name == "Set" {173
return name == "add" || name == "remove" || name == "clear"174
}175
receiver.kind == "byte-buffer" &&176
(name == "set" || name == "fill" || name == "copy")177
}179
function requiredBuiltinFunction(180
parameters: Array<TypeValue>,181
result: TypeValue,182
parameterNames: Array<string>,183
) -> BuiltinFunction {184
BuiltinFunction {185
typeValue: functionType(parameters, [], result),186
callSignature: requiredCallSignature(parameterNames, false),187
}188
}190
function defaultedBuiltinFunction(191
parameters: Array<TypeValue>,192
result: TypeValue,193
parameterNames: Array<string>,194
parameterDefaults: Array<bool>,195
) -> BuiltinFunction {196
BuiltinFunction {197
typeValue: functionType(parameters, [], result),198
callSignature: defaultedCallSignature(199
parameterNames,200
parameterDefaults,201
),202
}203
}205
export function builtinFunction(name: string) -> Option<BuiltinFunction> {206
match name {207
case "print" => Some(requiredBuiltinFunction(208
[primitive("string")],209
primitive("unit"),210
["value"],211
))212
case "toInt" => Some(requiredBuiltinFunction(213
[primitive("string")],214
constructor("Option", [primitive("int")]),215
["text"],216
))217
case "toIntRadix" => Some(requiredBuiltinFunction(218
[primitive("string"), primitive("int")],219
constructor("Option", [primitive("int")]),220
["text", "radix"],221
))222
case "fromCodePoint" => Some(requiredBuiltinFunction(223
[primitive("int")],224
constructor("Option", [primitive("string")]),225
["n"],226
))227
case "toFloat" => Some(requiredBuiltinFunction(228
[primitive("int")],229
primitive("float"),230
["n"],231
))232
case "map" => {233
let input = rigid("T", "builtin::map")234
let output = rigid("U", "builtin::map")235
Some(requiredBuiltinFunction(236
[237
emptyType("unknown"),238
functionType([input], [], output),239
],240
constructor("Array", [output]),241
["xs", "f"],242
))243
}244
case "filter" => {245
let input = rigid("T", "builtin::filter")246
Some(requiredBuiltinFunction(247
[248
emptyType("unknown"),249
functionType([input], [], primitive("bool")),250
],251
constructor("Array", [input]),252
["xs", "f"],253
))254
}255
case "reduce" => {256
let input = rigid("T", "builtin::reduce")257
let output = rigid("U", "builtin::reduce")258
Some(requiredBuiltinFunction(259
[260
emptyType("unknown"),261
output,262
functionType([output, input], [], output),263
],264
output,265
["xs", "initial", "f"],266
))267
}268
case "input" => Some(requiredBuiltinFunction(269
[],270
primitive("string"),271
[],272
))273
case "open" => Some(requiredBuiltinFunction(274
[primitive("string")],275
constructor("Result", [emptyType("file"), primitive("string")]),276
["path"],277
))278
case "assert" => Some(defaultedBuiltinFunction(279
[primitive("bool"), primitive("string")],280
primitive("unit"),281
["condition", "message"],282
[false, true],283
))284
case "Some" => {285
let value = rigid("T", "builtin::Some")286
Some(requiredBuiltinFunction(287
[value],288
constructor("Option", [value]),289
["value"],290
))291
}292
case "Ok" => {293
let value = rigid("T", "builtin::Ok")294
let error = rigid("E", "builtin::Ok")295
Some(requiredBuiltinFunction(296
[value],297
constructor("Result", [value, error]),298
["value"],299
))300
}301
case "Err" => {302
let value = rigid("T", "builtin::Err")303
let error = rigid("E", "builtin::Err")304
Some(requiredBuiltinFunction(305
[error],306
constructor("Result", [value, error]),307
["error"],308
))309
}310
case _ => None311
}312
}314
function builtinFunctionType(name: string) -> Option<TypeValue> {315
match builtinFunction(name) {316
case Some(found) => Some(found.typeValue)317
case None => None318
}319
}321
export function builtinIdentifierType(name: string) -> Option<TypeValue> {322
match builtinFunctionType(name) {323
case Some(found) => if name == "Some" || name == "Ok" ||324
name == "Err" || name == "map" || name == "filter" ||325
name == "reduce" {326
Some(emptyType("unknown"))327
} else {328
Some(found)329
}330
case None => None331
}332
}334
export function isBuiltinNamespace(name: string) -> bool {335
builtinNamespaceFamily(name) != ""336
}338
function unboundFunctionSuggestionNames() -> Array<string> {339
[340
"print",341
"toInt",342
"toIntRadix",343
"fromCodePoint",344
"toFloat",345
"map",346
"filter",347
"reduce",348
"input",349
"open",350
"assert",351
"Some",352
"Ok",353
"Err",354
]355
}357
export function unboundSuggestion(358
state: CheckerState,359
reference: PreviewResolvedReference,360
) -> string {361
let mut candidates: Array<string> = []362
match state.valueDeclarationIndexesByModule.get("{reference.moduleIndex}") {363
case Some(indexes) => for index in indexes {364
match state.resolved.declarations.get(index) {365
case Some(declaration) => if scopeIsWithin(366
state,367
reference.moduleIndex,368
reference.scopeOrdinal,369
declaration.scopeOrdinal,370
) {371
candidates.push(declaration.name)372
}373
case None => ()374
}375
}376
case None => ()377
}378
for candidate in unboundFunctionSuggestionNames() {379
candidates.push(candidate)380
}381
candidates.push("None")382
didYouMean(reference.name, candidates)383
}385
export function unboundCalleeSuggestion(386
state: CheckerState,387
reference: PreviewResolvedReference,388
) -> string {389
let mut candidates: Array<string> = []390
let mut seen: Set<string> = set {}391
let mut scopeOrdinal = reference.scopeOrdinal392
while scopeOrdinal >= 0 {393
match state.valueDeclarationIndexesByModule.get("{reference.moduleIndex}") {394
case Some(indexes) => for index in indexes {395
match state.resolved.declarations.get(index) {396
case Some(declaration) => if declaration.scopeOrdinal == scopeOrdinal {397
match symbolTypeAt(398
state,399
reference.moduleIndex,400
declaration.lo,401
declaration.hi,402
) {403
case Some(typeValue) => if !seen.contains(declaration.name) {404
seen.add(declaration.name)405
if typeValue.kind == "function" {406
candidates.push(declaration.name)407
}408
}409
case None => ()410
}411
}412
case None => ()413
}414
}415
case None => ()416
}417
scopeOrdinal = match scopeFor(state, reference.moduleIndex, scopeOrdinal) {418
case Some(scope) => scope.parentOrdinal419
case None => -1420
}421
}422
for candidate in unboundFunctionSuggestionNames() {423
if !seen.contains(candidate) {424
candidates.push(candidate)425
}426
}427
didYouMean(reference.name, candidates)428
}430
export function diagnoseUnboundReferences(state: CheckerState) -> () {431
for reference in state.resolved.references {432
if reference.targetModuleIndex >= 0 ||433
reference.namespace != "value" ||434
reference.role == "read" ||435
generatedLispexIntrinsic(state, reference.moduleIndex, reference.name) ||436
reference.name == "None" ||437
isBuiltinNamespace(reference.name) ||438
match builtinIdentifierType(reference.name) {439
case Some(_) => true440
case None => false441
} {442
continue443
}444
addDiagnostic(445
state,446
reference.moduleIndex,447
"TPZ5002",448
"`{reference.name}` is not bound{unboundSuggestion(state, reference)}",449
"",450
SyntaxNode {451
kind: "identifier",452
lo: reference.lo,453
hi: reference.hi,454
field: "",455
index: 0,456
attributes: [],457
children: [],458
},459
)460
}461
()462
}464
export function currentFunctionReturn(state: CheckerState) -> Option<TypeValue> {465
if state.expectedReturns.length == 0 {466
return None467
}468
state.expectedReturns.get(state.expectedReturns.length - 1)469
}471
export function isBareBindingExpression(state: CheckerState, node: SyntaxNode) -> bool {472
match state.bareBindingExpressions.get(state.bareBindingExpressions.length - 1) {473
case Some(value) => value.kind == node.kind && value.lo == node.lo && value.hi == node.hi474
case None => false475
}476
}478
export function currentExpectedExpression(479
state: CheckerState,480
expression: SyntaxNode,481
) -> Option<TypeValue> {482
if state.expectedExpressions.length == 0 {483
return None484
}485
match state.expectedExpressions.get(state.expectedExpressions.length - 1) {486
case Some(context) => if context.kind == expression.kind &&487
context.lo == expression.lo && context.hi == expression.hi {488
Some(context.typeValue)489
} else {490
None491
}492
case None => None493
}494
}496
export function inferExpressionWithExpected(497
service: CheckerService,498
state: CheckerState,499
moduleIndex: int,500
source: Bytes,501
expression: SyntaxNode,502
expected: TypeValue,503
contextual: bool,504
) -> TypeValue {505
let mut expectedExpressions = state.expectedExpressions506
expectedExpressions.push(ExpectedExpressionContext {507
kind: expression.kind,508
lo: expression.lo,509
hi: expression.hi,510
typeValue: expected,511
})512
let inferred = if contextual {513
inferExpressionContext(service, state, moduleIndex, source, expression)514
} else {515
serviceInferExpression(service, state, moduleIndex, source, expression)516
}517
expectedExpressions.pop()518
inferred519
}521
function appendReturnType(state: CheckerState, value: TypeValue) -> () {522
if state.returnJoins.length == 0 {523
return ()524
}525
let index = state.returnJoins.length - 1526
match state.returnJoins.get(index) {527
case Some(found) => {528
let mut values = found529
values.push(value)530
let mut joins = state.returnJoins531
joins[index] = values532
}533
case None => ()534
}535
()536
}538
export function checkReturn(539
service: CheckerService,540
state: CheckerState,541
moduleIndex: int,542
source: Bytes,543
node: SyntaxNode,544
) -> () {545
let value = child(node, "value")546
match currentFunctionReturn(state) {547
case None => {548
addDiagnostic(549
state,550
moduleIndex,551
"TPZ5001",552
"`return` outside a function",553
"",554
node,555
)556
match value {557
case Some(found) => {558
let _ = serviceInferExpression(service, state, moduleIndex, source, found)559
}560
case None => ()561
}562
}563
case Some(expected) => if expected.kind == "unknown" {564
let inferred = match value {565
case Some(found) => serviceInferExpression(service, state, moduleIndex, source, found)566
case None => primitive("unit")567
}568
appendReturnType(state, inferred)569
} else {570
match value {571
case Some(found) => {572
let _ = checkExpressionExpected(573
service,574
state,575
moduleIndex,576
source,577
found,578
expected,579
)580
}581
case None => reportExpectedType(582
state,583
moduleIndex,584
node,585
expected,586
primitive("unit"),587
)588
}589
}590
}591
()592
}594
export function builtinCallTypeParameters(595
service: CheckerService,596
state: CheckerState,597
moduleIndex: int,598
source: Bytes,599
callee: SyntaxNode,600
receiverType: TypeValue,601
) -> Array<string> {602
let empty: Array<string> = []603
if callee.kind == "expression/identifier" {604
if !unshadowedIdentifier(state, moduleIndex, callee) {605
return empty606
}607
return match text(source, callee) {608
case "Some" => ["T"]609
case "Ok" | "Err" => ["T", "E"]610
case "map" | "reduce" => ["T", "U"]611
case "filter" => ["T"]612
case _ => empty613
}614
}615
if callee.kind != "expression/member" {616
return empty617
}618
let object = match child(callee, "object") {619
case Some(found) => found620
case None => return empty621
}622
let field = match child(callee, "field") {623
case Some(found) => text(source, found)624
case None => return empty625
}626
if !builtinNamespaceObject(service, state, moduleIndex, source, object) {627
if receiverType.kind != "constructor" {628
return empty629
}630
return match receiverType.name {631
case "Option" => match field {632
case "okOr" | "okOrElse" | "map" | "flatMap" => ["U"]633
case _ => empty634
}635
case "Result" => match field {636
case "map" | "flatMap" => ["U"]637
case _ => empty638
}639
case "Array" => match field {640
case "map" | "reduce" => ["U"]641
case "sortedBy" | "sortBy" => ["K"]642
case _ => empty643
}644
case "Map" => if field == "mapValues" { ["W"] } else { empty }645
case _ => empty646
}647
}648
match text(source, object) {649
case "Array" | "Set" => if field == "of" { ["T"] } else { empty }650
case "Map" => if field == "new" || field == "ofEntries" {651
["K", "V"]652
} else {653
empty654
}655
case "JSON" => if field == "parseAs" || field == "decode" ||656
field == "stringify" {657
["T"]658
} else {659
empty660
}661
case "Test" => match field {662
case "assertEq" | "assertNe" | "assertSome" | "assertNone" => ["T"]663
case "assertOk" | "assertErr" => ["T", "E"]664
case _ => empty665
}666
case _ => empty667
}668
}670
export function contextualBuiltinCall(671
state: CheckerState,672
moduleIndex: int,673
source: Bytes,674
expression: SyntaxNode,675
callee: SyntaxNode,676
calleeType: TypeValue,677
) -> Option<TypeValue> {678
if !unshadowedIdentifier(state, moduleIndex, callee) {679
return None680
}681
let name = text(source, callee)682
if name != "Ok" && name != "Err" {683
return None684
}685
let expected = match currentExpectedExpression(state, expression) {686
case Some(found) => found687
case None => return None688
}689
if expected.kind != "constructor" ||690
expected.name != "Result" ||691
expected.arguments.length != 2 || hasHole(expected) {692
return None693
}694
let parameter = if name == "Ok" {695
expected.arguments.get(0)696
} else {697
expected.arguments.get(1)698
}699
match parameter {700
case Some(found) => Some(functionType([found], [], expected))701
case None => Some(calleeType)702
}703
}705
export function contextualBuiltinArgumentType(706
state: CheckerState,707
moduleIndex: int,708
source: Bytes,709
callee: SyntaxNode,710
argumentIndex: int,711
actuals: Array<TypeValue>,712
fallback: Option<TypeValue>,713
) -> Option<TypeValue> {714
if !unshadowedIdentifier(state, moduleIndex, callee) {715
return fallback716
}717
let name = text(source, callee)718
if argumentIndex == 1 && (name == "map" || name == "filter") {719
match actuals.get(0) {720
case Some(collection) => {721
let input = iterableElement(collection)722
return Some(functionType(723
[input],724
[],725
if name == "filter" {726
primitive("bool")727
} else {728
rigid("U", "builtin::map")729
},730
))731
}732
case None => ()733
}734
}735
if argumentIndex == 2 && name == "reduce" {736
let input = match actuals.get(0) {737
case Some(collection) => iterableElement(collection)738
case None => emptyType("unknown")739
}740
let output = match actuals.get(1) {741
case Some(initial) => widen(initial)742
case None => emptyType("unknown")743
}744
return Some(functionType(745
[output, input],746
[],747
output,748
))749
}750
fallback751
}753
export function contextualStandardWrapperCall(754
state: CheckerState,755
moduleIndex: int,756
expression: SyntaxNode,757
callee: SyntaxNode,758
calleeType: TypeValue,759
actuals: Array<TypeValue>,760
) -> Option<TypeValue> {761
if callee.kind != "expression/member" &&762
callee.kind != "expression/optional-access" {763
return None764
}765
if calleeType.kind != "unknown" {766
return None767
}768
match moduleAt(state, moduleIndex) {769
case Some(module) => if !module.identity.startsWith("std.") {770
return None771
}772
case None => return None773
}774
let expected = match currentExpectedExpression(state, expression) {775
case Some(found) => found776
case None => return None777
}778
let mut parameters: Array<TypeValue> = []779
for actual in actuals {780
parameters.push(widen(actual))781
}782
match moduleAt(state, moduleIndex) {783
case Some(module) => {784
let field = match child(callee, "field") {785
case Some(found) => text(module.source, found)786
case None => ""787
}788
if module.identity == "std.decimal" &&789
((field == "round" && parameters.length == 1) ||790
(field == "div" && parameters.length == 2)) {791
parameters.push(emptyType("rounding-mode"))792
}793
}794
case None => ()795
}796
Some(functionType(parameters, [], expected))797
}799
export function generatedLispexIntrinsic(800
state: CheckerState,801
moduleIndex: int,802
name: string,803
) -> bool {804
match moduleAt(state, moduleIndex) {805
case Some(module) => if !module.generatedStd {806
false807
} else if module.identity == "std.lispex.rules" {808
name == "__lispexRule"809
} else if module.identity == "std.lispex" {810
name == "__lispexValueFromCanonical" ||811
name == "__lispexCanonicalBytes" ||812
name == "__lispexDefaultLimits" ||813
name == "__lispexInspectRule" ||814
name == "__lispexEvaluate" ||815
name == "__lispexEvaluateWithEvidence" ||816
name == "__lispexConsumerArtifactFromBytes" ||817
name == "__lispexConsumerArtifactBytes" ||818
name == "__lispexPortableCoreBytes" ||819
name == "__lispexInspectConsumerArtifact" ||820
name == "__lispexVerifyConsumerArtifact" ||821
name == "__lispexFreshReplay"822
} else {823
false824
}825
case None => false826
}827
}829
export function contextualGeneratedLispexCall(830
state: CheckerState,831
moduleIndex: int,832
source: Bytes,833
expression: SyntaxNode,834
callee: SyntaxNode,835
actuals: Array<TypeValue>,836
) -> Option<TypeValue> {837
if callee.kind != "expression/identifier" ||838
!generatedLispexIntrinsic(state, moduleIndex, text(source, callee)) {839
return None840
}841
let expected = match currentExpectedExpression(state, expression) {842
case Some(found) => found843
case None => return None844
}845
let mut parameters: Array<TypeValue> = []846
for actual in actuals {847
parameters.push(widen(actual))848
}849
Some(functionType(parameters, [], expected))850
}852
function builtinCallIdentity(853
service: CheckerService,854
state: CheckerState,855
moduleIndex: int,856
source: Bytes,857
callee: SyntaxNode,858
) -> string {859
if callee.kind == "expression/identifier" {860
let name = text(source, callee)861
if generatedLispexIntrinsic(state, moduleIndex, name) {862
return "builtin::{name}"863
}864
if unshadowedIdentifier(state, moduleIndex, callee) {865
match builtinFunctionType(name) {866
case Some(_) => return "builtin::{name}"867
case None => ()868
}869
}870
}871
if callee.kind == "expression/member" {872
match child(callee, "object") {873
case Some(object) => {874
match userProtocolObject(state, moduleIndex, source, object) {875
case Some(definition) => return "builtin::{definition.name}"876
case None => ()877
}878
if builtinNamespaceObject(879
service,880
state,881
moduleIndex,882
source,883
object,884
) {885
let name = text(source, object)886
// JSON static operations have no resolved callable declaration.887
// Their member plan and checked types carry the invocation.888
if name == "JSON" { return "" }889
return "builtin::{name}"890
}891
}892
case None => ()893
}894
}895
""896
}898
function previewSourceCallArgument(899
source: Bytes,900
argument: SyntaxNode,901
sourceIndex: int,902
) -> PreviewCallArgument {903
let value = argumentValue(argument)904
PreviewCallArgument {905
bindingKind: if argument.kind == "call-argument/named" {906
"named"907
} else if argument.kind == "call-argument/spread" {908
"spread"909
} else {910
"positional"911
},912
bindingName: if argument.kind == "call-argument/named" {913
match child(argument, "name") {914
case Some(found) => text(source, found)915
case None => ""916
}917
} else {918
""919
},920
sourceIndex: Some(sourceIndex),921
lo: value.lo,922
hi: value.hi,923
}924
}926
function projectedCallIdentity(927
service: CheckerService,928
state: CheckerState,929
moduleIndex: int,930
source: Bytes,931
callee: SyntaxNode,932
reference: Option<PreviewResolvedReference>,933
) -> string {934
match reference {935
case Some(found) => if found.targetModule == "std.lispex.rules" &&936
found.targetName != "" {937
return "topaz.lispex-rule-handle/v1:{found.targetName}"938
}939
case None => ()940
}941
let identity = match reference {942
case Some(found) => referenceTargetIdentity(state, found)943
case None => ""944
}945
if identity != "" {946
return identity947
}948
builtinCallIdentity(service, state, moduleIndex, source, callee)949
}951
record CallFactContext {952
state: CheckerState,953
moduleIndex: int,954
source: Bytes,955
node: SyntaxNode,956
callee: SyntaxNode,957
calleeType: TypeValue,958
receiverType: TypeValue,959
resultType: TypeValue,960
}962
record CallArgumentObservation {963
arguments: Array<PreviewCallArgument>,964
evaluations: Array<PreviewCallEvaluation>,965
}967
record DirectCallProjection {968
field: string,969
calleeType: TypeValue,970
identity: string,971
observation: CallArgumentObservation,972
}974
function callField(source: Bytes, callee: SyntaxNode) -> string {975
match child(callee, "field") {976
case Some(found) => text(source, found)977
case None => ""978
}979
}981
function projectedDirectCalleeType(982
context: CallFactContext,983
reference: Option<PreviewResolvedReference>,984
) -> TypeValue {985
if context.calleeType.kind != "unknown" {986
return context.calleeType987
}988
match reference {989
case Some(foundReference) => match resolvedTypeFromFact(990
context.state,991
foundReference,992
) {993
case Some(found) => if found.kind == "unknown" {994
context.calleeType995
} else {996
found997
}998
case None => context.calleeType999
}1000
case None => context.calleeTypeSHA256 cebd4f807d6c51527ebbae9614e739c91ad04244549383da01f15b75ec51f21b