slow.monster

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, keeping
3// the mutually recursive expression service out of both modules.
5import types {
6 PreviewCallArgument,
7 PreviewCallEvaluation,
8 PreviewResolvedReference,
9 PreviewTypedCall,
10 SyntaxNode,
12import checker_types { TypeValue }
13import unicode { didYouMean }
14import 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,
29import checker.type_algebra {
30 assignable,
31 flattenType,
32 flattenedHasHole,
33 hasHole,
34 typeText,
35 widen,
37import 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,
54import checker.protocols_methods {
55 receiverMethodTargetIdentity,
56 referenceTargetType,
58import checker.members_capabilities {
59 builtinNamespaceFamily,
60 iterableElement,
63export 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) => found
67 case None => argument
68 }
69 } else {
70 argument
71 }
74// A declaration signature belongs to the callable itself. References used
75// while evaluating a receiver or producing a callable are different calls.
76export 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 None
93function 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 => None
105 }
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 => None
112 }
113 }
114 None
117function 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 }
143function 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 }
154export 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 true
159 }
160 }
161 return false
162 }
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")
179function 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 }
190function 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 }
205export 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 _ => None
311 }
314function builtinFunctionType(name: string) -> Option<TypeValue> {
315 match builtinFunction(name) {
316 case Some(found) => Some(found.typeValue)
317 case None => None
318 }
321export 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 => None
331 }
334export function isBuiltinNamespace(name: string) -> bool {
335 builtinNamespaceFamily(name) != ""
338function 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 ]
357export 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)
385export 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.scopeOrdinal
392 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.parentOrdinal
419 case None => -1
420 }
421 }
422 for candidate in unboundFunctionSuggestionNames() {
423 if !seen.contains(candidate) {
424 candidates.push(candidate)
425 }
426 }
427 didYouMean(reference.name, candidates)
430export 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(_) => true
440 case None => false
441 } {
442 continue
443 }
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 ()
464export function currentFunctionReturn(state: CheckerState) -> Option<TypeValue> {
465 if state.expectedReturns.length == 0 {
466 return None
467 }
468 state.expectedReturns.get(state.expectedReturns.length - 1)
471export 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.hi
474 case None => false
475 }
478export function currentExpectedExpression(
479 state: CheckerState,
480 expression: SyntaxNode,
481) -> Option<TypeValue> {
482 if state.expectedExpressions.length == 0 {
483 return None
484 }
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 None
491 }
492 case None => None
493 }
496export 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.expectedExpressions
506 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 inferred
521function appendReturnType(state: CheckerState, value: TypeValue) -> () {
522 if state.returnJoins.length == 0 {
523 return ()
524 }
525 let index = state.returnJoins.length - 1
526 match state.returnJoins.get(index) {
527 case Some(found) => {
528 let mut values = found
529 values.push(value)
530 let mut joins = state.returnJoins
531 joins[index] = values
532 }
533 case None => ()
534 }
535 ()
538export 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 ()
594export 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 empty
606 }
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 _ => empty
613 }
614 }
615 if callee.kind != "expression/member" {
616 return empty
617 }
618 let object = match child(callee, "object") {
619 case Some(found) => found
620 case None => return empty
621 }
622 let field = match child(callee, "field") {
623 case Some(found) => text(source, found)
624 case None => return empty
625 }
626 if !builtinNamespaceObject(service, state, moduleIndex, source, object) {
627 if receiverType.kind != "constructor" {
628 return empty
629 }
630 return match receiverType.name {
631 case "Option" => match field {
632 case "okOr" | "okOrElse" | "map" | "flatMap" => ["U"]
633 case _ => empty
634 }
635 case "Result" => match field {
636 case "map" | "flatMap" => ["U"]
637 case _ => empty
638 }
639 case "Array" => match field {
640 case "map" | "reduce" => ["U"]
641 case "sortedBy" | "sortBy" => ["K"]
642 case _ => empty
643 }
644 case "Map" => if field == "mapValues" { ["W"] } else { empty }
645 case _ => empty
646 }
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 empty
654 }
655 case "JSON" => if field == "parseAs" || field == "decode" ||
656 field == "stringify" {
657 ["T"]
658 } else {
659 empty
660 }
661 case "Test" => match field {
662 case "assertEq" | "assertNe" | "assertSome" | "assertNone" => ["T"]
663 case "assertOk" | "assertErr" => ["T", "E"]
664 case _ => empty
665 }
666 case _ => empty
667 }
670export 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 None
680 }
681 let name = text(source, callee)
682 if name != "Ok" && name != "Err" {
683 return None
684 }
685 let expected = match currentExpectedExpression(state, expression) {
686 case Some(found) => found
687 case None => return None
688 }
689 if expected.kind != "constructor" ||
690 expected.name != "Result" ||
691 expected.arguments.length != 2 || hasHole(expected) {
692 return None
693 }
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 }
705export 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 fallback
716 }
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 fallback
753export 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 None
764 }
765 if calleeType.kind != "unknown" {
766 return None
767 }
768 match moduleAt(state, moduleIndex) {
769 case Some(module) => if !module.identity.startsWith("std.") {
770 return None
771 }
772 case None => return None
773 }
774 let expected = match currentExpectedExpression(state, expression) {
775 case Some(found) => found
776 case None => return None
777 }
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))
799export 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 false
807 } 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 false
824 }
825 case None => false
826 }
829export 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 None
840 }
841 let expected = match currentExpectedExpression(state, expression) {
842 case Some(found) => found
843 case None => return None
844 }
845 let mut parameters: Array<TypeValue> = []
846 for actual in actuals {
847 parameters.push(widen(actual))
848 }
849 Some(functionType(parameters, [], expected))
852function 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 ""
898function 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 }
926function 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 identity
947 }
948 builtinCallIdentity(service, state, moduleIndex, source, callee)
951record 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,
962record CallArgumentObservation {
963 arguments: Array<PreviewCallArgument>,
964 evaluations: Array<PreviewCallEvaluation>,
967record DirectCallProjection {
968 field: string,
969 calleeType: TypeValue,
970 identity: string,
971 observation: CallArgumentObservation,
974function callField(source: Bytes, callee: SyntaxNode) -> string {
975 match child(callee, "field") {
976 case Some(found) => text(source, found)
977 case None => ""
978 }
981function projectedDirectCalleeType(
982 context: CallFactContext,
983 reference: Option<PreviewResolvedReference>,
984) -> TypeValue {
985 if context.calleeType.kind != "unknown" {
986 return context.calleeType
987 }
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.calleeType
995 } else {
996 found
997 }
998 case None => context.calleeType
999 }
1000 case None => context.calleeType

SHA256 cebd4f807d6c51527ebbae9614e739c91ad04244549383da01f15b75ec51f21b