@suss/adapter-python 0.21.1 → 0.22.1
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/README.md +71 -0
- package/dist/index.cjs +2183 -689
- package/dist/index.d.cts +148 -3
- package/dist/index.d.ts +148 -3
- package/dist/index.js +1831 -309
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -765,19 +765,399 @@ import {
|
|
|
765
765
|
mapReading,
|
|
766
766
|
unreadableReading,
|
|
767
767
|
valueToReadFurtherFrom,
|
|
768
|
-
walkDescendants,
|
|
768
|
+
walkDescendants as walkDescendants2,
|
|
769
769
|
writtenReading
|
|
770
770
|
} from "@suss/extractor";
|
|
771
771
|
|
|
772
|
+
// src/envReads.ts
|
|
773
|
+
import { runtimeConfigBinding } from "@suss/behavioral-ir";
|
|
774
|
+
import { SKIP_CHILDREN, walkDescendants } from "@suss/extractor";
|
|
775
|
+
var PYTHON_ENV_RECOGNITION = "python-env";
|
|
776
|
+
var DEFERRED_BODY_TYPES = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
|
|
777
|
+
function envReadEffects(root, module) {
|
|
778
|
+
const effects = [];
|
|
779
|
+
const startScope = module.scopeFor.get(root.id) ?? module.moduleScope;
|
|
780
|
+
walkDescendants(root, startScope, {
|
|
781
|
+
at: (node, scope) => {
|
|
782
|
+
const read = envReadAt(node, scope);
|
|
783
|
+
if (read !== null) {
|
|
784
|
+
effects.push(configReadEffect(read));
|
|
785
|
+
}
|
|
786
|
+
},
|
|
787
|
+
into: (node, scope) => {
|
|
788
|
+
if (DEFERRED_BODY_TYPES.has(node.type)) {
|
|
789
|
+
return SKIP_CHILDREN;
|
|
790
|
+
}
|
|
791
|
+
return module.scopeFor.get(node.id) ?? scope;
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
return effects;
|
|
795
|
+
}
|
|
796
|
+
function configReadEffect(read) {
|
|
797
|
+
return {
|
|
798
|
+
type: "interaction",
|
|
799
|
+
binding: runtimeConfigBinding({
|
|
800
|
+
recognition: PYTHON_ENV_RECOGNITION,
|
|
801
|
+
deploymentTarget: "lambda",
|
|
802
|
+
instanceName: "<unknown>"
|
|
803
|
+
}),
|
|
804
|
+
callee: `os.environ["${read.name}"]`,
|
|
805
|
+
interaction: {
|
|
806
|
+
class: "config-read",
|
|
807
|
+
name: read.name,
|
|
808
|
+
defaulted: read.defaulted
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
function envReadAt(node, scope) {
|
|
813
|
+
if (node.type === "subscript") {
|
|
814
|
+
return subscriptRead(node, scope);
|
|
815
|
+
}
|
|
816
|
+
if (node.type === "call") {
|
|
817
|
+
return callRead(node, scope);
|
|
818
|
+
}
|
|
819
|
+
return null;
|
|
820
|
+
}
|
|
821
|
+
function subscriptRead(node, scope) {
|
|
822
|
+
const value = field(node, "value");
|
|
823
|
+
const index = field(node, "subscript");
|
|
824
|
+
if (value === null || index === null || !isEnviron(value, scope)) {
|
|
825
|
+
return null;
|
|
826
|
+
}
|
|
827
|
+
const name = stringLiteralValue(index);
|
|
828
|
+
if (name === null || isAssignedTo(node)) {
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
return { name, defaulted: isDefaultedAt(node) };
|
|
832
|
+
}
|
|
833
|
+
function isAssignedTo(node) {
|
|
834
|
+
const parent = node.parent;
|
|
835
|
+
if (parent === null) {
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
if (parent.type === "delete_statement") {
|
|
839
|
+
return true;
|
|
840
|
+
}
|
|
841
|
+
return (parent.type === "assignment" || parent.type === "augmented_assignment") && field(parent, "left")?.id === node.id;
|
|
842
|
+
}
|
|
843
|
+
function callRead(node, scope) {
|
|
844
|
+
const callee = field(node, "function");
|
|
845
|
+
const args = field(node, "arguments");
|
|
846
|
+
if (callee === null || args === null || !isEnvGetter(callee, scope)) {
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
const positional = args.namedChildren.filter(
|
|
850
|
+
(arg) => arg !== null && arg.type !== "keyword_argument"
|
|
851
|
+
);
|
|
852
|
+
const name = positional[0] === void 0 ? null : stringLiteralValue(positional[0]);
|
|
853
|
+
if (name === null) {
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
const keywords = args.namedChildren.filter(
|
|
857
|
+
(arg) => arg !== null && arg.type === "keyword_argument"
|
|
858
|
+
);
|
|
859
|
+
const hasDefault = positional.length > 1 || keywords.some((arg) => field(arg, "name")?.text === "default");
|
|
860
|
+
return { name, defaulted: hasDefault || isDefaultedAt(node) };
|
|
861
|
+
}
|
|
862
|
+
function isEnvGetter(callee, scope) {
|
|
863
|
+
if (callee.type === "attribute") {
|
|
864
|
+
const object2 = field(callee, "object");
|
|
865
|
+
const attribute = field(callee, "attribute")?.text;
|
|
866
|
+
if (object2 === null) {
|
|
867
|
+
return false;
|
|
868
|
+
}
|
|
869
|
+
if (attribute === "get") {
|
|
870
|
+
return isEnviron(object2, scope);
|
|
871
|
+
}
|
|
872
|
+
return attribute === "getenv" && isOsModule(object2, scope);
|
|
873
|
+
}
|
|
874
|
+
return isImportedFromOs(callee, scope, "getenv");
|
|
875
|
+
}
|
|
876
|
+
function isEnviron(node, scope) {
|
|
877
|
+
if (node.type === "attribute") {
|
|
878
|
+
const object2 = field(node, "object");
|
|
879
|
+
return object2 !== null && field(node, "attribute")?.text === "environ" && isOsModule(object2, scope);
|
|
880
|
+
}
|
|
881
|
+
return isImportedFromOs(node, scope, "environ");
|
|
882
|
+
}
|
|
883
|
+
function isOsModule(node, scope) {
|
|
884
|
+
if (node.type !== "identifier") {
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
const binding = resolveName(scope, node.text);
|
|
888
|
+
return binding?.kind === "import" && binding.module === "os" && binding.relativeLevel === 0;
|
|
889
|
+
}
|
|
890
|
+
function isImportedFromOs(node, scope, name) {
|
|
891
|
+
if (node.type !== "identifier") {
|
|
892
|
+
return false;
|
|
893
|
+
}
|
|
894
|
+
const binding = resolveName(scope, node.text);
|
|
895
|
+
return binding?.kind === "importFrom" && binding.module === "os" && binding.relativeLevel === 0 && binding.importedName === name;
|
|
896
|
+
}
|
|
897
|
+
function isDefaultedAt(node) {
|
|
898
|
+
let child = node;
|
|
899
|
+
let parent = node.parent;
|
|
900
|
+
while (parent !== null) {
|
|
901
|
+
if (parent.type === "parenthesized_expression") {
|
|
902
|
+
child = parent;
|
|
903
|
+
parent = parent.parent;
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
if (parent.type !== "boolean_operator" || field(parent, "operator")?.text !== "or") {
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
if (field(parent, "left")?.id === child.id) {
|
|
910
|
+
return true;
|
|
911
|
+
}
|
|
912
|
+
child = parent;
|
|
913
|
+
parent = parent.parent;
|
|
914
|
+
}
|
|
915
|
+
return false;
|
|
916
|
+
}
|
|
917
|
+
|
|
772
918
|
// src/facts/resolve.ts
|
|
773
|
-
import { deriveOnDemand, evaluate } from "@suss/datalog";
|
|
919
|
+
import { deriveOnDemand, evaluate as evaluate2 } from "@suss/datalog";
|
|
774
920
|
|
|
775
921
|
// ../../resolution/dist/index.js
|
|
776
922
|
import { ruleLabel } from "@suss/datalog";
|
|
923
|
+
import { Database, evaluate, proofOf, witnesses } from "@suss/datalog";
|
|
777
924
|
import { constant, lit, rule, variable as v } from "@suss/datalog";
|
|
778
|
-
function
|
|
925
|
+
function exportTrail(proof, describe3) {
|
|
926
|
+
if (proof.kind !== "derived") {
|
|
927
|
+
return [];
|
|
928
|
+
}
|
|
929
|
+
const name = ruleLabel(proof.rule);
|
|
930
|
+
if (name === "re-export") {
|
|
931
|
+
const [m, n, m2, n2] = proof.premises[0].tuple;
|
|
932
|
+
return [
|
|
933
|
+
`${describe3(m)} takes ${String(n)} from ${describe3(m2)}, where it is called ${String(n2)}`,
|
|
934
|
+
...exportTrail(proof.premises[1], describe3)
|
|
935
|
+
];
|
|
936
|
+
}
|
|
937
|
+
if (name === "re-export all") {
|
|
938
|
+
const [m, m2] = proof.premises[0].tuple;
|
|
939
|
+
return [
|
|
940
|
+
`${describe3(m)} forwards everything ${describe3(m2)} exports`,
|
|
941
|
+
...exportTrail(proof.premises[1], describe3)
|
|
942
|
+
];
|
|
943
|
+
}
|
|
944
|
+
return [];
|
|
945
|
+
}
|
|
946
|
+
function argumentReason(proof, describe3) {
|
|
947
|
+
const [, p, a] = proof.tuple;
|
|
948
|
+
if (proof.kind !== "derived") {
|
|
949
|
+
return `${describe3(p)} is a parameter, and a call passes it ${describe3(a)}`;
|
|
950
|
+
}
|
|
951
|
+
const f = proof.premises[0].tuple[0];
|
|
952
|
+
return `${describe3(p)} is a parameter of ${describe3(f)}, and a call passes it ${describe3(a)}`;
|
|
953
|
+
}
|
|
954
|
+
var DEFAULT_PHRASES = {
|
|
955
|
+
alias: ({ tuple: tuple2, describe: describe3 }) => ({
|
|
956
|
+
reason: `${describe3(tuple2[0])} is declared as ${describe3(tuple2[1])}`
|
|
957
|
+
}),
|
|
958
|
+
"last write": ({ tuple: tuple2, describe: describe3 }) => ({
|
|
959
|
+
reason: `${describe3(tuple2[0])} is written more than once, and the last write leaves it as ${describe3(tuple2[1])}`
|
|
960
|
+
}),
|
|
961
|
+
fallback: ({ tuple: tuple2, describe: describe3 }) => ({
|
|
962
|
+
reason: `${describe3(tuple2[0])} is a fallback expression, and ${describe3(tuple2[1])} is the branch that resolves`
|
|
963
|
+
}),
|
|
964
|
+
import: ({ tuple: tuple2, premises, describe: describe3 }) => {
|
|
965
|
+
const [, module, name] = premises[0].tuple;
|
|
966
|
+
return {
|
|
967
|
+
reason: `${describe3(tuple2[0])} is imported from ${describe3(module)} under the name ${String(name)}`,
|
|
968
|
+
notes: exportTrail(premises[1], describe3)
|
|
969
|
+
};
|
|
970
|
+
},
|
|
971
|
+
argument: ({ premises, describe: describe3 }) => ({
|
|
972
|
+
reason: argumentReason(premises[0], describe3)
|
|
973
|
+
}),
|
|
974
|
+
"property read": ({ tuple: tuple2, premises, describe: describe3 }) => {
|
|
975
|
+
const [, object2, name] = premises[0].tuple;
|
|
976
|
+
const contains = premises[2];
|
|
977
|
+
const inherited = contains.kind === "derived" && contains.premises.length > 1;
|
|
978
|
+
return {
|
|
979
|
+
reason: `${describe3(tuple2[0])} reads ${String(name)} off ${describe3(object2)}, which contains ${describe3(tuple2[1])}`,
|
|
980
|
+
notes: inherited ? [`${String(name)} comes from a base class`] : []
|
|
981
|
+
};
|
|
982
|
+
},
|
|
983
|
+
"class instance": ({ tuple: tuple2, premises, describe: describe3, inline }) => ({
|
|
984
|
+
reason: `${describe3(tuple2[0])} makes an instance of ${describe3(tuple2[1])}`,
|
|
985
|
+
notes: inline(premises[1])
|
|
986
|
+
}),
|
|
987
|
+
"factory unwrap": ({ tuple: tuple2, premises, describe: describe3, inline }) => {
|
|
988
|
+
const factory = premises[1].tuple[1];
|
|
989
|
+
return {
|
|
990
|
+
reason: `${describe3(tuple2[0])} calls ${describe3(factory)}, a factory that passes its argument through, so it comes down to ${describe3(tuple2[1])}`,
|
|
991
|
+
notes: inline(premises[1])
|
|
992
|
+
};
|
|
993
|
+
},
|
|
994
|
+
"declared wrapper": ({ tuple: tuple2, premises, describe: describe3 }) => {
|
|
995
|
+
const callee = String(premises[0].tuple[1]);
|
|
996
|
+
const module = String(premises[2].tuple[1]);
|
|
997
|
+
const argument = String(premises[1].tuple[1]);
|
|
998
|
+
return {
|
|
999
|
+
reason: `${describe3(tuple2[0])} calls ${callee} from ${module}, which passes argument ${argument} through, so it comes down to ${describe3(tuple2[1])}`,
|
|
1000
|
+
assumptions: [
|
|
1001
|
+
`a pack declares that ${callee} from ${module} passes argument ${argument} through to its result`
|
|
1002
|
+
]
|
|
1003
|
+
};
|
|
1004
|
+
},
|
|
1005
|
+
"call result": ({ tuple: tuple2, premises, describe: describe3, inline }) => {
|
|
1006
|
+
const invokes = premises[0];
|
|
1007
|
+
const invoked = invokes.tuple[1];
|
|
1008
|
+
const calleeWalks = invokes.kind === "derived" ? invokes.premises.filter(
|
|
1009
|
+
(premise) => premise.kind === "derived" && (premise.relation === "comesTo" || premise.relation === "givesBack")
|
|
1010
|
+
) : [];
|
|
1011
|
+
return {
|
|
1012
|
+
reason: `${describe3(tuple2[0])} runs ${describe3(invoked)}, which returns ${describe3(tuple2[1])}`,
|
|
1013
|
+
notes: calleeWalks.flatMap((premise) => inline(premise))
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
var CHAIN_RELATIONS = /* @__PURE__ */ new Set([
|
|
1018
|
+
"resolves",
|
|
1019
|
+
"comesTo",
|
|
1020
|
+
"givesBack",
|
|
1021
|
+
"isWrittenAs",
|
|
1022
|
+
"comesFrom",
|
|
1023
|
+
"reaches",
|
|
1024
|
+
"stepsTo"
|
|
1025
|
+
]);
|
|
1026
|
+
function inlineChain(proof, state) {
|
|
1027
|
+
const explained = explainResolutionProof(proof, {
|
|
1028
|
+
describe: state.describe,
|
|
1029
|
+
phrases: state.phrases
|
|
1030
|
+
});
|
|
1031
|
+
if (explained === null || explained.steps.length === 0) {
|
|
1032
|
+
return [];
|
|
1033
|
+
}
|
|
1034
|
+
if (explained.truncated) {
|
|
1035
|
+
state.truncated = true;
|
|
1036
|
+
}
|
|
1037
|
+
return explained.steps.map((step) => step.reason);
|
|
1038
|
+
}
|
|
1039
|
+
function stepFrom(proof, state) {
|
|
1040
|
+
const name = ruleLabel(proof.rule);
|
|
1041
|
+
const phrase = state.phrases[name];
|
|
1042
|
+
const from = proof.tuple[0];
|
|
1043
|
+
const to = proof.tuple[1];
|
|
1044
|
+
if (phrase === void 0) {
|
|
1045
|
+
return {
|
|
1046
|
+
from,
|
|
1047
|
+
to,
|
|
1048
|
+
rule: name,
|
|
1049
|
+
reason: `${state.describe(from)} steps to ${state.describe(to)} (${name})`,
|
|
1050
|
+
notes: [],
|
|
1051
|
+
assumptions: []
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
const said = phrase({
|
|
1055
|
+
tuple: proof.tuple,
|
|
1056
|
+
premises: proof.premises,
|
|
1057
|
+
describe: state.describe,
|
|
1058
|
+
inline: (nested) => inlineChain(nested, state)
|
|
1059
|
+
});
|
|
1060
|
+
return {
|
|
1061
|
+
from,
|
|
1062
|
+
to,
|
|
1063
|
+
rule: name,
|
|
1064
|
+
reason: said.reason,
|
|
1065
|
+
notes: said.notes ?? [],
|
|
1066
|
+
assumptions: said.assumptions ?? []
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
function importStep(proof, state) {
|
|
1070
|
+
const [x, module, name] = proof.tuple;
|
|
1071
|
+
return {
|
|
1072
|
+
from: x,
|
|
1073
|
+
to: module,
|
|
1074
|
+
rule: "import",
|
|
1075
|
+
reason: `${state.describe(x)} is imported from ${state.describe(module)} under the name ${String(name)}`,
|
|
1076
|
+
notes: [],
|
|
1077
|
+
assumptions: []
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
var flattenInto = (proof, state) => {
|
|
1081
|
+
if (proof.kind === "truncated") {
|
|
1082
|
+
state.truncated = true;
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
if (proof.kind !== "derived") {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
const relation = proof.relation;
|
|
1089
|
+
if (relation === "stepsTo") {
|
|
1090
|
+
state.steps.push(stepFrom(proof, state));
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
if (relation === "reaches") {
|
|
1094
|
+
for (const p of proof.premises) {
|
|
1095
|
+
flattenInto(p, state);
|
|
1096
|
+
}
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
if (relation === "comesFrom") {
|
|
1100
|
+
if (proof.premises.length === 2) {
|
|
1101
|
+
flattenInto(proof.premises[0], state);
|
|
1102
|
+
state.steps.push(importStep(proof.premises[1], state));
|
|
1103
|
+
} else {
|
|
1104
|
+
state.steps.push(importStep(proof.premises[0], state));
|
|
1105
|
+
}
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
for (const p of proof.premises) {
|
|
1109
|
+
if (p.kind === "derived" && (p.relation === "reaches" || p.relation === "comesTo")) {
|
|
1110
|
+
flattenInto(p, state);
|
|
1111
|
+
}
|
|
1112
|
+
if (p.kind === "truncated") {
|
|
1113
|
+
state.truncated = true;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
function explainResolutionProof(proof, options) {
|
|
1118
|
+
if (proof.kind === "absence") {
|
|
1119
|
+
return null;
|
|
1120
|
+
}
|
|
1121
|
+
if (proof.kind === "derived" && !CHAIN_RELATIONS.has(proof.relation)) {
|
|
1122
|
+
return null;
|
|
1123
|
+
}
|
|
1124
|
+
const state = {
|
|
1125
|
+
steps: [],
|
|
1126
|
+
truncated: proof.kind === "truncated",
|
|
1127
|
+
describe: options.describe,
|
|
1128
|
+
phrases: { ...DEFAULT_PHRASES, ...options.phrases }
|
|
1129
|
+
};
|
|
1130
|
+
flattenInto(proof, state);
|
|
1131
|
+
const atoms = proof.kind === "fact" || proof.kind === "truncated" ? [proof.tuple[0]] : [proof.tuple[0], ...state.steps.map((step) => step.to)];
|
|
1132
|
+
return {
|
|
1133
|
+
atoms,
|
|
1134
|
+
steps: state.steps,
|
|
1135
|
+
assumptions: state.steps.flatMap((step) => step.assumptions),
|
|
1136
|
+
truncated: state.truncated
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
function renderExplanation(explanation, describe3) {
|
|
1140
|
+
const lines = [];
|
|
1141
|
+
if (explanation.steps.length > 0) {
|
|
1142
|
+
lines.push(explanation.atoms.map((atom) => describe3(atom)).join(" -> "));
|
|
1143
|
+
}
|
|
1144
|
+
for (const step of explanation.steps) {
|
|
1145
|
+
lines.push(` ${step.reason}`);
|
|
1146
|
+
for (const note of step.notes) {
|
|
1147
|
+
lines.push(` ${note}`);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
for (const assumption of explanation.assumptions) {
|
|
1151
|
+
lines.push(` assuming ${assumption}`);
|
|
1152
|
+
}
|
|
1153
|
+
if (explanation.truncated) {
|
|
1154
|
+
lines.push(" the chain goes on past the proof depth cap");
|
|
1155
|
+
}
|
|
1156
|
+
return lines;
|
|
1157
|
+
}
|
|
1158
|
+
function joinMountedPath(prefix, path6) {
|
|
779
1159
|
const trimmed = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
780
|
-
return trimmed === "" ?
|
|
1160
|
+
return trimmed === "" ? path6 : trimmed + path6;
|
|
781
1161
|
}
|
|
782
1162
|
function mountPathsOf(edges, childId) {
|
|
783
1163
|
return pathsOf(edges, /* @__PURE__ */ new Map(), childId, /* @__PURE__ */ new Set());
|
|
@@ -814,6 +1194,88 @@ function pathsOf(edges, memo, childId, visiting) {
|
|
|
814
1194
|
memo.set(childId, result);
|
|
815
1195
|
return result;
|
|
816
1196
|
}
|
|
1197
|
+
function explainResolvedKey(options) {
|
|
1198
|
+
const { db, rules, key, locate, displayPath } = options;
|
|
1199
|
+
const proofDb = new Database();
|
|
1200
|
+
let baseFacts = 0;
|
|
1201
|
+
for (const relation of db.relationNames()) {
|
|
1202
|
+
for (const tuple2 of db.facts(relation)) {
|
|
1203
|
+
proofDb.add(relation, tuple2);
|
|
1204
|
+
baseFacts++;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
const started = performance.now();
|
|
1208
|
+
evaluate(proofDb, rules, witnesses);
|
|
1209
|
+
const evaluateMs = performance.now() - started;
|
|
1210
|
+
const derivedFacts = proofDb.relationNames().reduce((count, relation) => count + proofDb.size(relation), 0) - baseFacts;
|
|
1211
|
+
const targets = new Set(
|
|
1212
|
+
proofDb.lookup("resolves", 0, key).map((tuple2) => String(tuple2[1]))
|
|
1213
|
+
);
|
|
1214
|
+
if (targets.size !== 1) {
|
|
1215
|
+
return null;
|
|
1216
|
+
}
|
|
1217
|
+
const target = [...targets][0];
|
|
1218
|
+
const proof = proofOf(
|
|
1219
|
+
proofDb,
|
|
1220
|
+
"resolves",
|
|
1221
|
+
[key, target],
|
|
1222
|
+
options.maxDepth === void 0 ? {} : { maxDepth: options.maxDepth }
|
|
1223
|
+
);
|
|
1224
|
+
const describe3 = (atom) => {
|
|
1225
|
+
const location = locate(String(atom));
|
|
1226
|
+
return location === null ? displayPath(String(atom)) : `${location.name} (${location.file}:${location.line})`;
|
|
1227
|
+
};
|
|
1228
|
+
const explanation = explainResolutionProof(proof, {
|
|
1229
|
+
describe: describe3,
|
|
1230
|
+
...options.phrases === void 0 ? {} : { phrases: options.phrases }
|
|
1231
|
+
});
|
|
1232
|
+
if (explanation === null) {
|
|
1233
|
+
return null;
|
|
1234
|
+
}
|
|
1235
|
+
const targetLocation = locate(target);
|
|
1236
|
+
if (targetLocation === null) {
|
|
1237
|
+
return null;
|
|
1238
|
+
}
|
|
1239
|
+
return {
|
|
1240
|
+
explanation,
|
|
1241
|
+
chain: explanation.atoms.map(describe3),
|
|
1242
|
+
lines: renderExplanation(explanation, describe3),
|
|
1243
|
+
target: targetLocation,
|
|
1244
|
+
stats: { baseFacts, derivedFacts, evaluateMs }
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
var NO_PLACEHOLDERS = /* @__PURE__ */ new Set();
|
|
1248
|
+
function singleAnswers(rows, placeholders = NO_PLACEHOLDERS) {
|
|
1249
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
1250
|
+
for (const row of rows) {
|
|
1251
|
+
const key = String(row[0]);
|
|
1252
|
+
const answer = String(row[1]);
|
|
1253
|
+
if (answer === key) {
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
const set2 = candidates.get(key) ?? /* @__PURE__ */ new Set();
|
|
1257
|
+
set2.add(answer);
|
|
1258
|
+
candidates.set(key, set2);
|
|
1259
|
+
}
|
|
1260
|
+
const settled = /* @__PURE__ */ new Map();
|
|
1261
|
+
for (const [key, set2] of candidates) {
|
|
1262
|
+
const answers = withoutPlaceholders(set2, placeholders);
|
|
1263
|
+
if (answers.length === 1) {
|
|
1264
|
+
settled.set(key, answers[0]);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return settled;
|
|
1268
|
+
}
|
|
1269
|
+
function placeholderValues(db) {
|
|
1270
|
+
return new Set(db.facts("placeholderValue").map((row) => String(row[0])));
|
|
1271
|
+
}
|
|
1272
|
+
function withoutPlaceholders(answers, placeholders) {
|
|
1273
|
+
const kept = [...answers].filter((answer) => !placeholders.has(answer));
|
|
1274
|
+
if (kept.length === 0) {
|
|
1275
|
+
return [...answers];
|
|
1276
|
+
}
|
|
1277
|
+
return kept;
|
|
1278
|
+
}
|
|
817
1279
|
var VALUE_STEP = constant("value");
|
|
818
1280
|
var NAMESPACE_IMPORT_NAME = "*";
|
|
819
1281
|
var NAMESPACE_IMPORT = constant(NAMESPACE_IMPORT_NAME);
|
|
@@ -1005,6 +1467,18 @@ var RESOLUTION_RULES = [
|
|
|
1005
1467
|
[v("x"), v("z")],
|
|
1006
1468
|
[lit("reaches", v("x"), v("z"), VALUE_STEP), lit("objectValue", v("z"))]
|
|
1007
1469
|
),
|
|
1470
|
+
// A call is written as whatever its callee's return value is written
|
|
1471
|
+
// as. Unlike `comesTo`, this has nothing to protect: which expression
|
|
1472
|
+
// a value was written as does not compete with unwrapping it.
|
|
1473
|
+
rule(
|
|
1474
|
+
"isWrittenAs",
|
|
1475
|
+
[v("x"), v("z")],
|
|
1476
|
+
[
|
|
1477
|
+
lit("invokes", v("x"), v("f")),
|
|
1478
|
+
lit("returnsValue", v("f"), v("ret")),
|
|
1479
|
+
lit("isWrittenAs", v("ret"), v("z"))
|
|
1480
|
+
]
|
|
1481
|
+
),
|
|
1008
1482
|
// What one call site put in a parameter, told apart from what the
|
|
1009
1483
|
// other callers passed.
|
|
1010
1484
|
rule(
|
|
@@ -1552,23 +2026,33 @@ function resolveCalls(db, callKeys) {
|
|
|
1552
2026
|
for (const callKey of callKeys) {
|
|
1553
2027
|
db.add("wanted", [callKey]);
|
|
1554
2028
|
}
|
|
1555
|
-
|
|
2029
|
+
evaluate2(db, RESOLUTION_PROGRAM.rules);
|
|
1556
2030
|
}
|
|
1557
2031
|
function subjectConstructions(db, valueKeys) {
|
|
1558
2032
|
const found = /* @__PURE__ */ new Map();
|
|
1559
2033
|
if (valueKeys.length === 0) {
|
|
1560
2034
|
return found;
|
|
1561
2035
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
2036
|
+
const askSubjects = (keys) => {
|
|
2037
|
+
for (const key of keys) {
|
|
2038
|
+
db.add("wantedSubject", [key]);
|
|
2039
|
+
}
|
|
2040
|
+
evaluate2(db, RESOLUTION_PROGRAM.rules);
|
|
2041
|
+
};
|
|
2042
|
+
askSubjects(valueKeys);
|
|
2043
|
+
const placeholders = placeholderValues(db);
|
|
2044
|
+
const written = singleAnswers(db.facts("wantedSubjectWritten"), placeholders);
|
|
2045
|
+
const calls = new Set(db.facts("call").map((row) => String(row[0])));
|
|
2046
|
+
const throughCalls = new Set(
|
|
2047
|
+
[...written.values()].filter((candidate) => calls.has(candidate))
|
|
2048
|
+
);
|
|
2049
|
+
if (throughCalls.size > 0) {
|
|
2050
|
+
askSubjects(throughCalls);
|
|
1571
2051
|
}
|
|
2052
|
+
const writtenThroughCall = singleAnswers(
|
|
2053
|
+
db.facts("wantedSubjectWritten"),
|
|
2054
|
+
placeholders
|
|
2055
|
+
);
|
|
1572
2056
|
const origins = /* @__PURE__ */ new Map();
|
|
1573
2057
|
for (const row of db.facts("wantedSubjectConstruction")) {
|
|
1574
2058
|
const key = `${String(row[0])}|${String(row[1])}`;
|
|
@@ -1577,12 +2061,14 @@ function subjectConstructions(db, valueKeys) {
|
|
|
1577
2061
|
origins.set(key, listed);
|
|
1578
2062
|
}
|
|
1579
2063
|
for (const valueKey2 of valueKeys) {
|
|
1580
|
-
const
|
|
1581
|
-
if (
|
|
2064
|
+
const direct = written.get(valueKey2);
|
|
2065
|
+
if (direct === void 0) {
|
|
1582
2066
|
continue;
|
|
1583
2067
|
}
|
|
1584
|
-
const
|
|
1585
|
-
const
|
|
2068
|
+
const deeper = calls.has(direct) ? writtenThroughCall.get(direct) : void 0;
|
|
2069
|
+
const constructionKey = deeper ?? direct;
|
|
2070
|
+
const originOf = deeper === void 0 ? valueKey2 : direct;
|
|
2071
|
+
const from = origins.get(`${originOf}|${constructionKey}`);
|
|
1586
2072
|
if (from !== void 0) {
|
|
1587
2073
|
found.set(valueKey2, { constructionKey, origins: from });
|
|
1588
2074
|
}
|
|
@@ -1778,6 +2264,9 @@ function emitExpressionFact(emitter, child) {
|
|
|
1778
2264
|
if (WRITTEN_VALUE_TYPES.has(child.type)) {
|
|
1779
2265
|
add(emitter, "writtenValue", nodeId(emitter.filePath, child));
|
|
1780
2266
|
}
|
|
2267
|
+
if (child.type === "none") {
|
|
2268
|
+
add(emitter, "placeholderValue", nodeId(emitter.filePath, child));
|
|
2269
|
+
}
|
|
1781
2270
|
}
|
|
1782
2271
|
function emitExpressionFacts(emitter, node) {
|
|
1783
2272
|
walkExpressions(emitter, node, (child) => {
|
|
@@ -2236,7 +2725,7 @@ function predicateOf(node) {
|
|
|
2236
2725
|
}
|
|
2237
2726
|
|
|
2238
2727
|
// src/paths/effects.ts
|
|
2239
|
-
var
|
|
2728
|
+
var NESTED_DEFINITION_TYPES = /* @__PURE__ */ new Set(["function_definition"]);
|
|
2240
2729
|
var LITERAL_ARGS = {
|
|
2241
2730
|
string: (node) => ({ kind: "string", value: node.text.slice(1, -1) }),
|
|
2242
2731
|
integer: (node) => {
|
|
@@ -2252,7 +2741,7 @@ var LITERAL_ARGS = {
|
|
|
2252
2741
|
};
|
|
2253
2742
|
function bodyCalls(node, found = []) {
|
|
2254
2743
|
for (const child of node.namedChildren) {
|
|
2255
|
-
if (child === null ||
|
|
2744
|
+
if (child === null || NESTED_DEFINITION_TYPES.has(child.type)) {
|
|
2256
2745
|
continue;
|
|
2257
2746
|
}
|
|
2258
2747
|
if (child.type === "call") {
|
|
@@ -2294,26 +2783,12 @@ function argsOf(call) {
|
|
|
2294
2783
|
function calleeText(call) {
|
|
2295
2784
|
return field(call, "function")?.text ?? call.text;
|
|
2296
2785
|
}
|
|
2297
|
-
function withoutChainLinks(calls) {
|
|
2298
|
-
const isLink = /* @__PURE__ */ new Set();
|
|
2299
|
-
for (const call of calls) {
|
|
2300
|
-
const callee = field(call, "function");
|
|
2301
|
-
if (callee === null || callee.type !== "attribute") {
|
|
2302
|
-
continue;
|
|
2303
|
-
}
|
|
2304
|
-
const object2 = field(callee, "object");
|
|
2305
|
-
if (object2 !== null && object2.type === "call") {
|
|
2306
|
-
isLink.add(object2.id);
|
|
2307
|
-
}
|
|
2308
|
-
}
|
|
2309
|
-
return calls.filter((call) => !isLink.has(call.id));
|
|
2310
|
-
}
|
|
2311
2786
|
function invocationEffects(definitionNode) {
|
|
2312
2787
|
const body = field(definitionNode, "body");
|
|
2313
2788
|
if (body === null) {
|
|
2314
2789
|
return [];
|
|
2315
2790
|
}
|
|
2316
|
-
const calls =
|
|
2791
|
+
const calls = bodyCalls(body).sort((a, b) => a.endIndex - b.endIndex);
|
|
2317
2792
|
if (calls.length === 0) {
|
|
2318
2793
|
return [];
|
|
2319
2794
|
}
|
|
@@ -3191,10 +3666,10 @@ function mergeDefs(...defs) {
|
|
|
3191
3666
|
function cloneDef(schema) {
|
|
3192
3667
|
return mergeDefs(schema._zod.def);
|
|
3193
3668
|
}
|
|
3194
|
-
function getElementAtPath(obj,
|
|
3195
|
-
if (!
|
|
3669
|
+
function getElementAtPath(obj, path6) {
|
|
3670
|
+
if (!path6)
|
|
3196
3671
|
return obj;
|
|
3197
|
-
return
|
|
3672
|
+
return path6.reduce((acc, key) => acc?.[key], obj);
|
|
3198
3673
|
}
|
|
3199
3674
|
function promiseAllObject(promisesObj) {
|
|
3200
3675
|
const keys = Object.keys(promisesObj);
|
|
@@ -3603,11 +4078,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
3603
4078
|
}
|
|
3604
4079
|
return false;
|
|
3605
4080
|
}
|
|
3606
|
-
function prefixIssues(
|
|
4081
|
+
function prefixIssues(path6, issues) {
|
|
3607
4082
|
return issues.map((iss) => {
|
|
3608
4083
|
var _a3;
|
|
3609
4084
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
3610
|
-
iss.path.unshift(
|
|
4085
|
+
iss.path.unshift(path6);
|
|
3611
4086
|
return iss;
|
|
3612
4087
|
});
|
|
3613
4088
|
}
|
|
@@ -3754,16 +4229,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
|
|
|
3754
4229
|
}
|
|
3755
4230
|
function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
3756
4231
|
const fieldErrors = { _errors: [] };
|
|
3757
|
-
const processError = (error52,
|
|
4232
|
+
const processError = (error52, path6 = []) => {
|
|
3758
4233
|
for (const issue2 of error52.issues) {
|
|
3759
4234
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3760
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4235
|
+
issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
|
|
3761
4236
|
} else if (issue2.code === "invalid_key") {
|
|
3762
|
-
processError({ issues: issue2.issues }, [...
|
|
4237
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
3763
4238
|
} else if (issue2.code === "invalid_element") {
|
|
3764
|
-
processError({ issues: issue2.issues }, [...
|
|
4239
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
3765
4240
|
} else {
|
|
3766
|
-
const fullpath = [...
|
|
4241
|
+
const fullpath = [...path6, ...issue2.path];
|
|
3767
4242
|
if (fullpath.length === 0) {
|
|
3768
4243
|
fieldErrors._errors.push(mapper(issue2));
|
|
3769
4244
|
} else {
|
|
@@ -3790,17 +4265,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
|
|
|
3790
4265
|
}
|
|
3791
4266
|
function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
3792
4267
|
const result = { errors: [] };
|
|
3793
|
-
const processError = (error52,
|
|
4268
|
+
const processError = (error52, path6 = []) => {
|
|
3794
4269
|
var _a3, _b;
|
|
3795
4270
|
for (const issue2 of error52.issues) {
|
|
3796
4271
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
3797
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
4272
|
+
issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
|
|
3798
4273
|
} else if (issue2.code === "invalid_key") {
|
|
3799
|
-
processError({ issues: issue2.issues }, [...
|
|
4274
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
3800
4275
|
} else if (issue2.code === "invalid_element") {
|
|
3801
|
-
processError({ issues: issue2.issues }, [...
|
|
4276
|
+
processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
|
|
3802
4277
|
} else {
|
|
3803
|
-
const fullpath = [...
|
|
4278
|
+
const fullpath = [...path6, ...issue2.path];
|
|
3804
4279
|
if (fullpath.length === 0) {
|
|
3805
4280
|
result.errors.push(mapper(issue2));
|
|
3806
4281
|
continue;
|
|
@@ -3832,8 +4307,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
|
|
|
3832
4307
|
}
|
|
3833
4308
|
function toDotPath(_path) {
|
|
3834
4309
|
const segs = [];
|
|
3835
|
-
const
|
|
3836
|
-
for (const seg of
|
|
4310
|
+
const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
4311
|
+
for (const seg of path6) {
|
|
3837
4312
|
if (typeof seg === "number")
|
|
3838
4313
|
segs.push(`[${seg}]`);
|
|
3839
4314
|
else if (typeof seg === "symbol")
|
|
@@ -16525,13 +17000,13 @@ function resolveRef(ref, ctx) {
|
|
|
16525
17000
|
if (!ref.startsWith("#")) {
|
|
16526
17001
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
16527
17002
|
}
|
|
16528
|
-
const
|
|
16529
|
-
if (
|
|
17003
|
+
const path6 = ref.slice(1).split("/").filter(Boolean);
|
|
17004
|
+
if (path6.length === 0) {
|
|
16530
17005
|
return ctx.rootSchema;
|
|
16531
17006
|
}
|
|
16532
17007
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
16533
|
-
if (
|
|
16534
|
-
const key =
|
|
17008
|
+
if (path6[0] === defsKey) {
|
|
17009
|
+
const key = path6[1];
|
|
16535
17010
|
if (!key || !ctx.defs[key]) {
|
|
16536
17011
|
throw new Error(`Reference not found: ${ref}`);
|
|
16537
17012
|
}
|
|
@@ -17291,8 +17766,8 @@ function baseUrlReference(semantics) {
|
|
|
17291
17766
|
const label = OPENING_HOLE.exec(semantics.path ?? "")?.[1];
|
|
17292
17767
|
return label === void 0 ? null : referenceFromName(patternHole(label));
|
|
17293
17768
|
}
|
|
17294
|
-
function normalizePath(
|
|
17295
|
-
let normalized =
|
|
17769
|
+
function normalizePath(path6) {
|
|
17770
|
+
let normalized = path6.replace(/:([a-zA-Z_]\w*)/g, "{$1}");
|
|
17296
17771
|
if (normalized.length > 1 && normalized.endsWith("/")) {
|
|
17297
17772
|
normalized = normalized.slice(0, -1);
|
|
17298
17773
|
}
|
|
@@ -17302,8 +17777,8 @@ function normalizePath(path4) {
|
|
|
17302
17777
|
);
|
|
17303
17778
|
return normalized;
|
|
17304
17779
|
}
|
|
17305
|
-
function pathShape(
|
|
17306
|
-
return normalizePath(
|
|
17780
|
+
function pathShape(path6) {
|
|
17781
|
+
return normalizePath(path6).replace(/\{[^}]*\}/g, "{}");
|
|
17307
17782
|
}
|
|
17308
17783
|
function methodsAgree(a, b) {
|
|
17309
17784
|
if (a === null || b === null) {
|
|
@@ -17381,8 +17856,8 @@ var restSemantics = defineBoundarySemantics({
|
|
|
17381
17856
|
return null;
|
|
17382
17857
|
}
|
|
17383
17858
|
const method = semantics.method === null ? "ANY" : semantics.method.toUpperCase();
|
|
17384
|
-
const
|
|
17385
|
-
return `${method} ${
|
|
17859
|
+
const path6 = semantics.path === null ? "?" : normalizePath(semantics.path);
|
|
17860
|
+
return `${method} ${path6}`;
|
|
17386
17861
|
},
|
|
17387
17862
|
/**
|
|
17388
17863
|
* A route with both halves stated decides for itself: the method
|
|
@@ -17391,14 +17866,14 @@ var restSemantics = defineBoundarySemantics({
|
|
|
17391
17866
|
* half might still be the one that handles the request, and nothing
|
|
17392
17867
|
* here can settle that, so it abstains instead of refusing.
|
|
17393
17868
|
*/
|
|
17394
|
-
servesRequest(semantics, method,
|
|
17869
|
+
servesRequest(semantics, method, path6) {
|
|
17395
17870
|
if (semantics.method === null || semantics.path === null) {
|
|
17396
17871
|
return "unknown";
|
|
17397
17872
|
}
|
|
17398
17873
|
if (!methodsAgree(semantics.method, method.toUpperCase())) {
|
|
17399
17874
|
return "nomatch";
|
|
17400
17875
|
}
|
|
17401
|
-
return routePathAdmits(semantics.path,
|
|
17876
|
+
return routePathAdmits(semantics.path, path6) ? "match" : "nomatch";
|
|
17402
17877
|
},
|
|
17403
17878
|
/**
|
|
17404
17879
|
* A route the registration pattern admits is inside it. A route
|
|
@@ -18035,71 +18510,10 @@ function returnTypeName(node) {
|
|
|
18035
18510
|
}
|
|
18036
18511
|
return annotation.type === "subscript" ? field(annotation, "value")?.text ?? null : annotation.text;
|
|
18037
18512
|
}
|
|
18038
|
-
function callsIn(node, found = []) {
|
|
18039
|
-
for (const child of node.namedChildren) {
|
|
18040
|
-
if (child === null) {
|
|
18041
|
-
continue;
|
|
18042
|
-
}
|
|
18043
|
-
if (child.type === "call") {
|
|
18044
|
-
found.push(child);
|
|
18045
|
-
}
|
|
18046
|
-
callsIn(child, found);
|
|
18047
|
-
}
|
|
18048
|
-
return found;
|
|
18049
|
-
}
|
|
18050
|
-
function storageEffects(calls, options) {
|
|
18051
|
-
const seen = /* @__PURE__ */ new Set();
|
|
18052
|
-
const asked = new Set(
|
|
18053
|
-
options.facts.facts("wanted").map((row) => String(row[0]))
|
|
18054
|
-
);
|
|
18055
|
-
const gather = (inCalls, filePath) => {
|
|
18056
|
-
const found = [...directStorageEffects(inCalls, { ...options, filePath })];
|
|
18057
|
-
resolveCalls(
|
|
18058
|
-
options.facts,
|
|
18059
|
-
inCalls.filter((call) => options.leadsToStorage.has(methodName(call))).map((call) => field(call, "function")).filter((callee) => callee !== null).map((callee) => calleeKeyOf(filePath, callee)).filter((key) => {
|
|
18060
|
-
const fresh = !asked.has(key);
|
|
18061
|
-
asked.add(key);
|
|
18062
|
-
return fresh;
|
|
18063
|
-
})
|
|
18064
|
-
);
|
|
18065
|
-
for (const call of inCalls) {
|
|
18066
|
-
const callee = field(call, "function");
|
|
18067
|
-
if (callee === null || !options.leadsToStorage.has(methodName(call))) {
|
|
18068
|
-
continue;
|
|
18069
|
-
}
|
|
18070
|
-
const key = resolvedOnce(options, calleeKeyOf(filePath, callee));
|
|
18071
|
-
const body = key === void 0 ? void 0 : options.definitionAt(key);
|
|
18072
|
-
if (key === void 0 || body === void 0 || seen.has(key)) {
|
|
18073
|
-
continue;
|
|
18074
|
-
}
|
|
18075
|
-
seen.add(key);
|
|
18076
|
-
const name = field(body, "name")?.text;
|
|
18077
|
-
const origin = {
|
|
18078
|
-
file: fileOf(key),
|
|
18079
|
-
line: body.startPosition.row + 1,
|
|
18080
|
-
...name === void 0 ? {} : { function: name }
|
|
18081
|
-
};
|
|
18082
|
-
found.push(
|
|
18083
|
-
...gather(callsIn(body), fileOf(key)).map(
|
|
18084
|
-
(effect) => effect.type === "interaction" && effect.origin === void 0 ? { ...effect, origin } : effect
|
|
18085
|
-
)
|
|
18086
|
-
);
|
|
18087
|
-
}
|
|
18088
|
-
return found;
|
|
18089
|
-
};
|
|
18090
|
-
return gather(calls, options.filePath);
|
|
18091
|
-
}
|
|
18092
|
-
function calleeKeyOf(filePath, callee) {
|
|
18093
|
-
return callee.type === "identifier" ? `${filePath}#${callee.text}` : nodeId(filePath, callee);
|
|
18094
|
-
}
|
|
18095
|
-
function resolvedOnce(options, calleeKey) {
|
|
18096
|
-
const resolved = options.facts.facts("wantedResolves").filter((row) => String(row[0]) === calleeKey).map((row) => String(row[1]));
|
|
18097
|
-
return resolved.length === 1 ? resolved[0] : void 0;
|
|
18098
|
-
}
|
|
18099
18513
|
function storageCallees(calls, filePath, couldMatch) {
|
|
18100
18514
|
return chainsIn(calls).filter((chain) => couldMatch.has(methodName(chain.root))).map((chain) => field(chain.root, "function")).filter((callee) => callee !== null).map((callee) => nodeId(filePath, callee));
|
|
18101
18515
|
}
|
|
18102
|
-
function
|
|
18516
|
+
function storageEffects(calls, options) {
|
|
18103
18517
|
if (options.patterns.length === 0) {
|
|
18104
18518
|
return [];
|
|
18105
18519
|
}
|
|
@@ -18157,7 +18571,7 @@ function directStorageEffects(calls, options) {
|
|
|
18157
18571
|
// src/discovery.ts
|
|
18158
18572
|
function decoratedStatements(root, module) {
|
|
18159
18573
|
const found = [];
|
|
18160
|
-
|
|
18574
|
+
walkDescendants2(
|
|
18161
18575
|
root,
|
|
18162
18576
|
{ scope: module.moduleScope, enclosingFunction: null },
|
|
18163
18577
|
{
|
|
@@ -18196,21 +18610,21 @@ function builtSubjects(decorated, options) {
|
|
|
18196
18610
|
}
|
|
18197
18611
|
const factsPath = options.absoluteFile ?? options.filePath;
|
|
18198
18612
|
const asked = /* @__PURE__ */ new Set();
|
|
18199
|
-
for (const { enclosingFunction, decorators } of decorated) {
|
|
18613
|
+
for (const { enclosingFunction: enclosingFunction2, decorators } of decorated) {
|
|
18200
18614
|
for (const { node, direct } of decorators) {
|
|
18201
18615
|
if (direct.module !== null && acceptedByAnyPattern(direct.module, options)) {
|
|
18202
18616
|
continue;
|
|
18203
18617
|
}
|
|
18204
18618
|
const written = decoratorReceiver(node);
|
|
18205
18619
|
if (written !== null) {
|
|
18206
|
-
asked.add(readKey(factsPath, written.object,
|
|
18620
|
+
asked.add(readKey(factsPath, written.object, enclosingFunction2));
|
|
18207
18621
|
}
|
|
18208
18622
|
}
|
|
18209
18623
|
}
|
|
18210
18624
|
return subjectConstructions(facts, [...asked]);
|
|
18211
18625
|
}
|
|
18212
18626
|
function decoratedUnits(decoratedStatement, module, options, subjects) {
|
|
18213
|
-
const { stmt, scope, enclosingFunction, decorators } = decoratedStatement;
|
|
18627
|
+
const { stmt, scope, enclosingFunction: enclosingFunction2, decorators } = decoratedStatement;
|
|
18214
18628
|
const units = [];
|
|
18215
18629
|
const { definition } = stripDecorators(stmt);
|
|
18216
18630
|
for (const { node: decoratorNode, direct } of decorators) {
|
|
@@ -18226,7 +18640,7 @@ function decoratedUnits(decoratedStatement, module, options, subjects) {
|
|
|
18226
18640
|
) ?? null
|
|
18227
18641
|
) ?? builtSubjectClassification(
|
|
18228
18642
|
decoratorNode,
|
|
18229
|
-
|
|
18643
|
+
enclosingFunction2,
|
|
18230
18644
|
subjects,
|
|
18231
18645
|
options
|
|
18232
18646
|
)
|
|
@@ -18262,14 +18676,14 @@ function acceptedByAnyPattern(module, options) {
|
|
|
18262
18676
|
(pack) => pack.discovery.some((pattern) => pattern.importModule.includes(module))
|
|
18263
18677
|
);
|
|
18264
18678
|
}
|
|
18265
|
-
function builtSubjectClassification(decoratorNode,
|
|
18679
|
+
function builtSubjectClassification(decoratorNode, enclosingFunction2, subjects, options) {
|
|
18266
18680
|
const written = decoratorReceiver(decoratorNode);
|
|
18267
18681
|
if (written === null) {
|
|
18268
18682
|
return null;
|
|
18269
18683
|
}
|
|
18270
18684
|
const factsPath = options.absoluteFile ?? options.filePath;
|
|
18271
18685
|
const built = subjects.get(
|
|
18272
|
-
readKey(factsPath, written.object,
|
|
18686
|
+
readKey(factsPath, written.object, enclosingFunction2)
|
|
18273
18687
|
);
|
|
18274
18688
|
const origin = built?.origins.find(
|
|
18275
18689
|
(candidate) => acceptedByAnyPattern(candidate.module, options)
|
|
@@ -18342,9 +18756,9 @@ function readPathArgument(classification) {
|
|
|
18342
18756
|
}
|
|
18343
18757
|
var NO_PATH_PARAMS = /* @__PURE__ */ new Set();
|
|
18344
18758
|
function templateReader(template) {
|
|
18345
|
-
return (
|
|
18759
|
+
return (path6) => {
|
|
18346
18760
|
const names = [];
|
|
18347
|
-
const canonical =
|
|
18761
|
+
const canonical = path6.replace(template, (_match, name) => {
|
|
18348
18762
|
names.push(name);
|
|
18349
18763
|
return `{${name}}`;
|
|
18350
18764
|
});
|
|
@@ -18357,10 +18771,10 @@ var PATH_TEMPLATE_READERS = {
|
|
|
18357
18771
|
/<(?:[A-Za-z_]\w*(?:\(.*?\))?:)?([A-Za-z_]\w*)>/g
|
|
18358
18772
|
)
|
|
18359
18773
|
};
|
|
18360
|
-
function readPathTemplate(pattern,
|
|
18774
|
+
function readPathTemplate(pattern, path6, range) {
|
|
18361
18775
|
const syntax = pattern.pathParamSyntax;
|
|
18362
18776
|
if (syntax === void 0) {
|
|
18363
|
-
return writtenReading({ path:
|
|
18777
|
+
return writtenReading({ path: path6, paramNames: NO_PATH_PARAMS }, range);
|
|
18364
18778
|
}
|
|
18365
18779
|
const reader = PATH_TEMPLATE_READERS[syntax];
|
|
18366
18780
|
if (reader === void 0) {
|
|
@@ -18369,7 +18783,7 @@ function readPathTemplate(pattern, path4, range) {
|
|
|
18369
18783
|
range
|
|
18370
18784
|
);
|
|
18371
18785
|
}
|
|
18372
|
-
return writtenReading(reader(
|
|
18786
|
+
return writtenReading(reader(path6), range);
|
|
18373
18787
|
}
|
|
18374
18788
|
function classRouteUnits(pattern, pack, classification, classNode, module, options) {
|
|
18375
18789
|
const pathArgument = readPathArgument(classification);
|
|
@@ -18439,7 +18853,7 @@ function readRoutePath(pattern, pathArgument, classification, module, options) {
|
|
|
18439
18853
|
);
|
|
18440
18854
|
return andThenReading(
|
|
18441
18855
|
composed,
|
|
18442
|
-
(
|
|
18856
|
+
(path6, range) => readPathTemplate(pattern, path6, range)
|
|
18443
18857
|
);
|
|
18444
18858
|
}
|
|
18445
18859
|
function readRoutePaths(pattern, pathArgument, classification, module, options) {
|
|
@@ -18468,7 +18882,7 @@ function readRoutePaths(pattern, pathArgument, classification, module, options)
|
|
|
18468
18882
|
pathArgument,
|
|
18469
18883
|
(literal2, range) => writtenReading(servedSpelling(pattern, prefix + literal2), range)
|
|
18470
18884
|
),
|
|
18471
|
-
(
|
|
18885
|
+
(path6, range) => readPathTemplate(pattern, path6, range)
|
|
18472
18886
|
),
|
|
18473
18887
|
mount: { siblings: resolution.values.length, prefix }
|
|
18474
18888
|
}));
|
|
@@ -18510,11 +18924,11 @@ function composeRoutePath(pattern, classification, module, options, literal2, ra
|
|
|
18510
18924
|
);
|
|
18511
18925
|
}
|
|
18512
18926
|
var REPEATED_SLASH_READERS = {
|
|
18513
|
-
kept: (
|
|
18514
|
-
merged: (
|
|
18927
|
+
kept: (path6) => path6,
|
|
18928
|
+
merged: (path6) => path6.replace(/\/{2,}/g, "/")
|
|
18515
18929
|
};
|
|
18516
|
-
function servedSpelling(pattern,
|
|
18517
|
-
return REPEATED_SLASH_READERS[pattern.pathRepeatedSlashes ?? "kept"](
|
|
18930
|
+
function servedSpelling(pattern, path6) {
|
|
18931
|
+
return REPEATED_SLASH_READERS[pattern.pathRepeatedSlashes ?? "kept"](path6);
|
|
18518
18932
|
}
|
|
18519
18933
|
function readRouterPrefix(pattern, classification, module, options, range) {
|
|
18520
18934
|
const resolution = routerResolutionOf(
|
|
@@ -18626,8 +19040,8 @@ function branchesFromReturns(readsReturnedStatus, definitionNode, effects, respo
|
|
|
18626
19040
|
"This return gives a status this reading cannot resolve to a number, so this outcome claims none",
|
|
18627
19041
|
range
|
|
18628
19042
|
);
|
|
18629
|
-
for (const
|
|
18630
|
-
const conditions =
|
|
19043
|
+
for (const path6 of paths) {
|
|
19044
|
+
const conditions = path6.map((condition) => ({
|
|
18631
19045
|
sourceText: condition.sourceText,
|
|
18632
19046
|
structured: condition.expression === null ? null : predicateOf(condition.expression),
|
|
18633
19047
|
polarity: condition.polarity,
|
|
@@ -18656,7 +19070,7 @@ function branchesFromReturns(readsReturnedStatus, definitionNode, effects, respo
|
|
|
18656
19070
|
},
|
|
18657
19071
|
effects: effectsReaching(effects, conditions),
|
|
18658
19072
|
location: range,
|
|
18659
|
-
isDefault:
|
|
19073
|
+
isDefault: path6.length === 0
|
|
18660
19074
|
});
|
|
18661
19075
|
}
|
|
18662
19076
|
}
|
|
@@ -18867,17 +19281,7 @@ function buildRouteUnit(options) {
|
|
|
18867
19281
|
() => readReturnAnnotation(definitionNode, enclosingScope, ctx)
|
|
18868
19282
|
);
|
|
18869
19283
|
const effects = invocationEffects(definitionNode);
|
|
18870
|
-
const
|
|
18871
|
-
...storageEffects(bodyCalls(definitionNode), {
|
|
18872
|
-
...storageLookup,
|
|
18873
|
-
filePath: storageLookup.factsPath
|
|
18874
|
-
}),
|
|
18875
|
-
...rawSqlEffects(bodyCalls(definitionNode), {
|
|
18876
|
-
facts: storageLookup.facts,
|
|
18877
|
-
filePath: storageLookup.factsPath,
|
|
18878
|
-
patterns: storageLookup.rawSql ?? []
|
|
18879
|
-
})
|
|
18880
|
-
];
|
|
19284
|
+
const extra = recognizedBodyEffects(definitionNode, module, storageLookup);
|
|
18881
19285
|
const perReturn = branchesFromReturns(
|
|
18882
19286
|
readsReturnedStatus,
|
|
18883
19287
|
definitionNode,
|
|
@@ -18886,9 +19290,9 @@ function buildRouteUnit(options) {
|
|
|
18886
19290
|
statusCode
|
|
18887
19291
|
);
|
|
18888
19292
|
const branches = (perReturn ?? []).map(
|
|
18889
|
-
(branch) =>
|
|
19293
|
+
(branch) => extra.length === 0 ? branch : { ...branch, extraEffects: extra }
|
|
18890
19294
|
);
|
|
18891
|
-
if (perReturn === null && (responseShape.reading.kind !== "absent" || statusCode.reading.kind !== "absent" ||
|
|
19295
|
+
if (perReturn === null && (responseShape.reading.kind !== "absent" || statusCode.reading.kind !== "absent" || extra.length > 0 || effects.length > 0)) {
|
|
18892
19296
|
branches.push({
|
|
18893
19297
|
conditions: [],
|
|
18894
19298
|
terminal: {
|
|
@@ -18908,7 +19312,7 @@ function buildRouteUnit(options) {
|
|
|
18908
19312
|
statusCodeReading: statusCode,
|
|
18909
19313
|
bodyShapeReading: { reading: responseShape.reading },
|
|
18910
19314
|
effects,
|
|
18911
|
-
...
|
|
19315
|
+
...extra.length === 0 ? {} : { extraEffects: extra },
|
|
18912
19316
|
location: rangeOf(definitionNode),
|
|
18913
19317
|
isDefault: true
|
|
18914
19318
|
});
|
|
@@ -18956,6 +19360,20 @@ function unreadRoleReadings(parameters, range) {
|
|
|
18956
19360
|
)
|
|
18957
19361
|
];
|
|
18958
19362
|
}
|
|
19363
|
+
function recognizedBodyEffects(definitionNode, module, storageLookup) {
|
|
19364
|
+
const storage = storageLookup === void 0 ? [] : [
|
|
19365
|
+
...storageEffects(bodyCalls(definitionNode), {
|
|
19366
|
+
...storageLookup,
|
|
19367
|
+
filePath: storageLookup.factsPath
|
|
19368
|
+
}),
|
|
19369
|
+
...rawSqlEffects(bodyCalls(definitionNode), {
|
|
19370
|
+
facts: storageLookup.facts,
|
|
19371
|
+
filePath: storageLookup.factsPath,
|
|
19372
|
+
patterns: storageLookup.rawSql ?? []
|
|
19373
|
+
})
|
|
19374
|
+
];
|
|
19375
|
+
return [...envReadEffects(definitionNode, module), ...storage];
|
|
19376
|
+
}
|
|
18959
19377
|
function bodyContentOf(bodyNode) {
|
|
18960
19378
|
for (const stmt of bodyStatements(bodyNode)) {
|
|
18961
19379
|
if (stmt.type === "pass_statement") {
|
|
@@ -19091,6 +19509,11 @@ function parameterNameAndType(param) {
|
|
|
19091
19509
|
return null;
|
|
19092
19510
|
}
|
|
19093
19511
|
|
|
19512
|
+
// src/facts/why.ts
|
|
19513
|
+
import fs4 from "fs";
|
|
19514
|
+
import path4 from "path";
|
|
19515
|
+
import { Database as Database4 } from "@suss/datalog";
|
|
19516
|
+
|
|
19094
19517
|
// src/moduleResolver.ts
|
|
19095
19518
|
import fs from "fs";
|
|
19096
19519
|
import path from "path";
|
|
@@ -19183,43 +19606,38 @@ function importedModuleText(module, relativeLevel) {
|
|
|
19183
19606
|
return relativeLevel > 0 ? `${".".repeat(relativeLevel)}${module}` : module;
|
|
19184
19607
|
}
|
|
19185
19608
|
function emitModuleImportFacts(db, filePath, module, resolverOptions) {
|
|
19186
|
-
const
|
|
19187
|
-
|
|
19188
|
-
|
|
19189
|
-
|
|
19190
|
-
|
|
19191
|
-
|
|
19192
|
-
|
|
19193
|
-
|
|
19194
|
-
|
|
19195
|
-
|
|
19196
|
-
|
|
19197
|
-
|
|
19198
|
-
|
|
19199
|
-
|
|
19200
|
-
|
|
19201
|
-
|
|
19202
|
-
|
|
19203
|
-
|
|
19204
|
-
|
|
19205
|
-
resolution.status === "resolved" ? "resolved" : resolution.reason
|
|
19206
|
-
]);
|
|
19207
|
-
const importedName = binding.kind === "import" ? binding.localName : binding.importedName;
|
|
19208
|
-
db.add("pyImportedName", [
|
|
19209
|
-
`${filePath}#${localName}`,
|
|
19210
|
-
moduleText,
|
|
19211
|
-
importedName
|
|
19212
|
-
]);
|
|
19213
|
-
if (resolution.status === "resolved") {
|
|
19214
|
-
db.add("pyImportResolved", [filePath, moduleText, resolution.file]);
|
|
19215
|
-
db.add("imports", [
|
|
19216
|
-
`${filePath}#${localName}`,
|
|
19217
|
-
resolution.file,
|
|
19218
|
-
importedName
|
|
19609
|
+
for (const scope of module.scopeFor.values()) {
|
|
19610
|
+
for (const [localName, binding] of scope.bindings) {
|
|
19611
|
+
if (binding.kind !== "import" && binding.kind !== "importFrom") {
|
|
19612
|
+
continue;
|
|
19613
|
+
}
|
|
19614
|
+
const nameKey = `${filePath}#${localName}`;
|
|
19615
|
+
const moduleText = importedModuleText(
|
|
19616
|
+
binding.module,
|
|
19617
|
+
binding.relativeLevel
|
|
19618
|
+
);
|
|
19619
|
+
const resolution = resolveModule(
|
|
19620
|
+
filePath,
|
|
19621
|
+
{ module: binding.module, relativeLevel: binding.relativeLevel },
|
|
19622
|
+
resolverOptions
|
|
19623
|
+
);
|
|
19624
|
+
db.add("pyImport", [
|
|
19625
|
+
filePath,
|
|
19626
|
+
moduleText,
|
|
19627
|
+
resolution.status === "resolved" ? "resolved" : resolution.reason
|
|
19219
19628
|
]);
|
|
19220
|
-
|
|
19221
|
-
|
|
19222
|
-
|
|
19629
|
+
const importedName = binding.kind === "import" ? binding.localName : binding.importedName;
|
|
19630
|
+
db.add("pyImportedName", [nameKey, moduleText, importedName]);
|
|
19631
|
+
if (scope.kind === "module") {
|
|
19632
|
+
db.add("exportsAs", [filePath, localName, nameKey]);
|
|
19633
|
+
}
|
|
19634
|
+
if (resolution.status === "resolved") {
|
|
19635
|
+
db.add("pyImportResolved", [filePath, moduleText, resolution.file]);
|
|
19636
|
+
db.add("imports", [nameKey, resolution.file, importedName]);
|
|
19637
|
+
continue;
|
|
19638
|
+
}
|
|
19639
|
+
db.add("imports", [nameKey, moduleText, importedName]);
|
|
19640
|
+
}
|
|
19223
19641
|
}
|
|
19224
19642
|
for (const openModule of module.openImports) {
|
|
19225
19643
|
db.add("pyOpenImport", [filePath, openModule]);
|
|
@@ -19238,6 +19656,7 @@ var GRAMMAR_PATH = path2.join(
|
|
|
19238
19656
|
"tree-sitter-python.wasm"
|
|
19239
19657
|
);
|
|
19240
19658
|
var languagePromise = null;
|
|
19659
|
+
var loadedLanguage = null;
|
|
19241
19660
|
function loadLanguage() {
|
|
19242
19661
|
languagePromise ??= (async () => {
|
|
19243
19662
|
if (!fs2.existsSync(GRAMMAR_PATH)) {
|
|
@@ -19246,12 +19665,28 @@ function loadLanguage() {
|
|
|
19246
19665
|
);
|
|
19247
19666
|
}
|
|
19248
19667
|
await Parser.init();
|
|
19249
|
-
|
|
19668
|
+
const language = await Language.load(GRAMMAR_PATH);
|
|
19669
|
+
loadedLanguage = language;
|
|
19670
|
+
return language;
|
|
19250
19671
|
})();
|
|
19251
19672
|
return languagePromise;
|
|
19252
19673
|
}
|
|
19253
19674
|
async function parsePython(source) {
|
|
19254
19675
|
const language = await loadLanguage();
|
|
19676
|
+
return parseWith(language, source);
|
|
19677
|
+
}
|
|
19678
|
+
async function preloadPythonGrammar() {
|
|
19679
|
+
await loadLanguage();
|
|
19680
|
+
}
|
|
19681
|
+
function parsePythonSync(source) {
|
|
19682
|
+
if (loadedLanguage === null) {
|
|
19683
|
+
throw new Error(
|
|
19684
|
+
"the Python grammar has not been loaded yet; call preloadPythonGrammar first"
|
|
19685
|
+
);
|
|
19686
|
+
}
|
|
19687
|
+
return parseWith(loadedLanguage, source);
|
|
19688
|
+
}
|
|
19689
|
+
function parseWith(language, source) {
|
|
19255
19690
|
const parser = new Parser();
|
|
19256
19691
|
parser.setLanguage(language);
|
|
19257
19692
|
try {
|
|
@@ -19268,8 +19703,705 @@ async function parsePython(source) {
|
|
|
19268
19703
|
// src/project.ts
|
|
19269
19704
|
import fs3 from "fs";
|
|
19270
19705
|
import path3 from "path";
|
|
19271
|
-
import {
|
|
19272
|
-
|
|
19706
|
+
import {
|
|
19707
|
+
disambiguateSummaryIds,
|
|
19708
|
+
linkCallsToSummaries,
|
|
19709
|
+
placeArgTargets as placeArgTargets2,
|
|
19710
|
+
placeCalleeParameters as placeCalleeParameters2,
|
|
19711
|
+
placeCalls as placeCalls2,
|
|
19712
|
+
recordParameterGaps as recordParameterGaps2,
|
|
19713
|
+
summaryIdFromParts,
|
|
19714
|
+
unfollowedCallGap as unfollowedCallGap2
|
|
19715
|
+
} from "@suss/behavioral-ir";
|
|
19716
|
+
import { Database as Database3 } from "@suss/datalog";
|
|
19717
|
+
import {
|
|
19718
|
+
assembleSummary as assembleSummary2,
|
|
19719
|
+
createCacheLayer,
|
|
19720
|
+
createTimer,
|
|
19721
|
+
moduleInitStructure,
|
|
19722
|
+
noopTimer,
|
|
19723
|
+
runDigest,
|
|
19724
|
+
stampModuleImports
|
|
19725
|
+
} from "@suss/extractor";
|
|
19726
|
+
|
|
19727
|
+
// src/diagnostics.ts
|
|
19728
|
+
import {
|
|
19729
|
+
buildUngatedExtractionReport,
|
|
19730
|
+
createPackTallies,
|
|
19731
|
+
tallyUnit
|
|
19732
|
+
} from "@suss/extractor";
|
|
19733
|
+
function buildPythonExtractionReport(args) {
|
|
19734
|
+
return buildUngatedExtractionReport({
|
|
19735
|
+
packs: args.packs.map((pack) => ({
|
|
19736
|
+
name: pack.name,
|
|
19737
|
+
version: pack.version ?? null,
|
|
19738
|
+
discovers: pack.discovery.length > 0
|
|
19739
|
+
})),
|
|
19740
|
+
tallies: args.tallies,
|
|
19741
|
+
filesWalked: args.filesWalked,
|
|
19742
|
+
summaries: args.summaries
|
|
19743
|
+
});
|
|
19744
|
+
}
|
|
19745
|
+
|
|
19746
|
+
// src/reach/closure.ts
|
|
19747
|
+
import {
|
|
19748
|
+
functionCallBinding,
|
|
19749
|
+
placeArgTargets,
|
|
19750
|
+
placeCalleeParameters,
|
|
19751
|
+
placeCalls,
|
|
19752
|
+
recordParameterGaps,
|
|
19753
|
+
TargetPlacements,
|
|
19754
|
+
unfollowedCallGap,
|
|
19755
|
+
worthRecording
|
|
19756
|
+
} from "@suss/behavioral-ir";
|
|
19757
|
+
import { Database as Database2, evaluate as evaluate3, lit as lit2, rule as rule2, variable as v2 } from "@suss/datalog";
|
|
19758
|
+
import {
|
|
19759
|
+
assembleSummary,
|
|
19760
|
+
SKIP_CHILDREN as SKIP_CHILDREN2,
|
|
19761
|
+
walkDescendants as walkDescendants3
|
|
19762
|
+
} from "@suss/extractor";
|
|
19763
|
+
|
|
19764
|
+
// src/reach/resolveCallee.ts
|
|
19765
|
+
var stop = (reason) => ({ kind: "stop", reason });
|
|
19766
|
+
var MODULE_STOP = {
|
|
19767
|
+
ambiguous: "multipleSources",
|
|
19768
|
+
outsideRoots: "outsideRun"
|
|
19769
|
+
};
|
|
19770
|
+
function resolveCallee2(call, site, ctx) {
|
|
19771
|
+
const callee = field(call, "function");
|
|
19772
|
+
if (callee === null) {
|
|
19773
|
+
return { kind: "stopped", reason: "noDeclaration" };
|
|
19774
|
+
}
|
|
19775
|
+
return asCallee(resolveExpression(callee, site, ctx), ctx);
|
|
19776
|
+
}
|
|
19777
|
+
function resolveNamedFunctionArgument(name, site, ctx) {
|
|
19778
|
+
if (site.rebound.has(name)) {
|
|
19779
|
+
return null;
|
|
19780
|
+
}
|
|
19781
|
+
const binding = resolveName(site.scope, name);
|
|
19782
|
+
if (binding !== null && binding.kind === "assignment") {
|
|
19783
|
+
return null;
|
|
19784
|
+
}
|
|
19785
|
+
const resolved = binding === null ? throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set()) : resolveBinding(binding, site.file, site.scope, ctx, /* @__PURE__ */ new Set());
|
|
19786
|
+
return resolved.kind === "function" ? resolved.target : null;
|
|
19787
|
+
}
|
|
19788
|
+
function asCallee(resolved, ctx) {
|
|
19789
|
+
if (resolved.kind === "function") {
|
|
19790
|
+
return { kind: "followed", target: resolved.target };
|
|
19791
|
+
}
|
|
19792
|
+
if (resolved.kind === "class") {
|
|
19793
|
+
return asCallee(
|
|
19794
|
+
memberOfClass(resolved.file, resolved.node, "__init__", ctx),
|
|
19795
|
+
ctx
|
|
19796
|
+
);
|
|
19797
|
+
}
|
|
19798
|
+
if (resolved.kind === "stop") {
|
|
19799
|
+
return { kind: "stopped", reason: resolved.reason };
|
|
19800
|
+
}
|
|
19801
|
+
if (resolved.kind === "package") {
|
|
19802
|
+
return { kind: "stopped", reason: "outsideRun" };
|
|
19803
|
+
}
|
|
19804
|
+
return { kind: "stopped", reason: "noDeclaration" };
|
|
19805
|
+
}
|
|
19806
|
+
var EXPRESSION_RESOLVERS = {
|
|
19807
|
+
identifier: (node, site, ctx) => resolveIdentifier(node.text, site, ctx),
|
|
19808
|
+
attribute: (node, site, ctx) => {
|
|
19809
|
+
const object2 = field(node, "object");
|
|
19810
|
+
const attribute = field(node, "attribute");
|
|
19811
|
+
if (object2 === null || attribute === null) {
|
|
19812
|
+
return stop("noDeclaration");
|
|
19813
|
+
}
|
|
19814
|
+
return memberOf(resolveExpression(object2, site, ctx), attribute.text, ctx);
|
|
19815
|
+
},
|
|
19816
|
+
// `Service().run()` runs a method on whatever the constructor built.
|
|
19817
|
+
call: (node, site, ctx) => {
|
|
19818
|
+
const callee = field(node, "function");
|
|
19819
|
+
if (callee === null) {
|
|
19820
|
+
return stop("noDeclaration");
|
|
19821
|
+
}
|
|
19822
|
+
const built = resolveExpression(callee, site, ctx);
|
|
19823
|
+
return built.kind === "class" ? built : stop("noDeclaration");
|
|
19824
|
+
},
|
|
19825
|
+
parenthesized_expression: (node, site, ctx) => {
|
|
19826
|
+
const inner = node.namedChildren[0];
|
|
19827
|
+
return inner === null || inner === void 0 ? stop("noDeclaration") : resolveExpression(inner, site, ctx);
|
|
19828
|
+
}
|
|
19829
|
+
};
|
|
19830
|
+
function resolveExpression(node, site, ctx) {
|
|
19831
|
+
const resolver = EXPRESSION_RESOLVERS[node.type];
|
|
19832
|
+
return resolver === void 0 ? stop("noDeclaration") : resolver(node, site, ctx);
|
|
19833
|
+
}
|
|
19834
|
+
function resolveIdentifier(name, site, ctx) {
|
|
19835
|
+
if (site.rebound.has(name)) {
|
|
19836
|
+
return stop("unsettledValue");
|
|
19837
|
+
}
|
|
19838
|
+
if (isReceiver(name, site.scope)) {
|
|
19839
|
+
return receiverClass(site);
|
|
19840
|
+
}
|
|
19841
|
+
const binding = resolveName(site.scope, name);
|
|
19842
|
+
if (binding === null) {
|
|
19843
|
+
return throughOpenImports(site.file, name, ctx, /* @__PURE__ */ new Set());
|
|
19844
|
+
}
|
|
19845
|
+
return resolveBinding(binding, site.file, site.scope, ctx, /* @__PURE__ */ new Set());
|
|
19846
|
+
}
|
|
19847
|
+
function isReceiver(name, scope) {
|
|
19848
|
+
return (name === "self" || name === "cls") && scope.kind === "function" && scope.parent?.kind === "class";
|
|
19849
|
+
}
|
|
19850
|
+
function receiverClass(site) {
|
|
19851
|
+
const classScope = site.scope.parent;
|
|
19852
|
+
if (classScope === null || classScope === void 0) {
|
|
19853
|
+
return stop("noDeclaration");
|
|
19854
|
+
}
|
|
19855
|
+
return { kind: "class", file: site.file, node: classScope.node };
|
|
19856
|
+
}
|
|
19857
|
+
var BINDING_RESOLVERS = {
|
|
19858
|
+
functionDef: (binding, file2) => ({
|
|
19859
|
+
kind: "function",
|
|
19860
|
+
target: reachedFunction(file2, binding.node)
|
|
19861
|
+
}),
|
|
19862
|
+
classDef: (binding, file2) => ({ kind: "class", file: file2, node: binding.node }),
|
|
19863
|
+
parameter: () => stop("callerSupplied"),
|
|
19864
|
+
// `import a.b.c` binds `a`, and `import a.b.c as m` binds `m` to `a.b.c`.
|
|
19865
|
+
import: (binding, file2, _scope, ctx) => moduleNamed(
|
|
19866
|
+
file2,
|
|
19867
|
+
binding.localName === binding.module.split(".")[0] ? binding.localName : binding.module,
|
|
19868
|
+
0,
|
|
19869
|
+
ctx
|
|
19870
|
+
),
|
|
19871
|
+
importFrom: (binding, file2, _scope, ctx, visited) => memberOf(
|
|
19872
|
+
moduleNamed(file2, binding.module, binding.relativeLevel, ctx),
|
|
19873
|
+
binding.importedName,
|
|
19874
|
+
ctx,
|
|
19875
|
+
visited
|
|
19876
|
+
),
|
|
19877
|
+
// `run = load` is an alias worth one hop; anything else is a value.
|
|
19878
|
+
assignment: (binding, file2, scope, ctx, visited) => aliasedValue(binding.value, file2, scope, ctx, visited),
|
|
19879
|
+
global: () => stop("unsettledValue"),
|
|
19880
|
+
nonlocal: () => stop("unsettledValue")
|
|
19881
|
+
};
|
|
19882
|
+
function resolveBinding(binding, file2, scope, ctx, visited) {
|
|
19883
|
+
const resolver = BINDING_RESOLVERS[binding.kind];
|
|
19884
|
+
return resolver(binding, file2, scope, ctx, visited);
|
|
19885
|
+
}
|
|
19886
|
+
function aliasedValue(value, file2, scope, ctx, visited) {
|
|
19887
|
+
if (value === null) {
|
|
19888
|
+
return stop("unsettledValue");
|
|
19889
|
+
}
|
|
19890
|
+
if (value.type === "identifier") {
|
|
19891
|
+
const binding = resolveName(scope, value.text);
|
|
19892
|
+
if (binding === null || binding.kind === "assignment") {
|
|
19893
|
+
return stop("unsettledValue");
|
|
19894
|
+
}
|
|
19895
|
+
return resolveBinding(binding, file2, scope, ctx, visited);
|
|
19896
|
+
}
|
|
19897
|
+
if (value.type === "call") {
|
|
19898
|
+
const callee = field(value, "function");
|
|
19899
|
+
const built = callee === null ? stop("unsettledValue") : aliasedValue(callee, file2, scope, ctx, visited);
|
|
19900
|
+
return built.kind === "class" ? built : stop("unsettledValue");
|
|
19901
|
+
}
|
|
19902
|
+
if (value.type === "attribute") {
|
|
19903
|
+
const object2 = field(value, "object");
|
|
19904
|
+
const attribute = field(value, "attribute");
|
|
19905
|
+
if (object2 === null || attribute === null) {
|
|
19906
|
+
return stop("unsettledValue");
|
|
19907
|
+
}
|
|
19908
|
+
return memberOf(
|
|
19909
|
+
aliasedValue(object2, file2, scope, ctx, visited),
|
|
19910
|
+
attribute.text,
|
|
19911
|
+
ctx
|
|
19912
|
+
);
|
|
19913
|
+
}
|
|
19914
|
+
return stop("unsettledValue");
|
|
19915
|
+
}
|
|
19916
|
+
function reachedFunction(file2, node, owner) {
|
|
19917
|
+
const name = field(node, "name")?.text ?? "<anon>";
|
|
19918
|
+
const ownerName = owner === void 0 ? null : field(owner, "name")?.text;
|
|
19919
|
+
return {
|
|
19920
|
+
file: file2,
|
|
19921
|
+
node,
|
|
19922
|
+
name,
|
|
19923
|
+
exportPath: ownerName === null || ownerName === void 0 ? [name] : [ownerName, name]
|
|
19924
|
+
};
|
|
19925
|
+
}
|
|
19926
|
+
function moduleNamed(from, module, relativeLevel, ctx) {
|
|
19927
|
+
const resolution = resolveModule(
|
|
19928
|
+
from.file,
|
|
19929
|
+
{ module, relativeLevel },
|
|
19930
|
+
{ roots: ctx.roots }
|
|
19931
|
+
);
|
|
19932
|
+
if (resolution.status !== "resolved") {
|
|
19933
|
+
return resolution.reason === "external" ? { kind: "package", dotted: module, relativeLevel, from } : stop(MODULE_STOP[resolution.reason]);
|
|
19934
|
+
}
|
|
19935
|
+
const file2 = ctx.filesByPath.get(resolution.file);
|
|
19936
|
+
return file2 === void 0 ? stop("outsideRun") : { kind: "module", file: file2 };
|
|
19937
|
+
}
|
|
19938
|
+
function memberOf(base, name, ctx, visited = /* @__PURE__ */ new Set()) {
|
|
19939
|
+
if (base.kind === "module") {
|
|
19940
|
+
return memberOfModule(base.file, name, ctx, visited);
|
|
19941
|
+
}
|
|
19942
|
+
if (base.kind === "package") {
|
|
19943
|
+
return moduleNamed(
|
|
19944
|
+
base.from,
|
|
19945
|
+
base.dotted === "" ? name : `${base.dotted}.${name}`,
|
|
19946
|
+
base.relativeLevel,
|
|
19947
|
+
ctx
|
|
19948
|
+
);
|
|
19949
|
+
}
|
|
19950
|
+
if (base.kind === "class") {
|
|
19951
|
+
return memberOfClass(base.file, base.node, name, ctx);
|
|
19952
|
+
}
|
|
19953
|
+
if (base.kind === "stop") {
|
|
19954
|
+
return base;
|
|
19955
|
+
}
|
|
19956
|
+
return stop("noDeclaration");
|
|
19957
|
+
}
|
|
19958
|
+
function memberOfClass(file2, classNode, name, ctx) {
|
|
19959
|
+
const classScope = file2.module.scopeFor.get(classNode.id);
|
|
19960
|
+
const binding = classScope?.bindings.get(name);
|
|
19961
|
+
if (binding === void 0) {
|
|
19962
|
+
return stop("noDeclaration");
|
|
19963
|
+
}
|
|
19964
|
+
if (binding.kind === "functionDef") {
|
|
19965
|
+
return {
|
|
19966
|
+
kind: "function",
|
|
19967
|
+
target: reachedFunction(file2, binding.node, classNode)
|
|
19968
|
+
};
|
|
19969
|
+
}
|
|
19970
|
+
return resolveBinding(binding, file2, classScope, ctx, /* @__PURE__ */ new Set());
|
|
19971
|
+
}
|
|
19972
|
+
function memberOfModule(file2, name, ctx, visited) {
|
|
19973
|
+
const key = `${file2.file}#${name}`;
|
|
19974
|
+
if (visited.has(key)) {
|
|
19975
|
+
return stop("unsettledValue");
|
|
19976
|
+
}
|
|
19977
|
+
visited.add(key);
|
|
19978
|
+
const binding = file2.module.moduleScope.bindings.get(name);
|
|
19979
|
+
if (binding !== void 0) {
|
|
19980
|
+
return resolveBinding(binding, file2, file2.module.moduleScope, ctx, visited);
|
|
19981
|
+
}
|
|
19982
|
+
if (file2.file.endsWith("__init__.py")) {
|
|
19983
|
+
const submodule = moduleNamed(file2, name, 1, ctx);
|
|
19984
|
+
if (submodule.kind === "module") {
|
|
19985
|
+
return submodule;
|
|
19986
|
+
}
|
|
19987
|
+
}
|
|
19988
|
+
return throughOpenImports(file2, name, ctx, visited);
|
|
19989
|
+
}
|
|
19990
|
+
function throughOpenImports(file2, name, ctx, visited) {
|
|
19991
|
+
const found = [];
|
|
19992
|
+
for (const spec of file2.module.openImports) {
|
|
19993
|
+
const dots = spec.length - spec.replace(/^\.+/, "").length;
|
|
19994
|
+
const opened = moduleNamed(file2, spec.slice(dots), dots, ctx);
|
|
19995
|
+
if (opened.kind !== "module") {
|
|
19996
|
+
continue;
|
|
19997
|
+
}
|
|
19998
|
+
const member = memberOfModule(opened.file, name, ctx, visited);
|
|
19999
|
+
if (member.kind !== "stop") {
|
|
20000
|
+
found.push(member);
|
|
20001
|
+
}
|
|
20002
|
+
}
|
|
20003
|
+
if (found.length > 1) {
|
|
20004
|
+
return stop("multipleSources");
|
|
20005
|
+
}
|
|
20006
|
+
return found[0] ?? stop("noDeclaration");
|
|
20007
|
+
}
|
|
20008
|
+
|
|
20009
|
+
// src/reach/closure.ts
|
|
20010
|
+
var REACHABLE_RULES = [
|
|
20011
|
+
rule2("reachable", [v2("f")], [lit2("entry", v2("f"))]),
|
|
20012
|
+
rule2(
|
|
20013
|
+
"reachable",
|
|
20014
|
+
[v2("g")],
|
|
20015
|
+
[lit2("reachable", v2("f")), lit2("calls", v2("f"), v2("g"))]
|
|
20016
|
+
)
|
|
20017
|
+
];
|
|
20018
|
+
function reachedFunctions(seeds, options) {
|
|
20019
|
+
const ctx = {
|
|
20020
|
+
filesByPath: new Map(options.files.map((file2) => [file2.file, file2])),
|
|
20021
|
+
roots: options.roots
|
|
20022
|
+
};
|
|
20023
|
+
const db = new Database2();
|
|
20024
|
+
const functionByKey = /* @__PURE__ */ new Map();
|
|
20025
|
+
const seedKeys = /* @__PURE__ */ new Set();
|
|
20026
|
+
const scanned = /* @__PURE__ */ new Set();
|
|
20027
|
+
const targetsByKey = /* @__PURE__ */ new Map();
|
|
20028
|
+
const stopsByKey = /* @__PURE__ */ new Map();
|
|
20029
|
+
const argTargetsByKey = /* @__PURE__ */ new Map();
|
|
20030
|
+
const parameterCallsByKey = /* @__PURE__ */ new Map();
|
|
20031
|
+
const passedPositions = /* @__PURE__ */ new Set();
|
|
20032
|
+
for (const seed of seeds) {
|
|
20033
|
+
seedKeys.add(seed.key);
|
|
20034
|
+
functionByKey.set(seed.key, {
|
|
20035
|
+
file: seed.file,
|
|
20036
|
+
node: seed.node,
|
|
20037
|
+
name: field(seed.node, "name")?.text ?? "<anon>",
|
|
20038
|
+
exportPath: []
|
|
20039
|
+
});
|
|
20040
|
+
db.add("entry", [seed.key]);
|
|
20041
|
+
}
|
|
20042
|
+
for (; ; ) {
|
|
20043
|
+
evaluate3(db, REACHABLE_RULES);
|
|
20044
|
+
const frontier = db.facts("reachable").map(([key]) => String(key)).filter((key) => !scanned.has(key));
|
|
20045
|
+
if (frontier.length === 0) {
|
|
20046
|
+
break;
|
|
20047
|
+
}
|
|
20048
|
+
for (const key of frontier) {
|
|
20049
|
+
scanned.add(key);
|
|
20050
|
+
const source = functionByKey.get(key);
|
|
20051
|
+
if (source === void 0) {
|
|
20052
|
+
continue;
|
|
20053
|
+
}
|
|
20054
|
+
const scan = scanBody(source, ctx);
|
|
20055
|
+
if (scan.stops.length > 0) {
|
|
20056
|
+
stopsByKey.set(key, scan.stops);
|
|
20057
|
+
}
|
|
20058
|
+
targetsByKey.set(key, scan.targets);
|
|
20059
|
+
argTargetsByKey.set(key, scan.argTargets);
|
|
20060
|
+
if (scan.parameterCalls.length > 0) {
|
|
20061
|
+
parameterCallsByKey.set(key, scan.parameterCalls);
|
|
20062
|
+
}
|
|
20063
|
+
for (const position of scan.passedPositions) {
|
|
20064
|
+
passedPositions.add(position);
|
|
20065
|
+
}
|
|
20066
|
+
for (const target of scan.followed) {
|
|
20067
|
+
const calleeKey = keyOf(target);
|
|
20068
|
+
if (!functionByKey.has(calleeKey)) {
|
|
20069
|
+
functionByKey.set(calleeKey, target);
|
|
20070
|
+
}
|
|
20071
|
+
db.add("calls", [key, calleeKey]);
|
|
20072
|
+
}
|
|
20073
|
+
}
|
|
20074
|
+
}
|
|
20075
|
+
const summaries = [];
|
|
20076
|
+
const summariesByKey = /* @__PURE__ */ new Map();
|
|
20077
|
+
for (const [keyAtom] of db.facts("reachable")) {
|
|
20078
|
+
const key = String(keyAtom);
|
|
20079
|
+
const target = functionByKey.get(key);
|
|
20080
|
+
if (seedKeys.has(key) || target === void 0) {
|
|
20081
|
+
continue;
|
|
20082
|
+
}
|
|
20083
|
+
const summary = assembleSummary(libraryUnit(target, options), {
|
|
20084
|
+
gapHandling: options.gapHandling
|
|
20085
|
+
});
|
|
20086
|
+
summary.confidence = { source: "inferred_static", level: "low" };
|
|
20087
|
+
if (options.gapHandling !== "silent") {
|
|
20088
|
+
summary.gaps.push(...(stopsByKey.get(key) ?? []).map(unfollowedCallGap));
|
|
20089
|
+
}
|
|
20090
|
+
placeCalls(summary, targetsByKey.get(key));
|
|
20091
|
+
placeArgTargets(summary, argTargetsByKey.get(key));
|
|
20092
|
+
placeCalleeParameters(summary, parameterCallsByKey.get(key));
|
|
20093
|
+
summariesByKey.set(key, [summary]);
|
|
20094
|
+
summaries.push(summary);
|
|
20095
|
+
}
|
|
20096
|
+
if (options.gapHandling !== "silent") {
|
|
20097
|
+
recordParameterGaps(parameterCallsByKey, summariesByKey, passedPositions);
|
|
20098
|
+
}
|
|
20099
|
+
return {
|
|
20100
|
+
summaries,
|
|
20101
|
+
targetsByKey,
|
|
20102
|
+
stopsByKey,
|
|
20103
|
+
argTargetsByKey,
|
|
20104
|
+
parameterCallsByKey,
|
|
20105
|
+
passedPositions
|
|
20106
|
+
};
|
|
20107
|
+
}
|
|
20108
|
+
function keyOf(target) {
|
|
20109
|
+
return nodeId(target.file.file, target.node);
|
|
20110
|
+
}
|
|
20111
|
+
var EMPTY_SCAN = {
|
|
20112
|
+
followed: [],
|
|
20113
|
+
stops: [],
|
|
20114
|
+
targets: /* @__PURE__ */ new Map(),
|
|
20115
|
+
argTargets: /* @__PURE__ */ new Map(),
|
|
20116
|
+
parameterCalls: [],
|
|
20117
|
+
passedPositions: /* @__PURE__ */ new Set()
|
|
20118
|
+
};
|
|
20119
|
+
function scanBody(source, ctx) {
|
|
20120
|
+
const followed = [];
|
|
20121
|
+
const stops = [];
|
|
20122
|
+
const placements = new TargetPlacements();
|
|
20123
|
+
const parameterCalls = [];
|
|
20124
|
+
const passedPositions = /* @__PURE__ */ new Set();
|
|
20125
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20126
|
+
const parameterCallsSeen = /* @__PURE__ */ new Set();
|
|
20127
|
+
const { file: file2, node } = source;
|
|
20128
|
+
const body = field(node, "body");
|
|
20129
|
+
if (body === null) {
|
|
20130
|
+
return EMPTY_SCAN;
|
|
20131
|
+
}
|
|
20132
|
+
const ownParameters = callParameterNames(node, source.exportPath.length > 1);
|
|
20133
|
+
const recordPassedArgs = (call, callee, calleeKey, where) => {
|
|
20134
|
+
const args = field(call, "arguments");
|
|
20135
|
+
if (args === null) {
|
|
20136
|
+
return;
|
|
20137
|
+
}
|
|
20138
|
+
args.namedChildren.forEach((arg, position) => {
|
|
20139
|
+
if (arg === null || arg.type !== "identifier") {
|
|
20140
|
+
return;
|
|
20141
|
+
}
|
|
20142
|
+
const resolved = resolveNamedFunctionArgument(
|
|
20143
|
+
arg.text,
|
|
20144
|
+
{ file: file2, scope: where.scope, rebound: where.rebound },
|
|
20145
|
+
ctx
|
|
20146
|
+
);
|
|
20147
|
+
if (resolved === null) {
|
|
20148
|
+
return;
|
|
20149
|
+
}
|
|
20150
|
+
const key = keyOf(resolved);
|
|
20151
|
+
if (!seen.has(key)) {
|
|
20152
|
+
seen.add(key);
|
|
20153
|
+
followed.push(resolved);
|
|
20154
|
+
}
|
|
20155
|
+
placements.placeArg(callee, position, {
|
|
20156
|
+
file: resolved.file.displayPath,
|
|
20157
|
+
span: spanOf(resolved.node)
|
|
20158
|
+
});
|
|
20159
|
+
if (calleeKey !== null) {
|
|
20160
|
+
passedPositions.add(`${calleeKey}#${position}`);
|
|
20161
|
+
}
|
|
20162
|
+
});
|
|
20163
|
+
};
|
|
20164
|
+
const record2 = (call, where) => {
|
|
20165
|
+
const callee = calleeText(call);
|
|
20166
|
+
const outcome = resolveCallee2(
|
|
20167
|
+
call,
|
|
20168
|
+
{ file: file2, scope: where.scope, rebound: where.rebound },
|
|
20169
|
+
ctx
|
|
20170
|
+
);
|
|
20171
|
+
const placed = outcome.kind === "followed" ? {
|
|
20172
|
+
file: outcome.target.file.displayPath,
|
|
20173
|
+
span: spanOf(outcome.target.node)
|
|
20174
|
+
} : outcome.reason === "noDeclaration" ? null : { file: file2.displayPath, span: spanOf(call) };
|
|
20175
|
+
placements.place(callee, placed);
|
|
20176
|
+
recordPassedArgs(
|
|
20177
|
+
call,
|
|
20178
|
+
callee,
|
|
20179
|
+
outcome.kind === "followed" ? keyOf(outcome.target) : null,
|
|
20180
|
+
where
|
|
20181
|
+
);
|
|
20182
|
+
if (outcome.kind === "stopped") {
|
|
20183
|
+
const stopKey = `${outcome.reason}:${callee}`;
|
|
20184
|
+
if (!seen.has(stopKey) && worthRecording(outcome.reason)) {
|
|
20185
|
+
seen.add(stopKey);
|
|
20186
|
+
stops.push({ callee, reason: outcome.reason });
|
|
20187
|
+
}
|
|
20188
|
+
if (outcome.reason === "callerSupplied" && !parameterCallsSeen.has(callee)) {
|
|
20189
|
+
const parameterIndex = ownParameters.indexOf(callee);
|
|
20190
|
+
if (parameterIndex !== -1) {
|
|
20191
|
+
parameterCallsSeen.add(callee);
|
|
20192
|
+
parameterCalls.push({ callee, parameterIndex });
|
|
20193
|
+
}
|
|
20194
|
+
}
|
|
20195
|
+
return;
|
|
20196
|
+
}
|
|
20197
|
+
const key = keyOf(outcome.target);
|
|
20198
|
+
if (!seen.has(key)) {
|
|
20199
|
+
seen.add(key);
|
|
20200
|
+
followed.push(outcome.target);
|
|
20201
|
+
}
|
|
20202
|
+
};
|
|
20203
|
+
walkDescendants3(
|
|
20204
|
+
body,
|
|
20205
|
+
{ scope: scopeAt(file2, node), rebound: reboundNames(body) },
|
|
20206
|
+
{
|
|
20207
|
+
at: (child, where) => {
|
|
20208
|
+
if (child.type === "call") {
|
|
20209
|
+
record2(child, where);
|
|
20210
|
+
}
|
|
20211
|
+
},
|
|
20212
|
+
into: (child, where) => {
|
|
20213
|
+
if (child.type === "function_definition") {
|
|
20214
|
+
return SKIP_CHILDREN2;
|
|
20215
|
+
}
|
|
20216
|
+
if (child.type === "lambda") {
|
|
20217
|
+
return {
|
|
20218
|
+
scope: where.scope,
|
|
20219
|
+
rebound: /* @__PURE__ */ new Set([...where.rebound, ...lambdaParameters(child)])
|
|
20220
|
+
};
|
|
20221
|
+
}
|
|
20222
|
+
return {
|
|
20223
|
+
scope: file2.module.scopeFor.get(child.id) ?? where.scope,
|
|
20224
|
+
rebound: where.rebound
|
|
20225
|
+
};
|
|
20226
|
+
}
|
|
20227
|
+
}
|
|
20228
|
+
);
|
|
20229
|
+
return {
|
|
20230
|
+
followed,
|
|
20231
|
+
stops,
|
|
20232
|
+
targets: placements.targets,
|
|
20233
|
+
argTargets: placements.argTargets,
|
|
20234
|
+
parameterCalls,
|
|
20235
|
+
passedPositions
|
|
20236
|
+
};
|
|
20237
|
+
}
|
|
20238
|
+
function scopeAt(file2, node) {
|
|
20239
|
+
let current = node;
|
|
20240
|
+
while (current !== null) {
|
|
20241
|
+
const scope = file2.module.scopeFor.get(current.id);
|
|
20242
|
+
if (scope !== void 0) {
|
|
20243
|
+
return scope;
|
|
20244
|
+
}
|
|
20245
|
+
current = current.parent;
|
|
20246
|
+
}
|
|
20247
|
+
return file2.module.moduleScope;
|
|
20248
|
+
}
|
|
20249
|
+
var REBINDING_FIELDS = {
|
|
20250
|
+
assignment: "left",
|
|
20251
|
+
augmented_assignment: "left",
|
|
20252
|
+
named_expression: "name",
|
|
20253
|
+
for_statement: "left",
|
|
20254
|
+
for_in_clause: "left",
|
|
20255
|
+
as_pattern: "alias"
|
|
20256
|
+
};
|
|
20257
|
+
function reboundNames(body) {
|
|
20258
|
+
const names = /* @__PURE__ */ new Set();
|
|
20259
|
+
walkDescendants3(body, null, {
|
|
20260
|
+
at: (node) => {
|
|
20261
|
+
const target = REBINDING_FIELDS[node.type];
|
|
20262
|
+
if (target === void 0 || boundByBinder(node, body)) {
|
|
20263
|
+
return;
|
|
20264
|
+
}
|
|
20265
|
+
const written = field(node, target);
|
|
20266
|
+
if (written !== null) {
|
|
20267
|
+
for (const name of identifiersUnder(written)) {
|
|
20268
|
+
names.add(name);
|
|
20269
|
+
}
|
|
20270
|
+
}
|
|
20271
|
+
},
|
|
20272
|
+
into: (node) => node.type === "function_definition" ? SKIP_CHILDREN2 : null
|
|
20273
|
+
});
|
|
20274
|
+
return names;
|
|
20275
|
+
}
|
|
20276
|
+
function boundByBinder(node, body) {
|
|
20277
|
+
if (node.type !== "assignment") {
|
|
20278
|
+
return false;
|
|
20279
|
+
}
|
|
20280
|
+
const statement = node.parent;
|
|
20281
|
+
return field(node, "left")?.type === "identifier" && statement?.type === "expression_statement" && statement.parent?.id === body.id;
|
|
20282
|
+
}
|
|
20283
|
+
function identifiersUnder(node, found = []) {
|
|
20284
|
+
if (node.type === "identifier") {
|
|
20285
|
+
found.push(node.text);
|
|
20286
|
+
return found;
|
|
20287
|
+
}
|
|
20288
|
+
for (const child of node.namedChildren) {
|
|
20289
|
+
if (child !== null && child.type !== "attribute" && child.type !== "subscript") {
|
|
20290
|
+
identifiersUnder(child, found);
|
|
20291
|
+
}
|
|
20292
|
+
}
|
|
20293
|
+
return found;
|
|
20294
|
+
}
|
|
20295
|
+
function lambdaParameters(lambda) {
|
|
20296
|
+
const parameters = field(lambda, "parameters");
|
|
20297
|
+
return parameters === null ? [] : identifiersUnder(parameters);
|
|
20298
|
+
}
|
|
20299
|
+
function libraryUnit(target, options) {
|
|
20300
|
+
const { file: file2, node, name, exportPath } = target;
|
|
20301
|
+
const body = field(node, "body");
|
|
20302
|
+
const extra = recognizedBodyEffects(
|
|
20303
|
+
node,
|
|
20304
|
+
file2.module,
|
|
20305
|
+
options.storageFor(file2)
|
|
20306
|
+
);
|
|
20307
|
+
const range = rangeOf(node);
|
|
20308
|
+
return {
|
|
20309
|
+
identity: {
|
|
20310
|
+
name,
|
|
20311
|
+
nameKind: "binding",
|
|
20312
|
+
kind: "library",
|
|
20313
|
+
file: file2.displayPath,
|
|
20314
|
+
range,
|
|
20315
|
+
span: spanOf(node),
|
|
20316
|
+
exportName: exportPath[0] ?? name,
|
|
20317
|
+
exportPath
|
|
20318
|
+
},
|
|
20319
|
+
boundaryBinding: functionCallBinding({
|
|
20320
|
+
transport: "in-process",
|
|
20321
|
+
recognition: "reachable"
|
|
20322
|
+
}),
|
|
20323
|
+
parameters: positionalParameters(node, exportPath.length > 1),
|
|
20324
|
+
branches: [
|
|
20325
|
+
{
|
|
20326
|
+
conditions: [],
|
|
20327
|
+
terminal: {
|
|
20328
|
+
kind: "return",
|
|
20329
|
+
statusCode: null,
|
|
20330
|
+
body: null,
|
|
20331
|
+
exceptionType: null,
|
|
20332
|
+
message: null,
|
|
20333
|
+
component: null,
|
|
20334
|
+
renderTree: null,
|
|
20335
|
+
delegateTarget: null,
|
|
20336
|
+
emitEvent: null,
|
|
20337
|
+
location: range
|
|
20338
|
+
},
|
|
20339
|
+
effects: invocationEffects(node),
|
|
20340
|
+
...extra.length === 0 ? {} : { extraEffects: extra },
|
|
20341
|
+
location: range,
|
|
20342
|
+
isDefault: true
|
|
20343
|
+
}
|
|
20344
|
+
],
|
|
20345
|
+
bodyContent: body === null ? "absent" : bodyContentOf(body),
|
|
20346
|
+
dependencyCalls: [],
|
|
20347
|
+
declaredContract: null
|
|
20348
|
+
};
|
|
20349
|
+
}
|
|
20350
|
+
function positionalParameters(node, isMethod) {
|
|
20351
|
+
const parameters = field(node, "parameters");
|
|
20352
|
+
if (parameters === null) {
|
|
20353
|
+
return [];
|
|
20354
|
+
}
|
|
20355
|
+
const out = [];
|
|
20356
|
+
let position = 0;
|
|
20357
|
+
for (const param of parameters.namedChildren) {
|
|
20358
|
+
if (param === null) {
|
|
20359
|
+
continue;
|
|
20360
|
+
}
|
|
20361
|
+
const name = parameterName3(param);
|
|
20362
|
+
if (name === null) {
|
|
20363
|
+
continue;
|
|
20364
|
+
}
|
|
20365
|
+
if (isMethod && position === 0 && (name === "self" || name === "cls")) {
|
|
20366
|
+
position += 1;
|
|
20367
|
+
continue;
|
|
20368
|
+
}
|
|
20369
|
+
out.push({ name, position, role: name, typeText: null });
|
|
20370
|
+
position += 1;
|
|
20371
|
+
}
|
|
20372
|
+
return out;
|
|
20373
|
+
}
|
|
20374
|
+
function callParameterNames(node, isMethod) {
|
|
20375
|
+
const parameters = field(node, "parameters");
|
|
20376
|
+
if (parameters === null) {
|
|
20377
|
+
return [];
|
|
20378
|
+
}
|
|
20379
|
+
const names = [];
|
|
20380
|
+
for (const param of parameters.namedChildren) {
|
|
20381
|
+
if (param === null) {
|
|
20382
|
+
continue;
|
|
20383
|
+
}
|
|
20384
|
+
const name = parameterName3(param);
|
|
20385
|
+
if (name === null) {
|
|
20386
|
+
continue;
|
|
20387
|
+
}
|
|
20388
|
+
if (isMethod && names.length === 0 && (name === "self" || name === "cls")) {
|
|
20389
|
+
continue;
|
|
20390
|
+
}
|
|
20391
|
+
names.push(name);
|
|
20392
|
+
}
|
|
20393
|
+
return names;
|
|
20394
|
+
}
|
|
20395
|
+
function parameterName3(param) {
|
|
20396
|
+
if (param.type === "identifier") {
|
|
20397
|
+
return param.text;
|
|
20398
|
+
}
|
|
20399
|
+
const named = field(param, "name");
|
|
20400
|
+
if (named !== null && named.type === "identifier") {
|
|
20401
|
+
return named.text;
|
|
20402
|
+
}
|
|
20403
|
+
return identifiersUnder(param)[0] ?? null;
|
|
20404
|
+
}
|
|
19273
20405
|
|
|
19274
20406
|
// src/routers.ts
|
|
19275
20407
|
var UNSTATED_PREFIX = { kind: "unstated" };
|
|
@@ -19544,6 +20676,9 @@ function buildPatternIndex(files, importModule, composition, resolverOptions) {
|
|
|
19544
20676
|
for (const bound of files) {
|
|
19545
20677
|
collectConstructions(bound, importModule, composition, index);
|
|
19546
20678
|
}
|
|
20679
|
+
for (const bound of files) {
|
|
20680
|
+
collectReturnedConstructions(bound, importModule, composition, index);
|
|
20681
|
+
}
|
|
19547
20682
|
if (objectPrefix?.carrier !== void 0) {
|
|
19548
20683
|
const carrier = objectPrefix.carrier;
|
|
19549
20684
|
for (const bound of files) {
|
|
@@ -19719,6 +20854,37 @@ function collectConstructions(bound, importModule, composition, index) {
|
|
|
19719
20854
|
);
|
|
19720
20855
|
}
|
|
19721
20856
|
}
|
|
20857
|
+
function collectReturnedConstructions(bound, importModule, composition, index) {
|
|
20858
|
+
for (const stmt of bodyStatements(bound.root)) {
|
|
20859
|
+
const definition = stripDecorators(stmt).definition;
|
|
20860
|
+
if (definition.type !== "function_definition") {
|
|
20861
|
+
continue;
|
|
20862
|
+
}
|
|
20863
|
+
const scope = bound.module.scopeFor.get(definition.id);
|
|
20864
|
+
for (const inner of nestedStatements(definition)) {
|
|
20865
|
+
if (inner.type !== "return_statement" || scope === void 0) {
|
|
20866
|
+
continue;
|
|
20867
|
+
}
|
|
20868
|
+
const returned = inner.namedChildren[0];
|
|
20869
|
+
if (returned?.type !== "call") {
|
|
20870
|
+
continue;
|
|
20871
|
+
}
|
|
20872
|
+
const callee = field(returned, "function");
|
|
20873
|
+
const constructorName = callee === null ? null : importedConstructorName(callee, scope, importModule);
|
|
20874
|
+
if (constructorName !== composition.routerConstructorName) {
|
|
20875
|
+
continue;
|
|
20876
|
+
}
|
|
20877
|
+
const { keywordArgs } = readCallArguments(field(returned, "arguments"));
|
|
20878
|
+
const valueKey2 = nodeId(bound.file, returned);
|
|
20879
|
+
index.byValueKey.set(valueKey2, {
|
|
20880
|
+
constructorName,
|
|
20881
|
+
valueKey: valueKey2,
|
|
20882
|
+
prefix: constructorPrefix(keywordArgs, composition, scope),
|
|
20883
|
+
reassigned: false
|
|
20884
|
+
});
|
|
20885
|
+
}
|
|
20886
|
+
}
|
|
20887
|
+
}
|
|
19722
20888
|
function collectCarrierConstructions(carrier, composition, scan) {
|
|
19723
20889
|
walkStatements(
|
|
19724
20890
|
bodyStatements(scan.bound.root),
|
|
@@ -19777,6 +20943,21 @@ function constructionNamed(name, scope, scan, constructions = scan.index.constru
|
|
|
19777
20943
|
};
|
|
19778
20944
|
return resolvers[binding.kind]?.() ?? null;
|
|
19779
20945
|
}
|
|
20946
|
+
function constructionThroughFacts(name, scope, scan) {
|
|
20947
|
+
const facts = scan.index.facts;
|
|
20948
|
+
const binding = resolveName(scope, name);
|
|
20949
|
+
if (facts === void 0 || binding === null) {
|
|
20950
|
+
return null;
|
|
20951
|
+
}
|
|
20952
|
+
if (binding.kind !== "assignment" && binding.kind !== "importFrom") {
|
|
20953
|
+
return null;
|
|
20954
|
+
}
|
|
20955
|
+
const nameKey = `${scan.bound.file}#${name}`;
|
|
20956
|
+
const constructionKey = subjectConstructions(facts, [nameKey]).get(
|
|
20957
|
+
nameKey
|
|
20958
|
+
)?.constructionKey;
|
|
20959
|
+
return constructionKey === void 0 ? null : scan.index.byValueKey.get(constructionKey) ?? null;
|
|
20960
|
+
}
|
|
19780
20961
|
var MOUNT_STATE_BY_EFFECT = {
|
|
19781
20962
|
prefixes: (statedPrefix, site) => ({
|
|
19782
20963
|
kind: "mounted",
|
|
@@ -19946,10 +21127,10 @@ function constructionsReturnedBy(target, index) {
|
|
|
19946
21127
|
return { kind: "unread" };
|
|
19947
21128
|
}
|
|
19948
21129
|
const contained = containedValues(facts, returned);
|
|
19949
|
-
|
|
21130
|
+
const settled = subjectConstructions(facts, contained);
|
|
19950
21131
|
const found = [];
|
|
19951
21132
|
for (const value of contained) {
|
|
19952
|
-
const resolved =
|
|
21133
|
+
const resolved = settled.get(value)?.constructionKey ?? value;
|
|
19953
21134
|
const construction = index.byValueKey.get(resolved);
|
|
19954
21135
|
if (construction === void 0) {
|
|
19955
21136
|
return {
|
|
@@ -19964,10 +21145,6 @@ function constructionsReturnedBy(target, index) {
|
|
|
19964
21145
|
}
|
|
19965
21146
|
return { kind: "settled", found };
|
|
19966
21147
|
}
|
|
19967
|
-
function resolvedValueKey(facts, value) {
|
|
19968
|
-
const row = facts.facts("wantedIsWrittenAs").find((entry) => String(entry[0]) === value);
|
|
19969
|
-
return row === void 0 ? value : String(row[1]);
|
|
19970
|
-
}
|
|
19971
21148
|
function mountedConstructions(arg, position, scan) {
|
|
19972
21149
|
if (arg?.kind === "attribute") {
|
|
19973
21150
|
const construction = constructionThroughModule(arg, position.scope, scan);
|
|
@@ -19977,7 +21154,7 @@ function mountedConstructions(arg, position, scan) {
|
|
|
19977
21154
|
return [];
|
|
19978
21155
|
}
|
|
19979
21156
|
const named = (name) => {
|
|
19980
|
-
const construction = constructionNamed(name, position.scope, scan);
|
|
21157
|
+
const construction = constructionNamed(name, position.scope, scan) ?? constructionThroughFacts(name, position.scope, scan);
|
|
19981
21158
|
return construction === null ? [] : [construction];
|
|
19982
21159
|
};
|
|
19983
21160
|
const target = position.loopBindings.get(arg.name);
|
|
@@ -20364,36 +21541,69 @@ function carrierPrefix(carrier, constructorArg, objectName, scope, scan) {
|
|
|
20364
21541
|
return readings[construction.prefix.kind]();
|
|
20365
21542
|
}
|
|
20366
21543
|
|
|
21544
|
+
// src/version.ts
|
|
21545
|
+
import { createAdapterStamp } from "@suss/extractor";
|
|
21546
|
+
var ADAPTER_VERSION = "0.1.0";
|
|
21547
|
+
var adapterStamp = createAdapterStamp({
|
|
21548
|
+
moduleUrl: import.meta.url,
|
|
21549
|
+
version: ADAPTER_VERSION
|
|
21550
|
+
});
|
|
21551
|
+
|
|
20367
21552
|
// src/project.ts
|
|
20368
|
-
function reportUnresolvedProjectModules(options) {
|
|
21553
|
+
function reportUnresolvedProjectModules(options, db) {
|
|
21554
|
+
const imported = new Set(db.facts("pyImport").map((row) => String(row[1])));
|
|
20369
21555
|
for (const pack of options.packs) {
|
|
20370
21556
|
for (const module of pack.projectModules ?? []) {
|
|
20371
|
-
|
|
20372
|
-
if (resolved.status === "resolved") {
|
|
21557
|
+
if (imported.has(module)) {
|
|
20373
21558
|
continue;
|
|
20374
21559
|
}
|
|
20375
21560
|
process.stderr.write(
|
|
20376
|
-
`[suss] ${pack.name}:
|
|
21561
|
+
`[suss] ${pack.name}: no file under ${options.roots.join(", ")} imports ${module}, so the stub for it changes nothing.
|
|
20377
21562
|
`
|
|
20378
21563
|
);
|
|
20379
21564
|
}
|
|
20380
21565
|
}
|
|
20381
21566
|
}
|
|
20382
21567
|
async function extractPythonProject(options) {
|
|
20383
|
-
const
|
|
21568
|
+
const timer = options.onTiming !== void 0 ? createTimer() : noopTimer();
|
|
21569
|
+
const cacheDir = adapterStamp.declineWhenRunFromSource(
|
|
21570
|
+
options.cacheDir === null ? null : options.cacheDir ?? (options.projectRoot !== void 0 ? path3.join(options.projectRoot, ".suss", "cache") : null)
|
|
21571
|
+
);
|
|
21572
|
+
const cache = createCacheLayer(cacheDir);
|
|
21573
|
+
const packsDigest = adapterStamp.packsDigest(
|
|
21574
|
+
options.packs.map(
|
|
21575
|
+
(pack) => pack.version !== void 0 ? { name: pack.name, version: pack.version } : { name: pack.name }
|
|
21576
|
+
)
|
|
21577
|
+
);
|
|
21578
|
+
const cacheInput = {
|
|
21579
|
+
files: cacheDir === null ? [] : options.files,
|
|
21580
|
+
adapterPacksDigest: cacheDir === null ? packsDigest : runDigest(packsDigest, options.packs, options.files)
|
|
21581
|
+
};
|
|
21582
|
+
const lookup = await timer.timeAsync(
|
|
21583
|
+
"cache.lookup",
|
|
21584
|
+
() => cache.lookup(cacheInput)
|
|
21585
|
+
);
|
|
21586
|
+
options.onCacheDiagnostic?.(lookup.diagnostic);
|
|
21587
|
+
if (lookup.kind === "hit") {
|
|
21588
|
+
options.onTiming?.(timer.report());
|
|
21589
|
+
return { summaries: lookup.summaries, facts: new Database3() };
|
|
21590
|
+
}
|
|
21591
|
+
const db = new Database3();
|
|
20384
21592
|
const summaries = [];
|
|
20385
21593
|
const gapHandling = options.gapHandling ?? "permissive";
|
|
21594
|
+
const tallies = createPackTallies(options.packs);
|
|
20386
21595
|
const displayPathOf = (file2) => options.workspaceRoot !== void 0 ? path3.relative(options.workspaceRoot, file2) : file2;
|
|
20387
|
-
reportUnresolvedProjectModules(options);
|
|
20388
21596
|
const bound = [];
|
|
20389
21597
|
for (const file2 of options.files) {
|
|
20390
|
-
|
|
20391
|
-
|
|
20392
|
-
|
|
20393
|
-
|
|
20394
|
-
|
|
20395
|
-
|
|
20396
|
-
|
|
21598
|
+
await timer.timeAsync("parse", async () => {
|
|
21599
|
+
const source = fs3.readFileSync(file2, "utf8");
|
|
21600
|
+
const tree = await parsePython(source);
|
|
21601
|
+
bound.push({
|
|
21602
|
+
file: file2,
|
|
21603
|
+
displayPath: displayPathOf(file2),
|
|
21604
|
+
root: tree.rootNode,
|
|
21605
|
+
module: bindModule(tree.rootNode)
|
|
21606
|
+
});
|
|
20397
21607
|
});
|
|
20398
21608
|
}
|
|
20399
21609
|
const mountsRouters = options.packs.some(
|
|
@@ -20404,81 +21614,172 @@ async function extractPythonProject(options) {
|
|
|
20404
21614
|
const discovers = options.packs.some((pack) => pack.discovery.length > 0);
|
|
20405
21615
|
const needsValues = discovers || mountsRouters || storagePatterns.length > 0;
|
|
20406
21616
|
const definitions = /* @__PURE__ */ new Map();
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
|
|
20410
|
-
|
|
20411
|
-
|
|
20412
|
-
|
|
21617
|
+
timer.time("discover", () => {
|
|
21618
|
+
for (const { file: file2, root, module: moduleBinding } of bound) {
|
|
21619
|
+
emitModuleImportFacts(db, file2, moduleBinding, { roots: options.roots });
|
|
21620
|
+
if (needsValues) {
|
|
21621
|
+
emitValueFacts(db, file2, root);
|
|
21622
|
+
}
|
|
20413
21623
|
indexDefinitions(definitions, file2, root);
|
|
20414
21624
|
}
|
|
20415
|
-
}
|
|
20416
|
-
const couldMatch = methodsDeclaredNear(db, storagePatterns, definitions);
|
|
20417
|
-
const startsAQuery = /* @__PURE__ */ new Set([
|
|
20418
|
-
...couldMatch,
|
|
20419
|
-
...storagePatterns.flatMap((pattern) => pattern.queryFunctions ?? [])
|
|
20420
|
-
]);
|
|
20421
|
-
const leadsToStorage = functionsReachingStorage(
|
|
20422
|
-
db,
|
|
20423
|
-
definitions,
|
|
20424
|
-
startsAQuery
|
|
20425
|
-
);
|
|
20426
|
-
const routerIndex = buildRouterIndex(bound, options.packs, {
|
|
20427
|
-
roots: options.roots,
|
|
20428
|
-
...mountsRouters ? { facts: db } : {}
|
|
20429
21625
|
});
|
|
20430
|
-
|
|
21626
|
+
reportUnresolvedProjectModules(options, db);
|
|
21627
|
+
const couldMatch = timer.time(
|
|
21628
|
+
"discover",
|
|
21629
|
+
() => methodsDeclaredNear(db, storagePatterns, definitions)
|
|
21630
|
+
);
|
|
21631
|
+
const routerIndex = timer.time(
|
|
21632
|
+
"discover",
|
|
21633
|
+
() => buildRouterIndex(bound, options.packs, {
|
|
21634
|
+
roots: options.roots,
|
|
21635
|
+
...mountsRouters ? { facts: db } : {}
|
|
21636
|
+
})
|
|
21637
|
+
);
|
|
21638
|
+
const storageFor = (file2) => storagePatterns.length > 0 || rawSqlPatterns.length > 0 ? {
|
|
21639
|
+
facts: db,
|
|
21640
|
+
factsPath: file2.file,
|
|
21641
|
+
patterns: storagePatterns,
|
|
21642
|
+
definitionAt: (key) => definitions.get(key),
|
|
21643
|
+
couldMatch,
|
|
21644
|
+
rawSql: rawSqlPatterns
|
|
21645
|
+
} : void 0;
|
|
21646
|
+
const seeds = [];
|
|
21647
|
+
const summariesBySeed = /* @__PURE__ */ new Map();
|
|
21648
|
+
for (const boundFile of bound) {
|
|
21649
|
+
const { file: file2, root, module: moduleBinding } = boundFile;
|
|
20431
21650
|
const displayPath = displayPathOf(file2);
|
|
20432
|
-
const
|
|
20433
|
-
|
|
20434
|
-
|
|
20435
|
-
|
|
20436
|
-
|
|
20437
|
-
|
|
20438
|
-
|
|
20439
|
-
|
|
20440
|
-
|
|
20441
|
-
|
|
20442
|
-
|
|
20443
|
-
|
|
20444
|
-
|
|
20445
|
-
couldMatch,
|
|
20446
|
-
leadsToStorage,
|
|
20447
|
-
rawSql: rawSqlPatterns
|
|
20448
|
-
}
|
|
20449
|
-
} : {}
|
|
20450
|
-
});
|
|
21651
|
+
const storage = storageFor(boundFile);
|
|
21652
|
+
const rawUnits = timer.time(
|
|
21653
|
+
"discover",
|
|
21654
|
+
() => discoverUnits(root, moduleBinding, {
|
|
21655
|
+
packs: options.packs,
|
|
21656
|
+
filePath: displayPath,
|
|
21657
|
+
absoluteFile: file2,
|
|
21658
|
+
routerIndex,
|
|
21659
|
+
gapHandling,
|
|
21660
|
+
...needsValues ? { facts: db } : {},
|
|
21661
|
+
...storage === void 0 ? {} : { storage }
|
|
21662
|
+
})
|
|
21663
|
+
);
|
|
20451
21664
|
for (const raw of rawUnits) {
|
|
20452
|
-
const summary =
|
|
21665
|
+
const summary = timer.time(
|
|
21666
|
+
"summarize",
|
|
21667
|
+
() => assembleSummary2(raw, { gapHandling })
|
|
21668
|
+
);
|
|
20453
21669
|
summary.confidence = { source: "inferred_static", level: "low" };
|
|
20454
21670
|
summaries.push(summary);
|
|
20455
21671
|
emitEntryFact(db, file2, raw.identity.range, raw.identity.name);
|
|
21672
|
+
tallyUnit(tallies, raw.boundaryBinding?.recognition);
|
|
21673
|
+
const span = raw.identity.span;
|
|
21674
|
+
const key = span === void 0 ? null : `${file2}:${span.start}-${span.end}`;
|
|
21675
|
+
const node = key === null ? void 0 : definitions.get(key);
|
|
21676
|
+
if (key === null || node === void 0) {
|
|
21677
|
+
continue;
|
|
21678
|
+
}
|
|
21679
|
+
const sharing = summariesBySeed.get(key);
|
|
21680
|
+
if (sharing === void 0) {
|
|
21681
|
+
seeds.push({ key, file: boundFile, node });
|
|
21682
|
+
summariesBySeed.set(key, [summary]);
|
|
21683
|
+
} else {
|
|
21684
|
+
sharing.push(summary);
|
|
21685
|
+
}
|
|
20456
21686
|
}
|
|
21687
|
+
const loadTimeReads = timer.time(
|
|
21688
|
+
"discover",
|
|
21689
|
+
() => envReadEffects(root, moduleBinding)
|
|
21690
|
+
);
|
|
21691
|
+
if (loadTimeReads.length > 0) {
|
|
21692
|
+
const summary = timer.time(
|
|
21693
|
+
"summarize",
|
|
21694
|
+
() => assembleSummary2(
|
|
21695
|
+
moduleInitStructure({
|
|
21696
|
+
name: path3.basename(displayPath),
|
|
21697
|
+
file: displayPath,
|
|
21698
|
+
range: rangeOf(root),
|
|
21699
|
+
effects: loadTimeReads
|
|
21700
|
+
}),
|
|
21701
|
+
{ gapHandling }
|
|
21702
|
+
)
|
|
21703
|
+
);
|
|
21704
|
+
summary.confidence = { source: "inferred_static", level: "low" };
|
|
21705
|
+
summaries.push(summary);
|
|
21706
|
+
}
|
|
21707
|
+
}
|
|
21708
|
+
const reached = timer.time(
|
|
21709
|
+
"summarize",
|
|
21710
|
+
() => reachedFunctions(seeds, {
|
|
21711
|
+
files: bound,
|
|
21712
|
+
roots: options.roots,
|
|
21713
|
+
gapHandling,
|
|
21714
|
+
storageFor
|
|
21715
|
+
})
|
|
21716
|
+
);
|
|
21717
|
+
for (const [key, owners] of summariesBySeed) {
|
|
21718
|
+
for (const summary of owners) {
|
|
21719
|
+
if (gapHandling !== "silent") {
|
|
21720
|
+
summary.gaps.push(
|
|
21721
|
+
...(reached.stopsByKey.get(key) ?? []).map(unfollowedCallGap2)
|
|
21722
|
+
);
|
|
21723
|
+
}
|
|
21724
|
+
placeCalls2(summary, reached.targetsByKey.get(key));
|
|
21725
|
+
placeArgTargets2(summary, reached.argTargetsByKey.get(key));
|
|
21726
|
+
placeCalleeParameters2(summary, reached.parameterCallsByKey.get(key));
|
|
21727
|
+
}
|
|
21728
|
+
}
|
|
21729
|
+
if (gapHandling !== "silent") {
|
|
21730
|
+
recordParameterGaps2(
|
|
21731
|
+
reached.parameterCallsByKey,
|
|
21732
|
+
summariesBySeed,
|
|
21733
|
+
reached.passedPositions
|
|
21734
|
+
);
|
|
21735
|
+
}
|
|
21736
|
+
summaries.push(...reached.summaries);
|
|
21737
|
+
const resolvedImports = resolvedImportsOf(db, displayPathOf);
|
|
21738
|
+
stampModuleImports(summaries, (file2) => resolvedImports.get(file2) ?? []);
|
|
21739
|
+
const idRoot = options.projectRoot ?? options.workspaceRoot;
|
|
21740
|
+
for (const summary of summaries) {
|
|
21741
|
+
const absoluteFile = options.workspaceRoot === void 0 ? summary.location.file : path3.resolve(options.workspaceRoot, summary.location.file);
|
|
21742
|
+
summary.identity.id = summaryIdFromParts({
|
|
21743
|
+
workspace: void 0,
|
|
21744
|
+
file: idRoot === void 0 ? absoluteFile : path3.relative(idRoot, absoluteFile),
|
|
21745
|
+
name: summary.identity.name,
|
|
21746
|
+
exportPath: summary.identity.exportPath
|
|
21747
|
+
});
|
|
20457
21748
|
}
|
|
20458
|
-
|
|
21749
|
+
disambiguateSummaryIds(summaries);
|
|
21750
|
+
linkCallsToSummaries(summaries);
|
|
21751
|
+
await timer.timeAsync("cache.write", async () => {
|
|
21752
|
+
if (cacheDir === null || summaries.length === 0) {
|
|
21753
|
+
return;
|
|
21754
|
+
}
|
|
21755
|
+
try {
|
|
21756
|
+
await cache.write(cacheInput, summaries);
|
|
21757
|
+
} catch {
|
|
21758
|
+
}
|
|
21759
|
+
});
|
|
21760
|
+
options.onExtractionReport?.(
|
|
21761
|
+
buildPythonExtractionReport({
|
|
21762
|
+
packs: options.packs,
|
|
21763
|
+
tallies,
|
|
21764
|
+
filesWalked: options.files.length,
|
|
21765
|
+
summaries
|
|
21766
|
+
})
|
|
21767
|
+
);
|
|
21768
|
+
options.onTiming?.(timer.report());
|
|
20459
21769
|
return { summaries, facts: db };
|
|
20460
21770
|
}
|
|
20461
|
-
function
|
|
21771
|
+
function resolvedImportsOf(db, displayPathOf) {
|
|
20462
21772
|
const importsByFile = /* @__PURE__ */ new Map();
|
|
20463
21773
|
for (const [from, , to] of db.facts("pyImportResolved")) {
|
|
20464
21774
|
if (typeof from !== "string" || typeof to !== "string") {
|
|
20465
21775
|
continue;
|
|
20466
21776
|
}
|
|
20467
21777
|
const key = displayPathOf(from);
|
|
20468
|
-
const seen = importsByFile.get(key) ??
|
|
20469
|
-
seen.
|
|
21778
|
+
const seen = importsByFile.get(key) ?? [];
|
|
21779
|
+
seen.push(displayPathOf(to));
|
|
20470
21780
|
importsByFile.set(key, seen);
|
|
20471
21781
|
}
|
|
20472
|
-
|
|
20473
|
-
const imports = importsByFile.get(summary.location.file);
|
|
20474
|
-
if (imports === void 0 || imports.size === 0) {
|
|
20475
|
-
continue;
|
|
20476
|
-
}
|
|
20477
|
-
summary.metadata = {
|
|
20478
|
-
...summary.metadata ?? {},
|
|
20479
|
-
moduleImports: [...imports].sort()
|
|
20480
|
-
};
|
|
20481
|
-
}
|
|
21782
|
+
return importsByFile;
|
|
20482
21783
|
}
|
|
20483
21784
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
20484
21785
|
"__pycache__",
|
|
@@ -20533,54 +21834,271 @@ function methodsDeclaredNear(db, patterns, definitions) {
|
|
|
20533
21834
|
}
|
|
20534
21835
|
return found;
|
|
20535
21836
|
}
|
|
20536
|
-
|
|
20537
|
-
|
|
20538
|
-
|
|
20539
|
-
|
|
21837
|
+
|
|
21838
|
+
// src/facts/why.ts
|
|
21839
|
+
var FUNCTION_TYPES2 = /* @__PURE__ */ new Set(["function_definition", "lambda"]);
|
|
21840
|
+
var IMPORT_TYPES = /* @__PURE__ */ new Set(["import_statement", "import_from_statement"]);
|
|
21841
|
+
function namedChildrenOf(node) {
|
|
21842
|
+
return node.namedChildren.filter((child) => child !== null);
|
|
21843
|
+
}
|
|
21844
|
+
function indexImportNames(file2, stmt, locations) {
|
|
21845
|
+
for (const nameNode of fields(stmt, "name")) {
|
|
21846
|
+
if (nameNode.type === "aliased_import") {
|
|
21847
|
+
const alias = field(nameNode, "alias");
|
|
21848
|
+
if (alias !== null) {
|
|
21849
|
+
locations.set(`${file2}#${alias.text}`, { file: file2, node: alias });
|
|
21850
|
+
}
|
|
21851
|
+
continue;
|
|
21852
|
+
}
|
|
21853
|
+
const local = nameNode.type === "dotted_name" ? nameNode.namedChild(0) : nameNode;
|
|
21854
|
+
if (local !== null) {
|
|
21855
|
+
locations.set(`${file2}#${local.text}`, { file: file2, node: local });
|
|
21856
|
+
}
|
|
20540
21857
|
}
|
|
20541
|
-
return callee.type === "attribute" ? field(callee, "attribute")?.text ?? "" : callee.text;
|
|
20542
21858
|
}
|
|
20543
|
-
function
|
|
20544
|
-
|
|
20545
|
-
|
|
21859
|
+
function enclosingFunction(node) {
|
|
21860
|
+
let current = node.parent;
|
|
21861
|
+
while (current !== null) {
|
|
21862
|
+
if (FUNCTION_TYPES2.has(current.type)) {
|
|
21863
|
+
return current;
|
|
21864
|
+
}
|
|
21865
|
+
current = current.parent;
|
|
21866
|
+
}
|
|
21867
|
+
return null;
|
|
21868
|
+
}
|
|
21869
|
+
function indexFile(file2, root, locations) {
|
|
21870
|
+
const walk = (node) => {
|
|
21871
|
+
locations.set(nodeId(file2, node), { file: file2, node });
|
|
21872
|
+
if (IMPORT_TYPES.has(node.type)) {
|
|
21873
|
+
indexImportNames(file2, node, locations);
|
|
21874
|
+
}
|
|
21875
|
+
if (FUNCTION_TYPES2.has(node.type) || node.type === "class_definition") {
|
|
21876
|
+
const name = field(node, "name");
|
|
21877
|
+
if (name !== null) {
|
|
21878
|
+
locations.set(`${file2}#${name.text}`, { file: file2, node: name });
|
|
21879
|
+
}
|
|
21880
|
+
}
|
|
21881
|
+
if (node.type === "assignment") {
|
|
21882
|
+
const left = field(node, "left");
|
|
21883
|
+
if (left !== null && left.type === "identifier") {
|
|
21884
|
+
locations.set(`${file2}#${left.text}`, { file: file2, node: left });
|
|
21885
|
+
}
|
|
21886
|
+
}
|
|
21887
|
+
for (const child of namedChildrenOf(node)) {
|
|
21888
|
+
walk(child);
|
|
21889
|
+
}
|
|
21890
|
+
};
|
|
21891
|
+
walk(root);
|
|
21892
|
+
}
|
|
21893
|
+
function displayNameOf(node) {
|
|
21894
|
+
if (node.type === "identifier") {
|
|
21895
|
+
return node.text;
|
|
21896
|
+
}
|
|
21897
|
+
if (node.type === "call") {
|
|
21898
|
+
const callee = field(node, "function");
|
|
21899
|
+
return `${callee?.text ?? "call"}(...)`;
|
|
21900
|
+
}
|
|
21901
|
+
if (node.type === "function_definition" || node.type === "class_definition") {
|
|
21902
|
+
return field(node, "name")?.text ?? node.text;
|
|
21903
|
+
}
|
|
21904
|
+
const text = node.text.split("\n")[0].trim();
|
|
21905
|
+
return text.length > 40 ? `${text.slice(0, 40)}...` : text;
|
|
21906
|
+
}
|
|
21907
|
+
function width(node) {
|
|
21908
|
+
return node.endIndex - node.startIndex;
|
|
21909
|
+
}
|
|
21910
|
+
var PythonWhySession = class {
|
|
21911
|
+
root;
|
|
21912
|
+
db = new Database4();
|
|
21913
|
+
locations = /* @__PURE__ */ new Map();
|
|
21914
|
+
trees = /* @__PURE__ */ new Map();
|
|
21915
|
+
constructor(options) {
|
|
21916
|
+
this.root = path4.resolve(options.dir);
|
|
21917
|
+
const roots = [this.root];
|
|
21918
|
+
for (const file2 of findPythonFiles(this.root)) {
|
|
21919
|
+
const source = fs4.readFileSync(file2, "utf8");
|
|
21920
|
+
const root = parsePythonSync(source).rootNode;
|
|
21921
|
+
this.trees.set(file2, root);
|
|
21922
|
+
emitModuleImportFacts(this.db, file2, bindModule(root), { roots });
|
|
21923
|
+
emitValueFacts(this.db, file2, root);
|
|
21924
|
+
indexFile(file2, root, this.locations);
|
|
21925
|
+
}
|
|
21926
|
+
}
|
|
21927
|
+
/**
|
|
21928
|
+
* The smallest expression on `line` of `file` whose text is exactly
|
|
21929
|
+
* `text`, or null.
|
|
21930
|
+
*/
|
|
21931
|
+
findExpression(file2, line, text) {
|
|
21932
|
+
const root = this.rootOf(file2);
|
|
21933
|
+
if (root === null) {
|
|
21934
|
+
return null;
|
|
21935
|
+
}
|
|
21936
|
+
let found = null;
|
|
21937
|
+
const visit = (node) => {
|
|
21938
|
+
if (node.startPosition.row + 1 === line && node.text === text) {
|
|
21939
|
+
if (found === null || width(node) <= width(found)) {
|
|
21940
|
+
found = node;
|
|
21941
|
+
}
|
|
21942
|
+
}
|
|
21943
|
+
for (const child of namedChildrenOf(node)) {
|
|
21944
|
+
visit(child);
|
|
21945
|
+
}
|
|
21946
|
+
};
|
|
21947
|
+
visit(root);
|
|
21948
|
+
return found === null ? null : { file: this.pathOf(file2), node: found };
|
|
21949
|
+
}
|
|
21950
|
+
/**
|
|
21951
|
+
* The callee of the call written as `calleeText` between `startLine`
|
|
21952
|
+
* and `endLine` of `file`, or null.
|
|
21953
|
+
*/
|
|
21954
|
+
findCallee(file2, startLine, endLine, calleeText2) {
|
|
21955
|
+
const root = this.rootOf(file2);
|
|
21956
|
+
if (root === null) {
|
|
21957
|
+
return null;
|
|
21958
|
+
}
|
|
21959
|
+
let found = null;
|
|
21960
|
+
const visit = (node) => {
|
|
21961
|
+
if (node.type === "call") {
|
|
21962
|
+
const line = node.startPosition.row + 1;
|
|
21963
|
+
const callee = field(node, "function");
|
|
21964
|
+
if (line >= startLine && line <= endLine && callee !== null && callee.text === calleeText2) {
|
|
21965
|
+
found = callee;
|
|
21966
|
+
}
|
|
21967
|
+
}
|
|
21968
|
+
for (const child of namedChildrenOf(node)) {
|
|
21969
|
+
visit(child);
|
|
21970
|
+
}
|
|
21971
|
+
};
|
|
21972
|
+
visit(root);
|
|
21973
|
+
return found === null ? null : { file: this.pathOf(file2), node: found };
|
|
21974
|
+
}
|
|
21975
|
+
/**
|
|
21976
|
+
* Why `value` resolves to what it does: the witness proof, flattened
|
|
21977
|
+
* to the chain and rendered. Null when the value does not resolve,
|
|
21978
|
+
* which the caller says in its own words.
|
|
21979
|
+
*/
|
|
21980
|
+
explain(value, options = {}) {
|
|
21981
|
+
return explainResolvedKey({
|
|
21982
|
+
db: this.db,
|
|
21983
|
+
rules: RESOLUTION_RULES,
|
|
21984
|
+
key: readKey(value.file, value.node, enclosingFunction(value.node)),
|
|
21985
|
+
locate: (key) => this.locate(key),
|
|
21986
|
+
displayPath: (key) => this.displayPath(key),
|
|
21987
|
+
...options
|
|
21988
|
+
});
|
|
21989
|
+
}
|
|
21990
|
+
locate(key) {
|
|
21991
|
+
const located = this.locations.get(key);
|
|
21992
|
+
if (located === void 0) {
|
|
21993
|
+
return null;
|
|
21994
|
+
}
|
|
21995
|
+
return {
|
|
21996
|
+
name: displayNameOf(located.node),
|
|
21997
|
+
file: this.displayPath(located.file),
|
|
21998
|
+
line: located.node.startPosition.row + 1
|
|
21999
|
+
};
|
|
22000
|
+
}
|
|
22001
|
+
rootOf(file2) {
|
|
22002
|
+
return this.trees.get(this.pathOf(file2)) ?? null;
|
|
22003
|
+
}
|
|
22004
|
+
pathOf(file2) {
|
|
22005
|
+
return path4.isAbsolute(file2) ? file2 : path4.resolve(this.root, file2);
|
|
22006
|
+
}
|
|
22007
|
+
/** A file path said relative to the root, or as given when it is outside the root. */
|
|
22008
|
+
displayPath(key) {
|
|
22009
|
+
if (key.startsWith(this.root)) {
|
|
22010
|
+
return path4.relative(this.root, key);
|
|
22011
|
+
}
|
|
22012
|
+
return key;
|
|
22013
|
+
}
|
|
22014
|
+
};
|
|
22015
|
+
|
|
22016
|
+
// src/stubEvidence.ts
|
|
22017
|
+
import fs5 from "fs";
|
|
22018
|
+
import path5 from "path";
|
|
22019
|
+
function importsSubmoduleOf(module, packageName) {
|
|
22020
|
+
return module === packageName || module.startsWith(`${packageName}.`);
|
|
22021
|
+
}
|
|
22022
|
+
function importStatementModules(stmt) {
|
|
22023
|
+
const modules = [];
|
|
22024
|
+
for (const nameNode of fields(stmt, "name")) {
|
|
22025
|
+
if (nameNode.type === "aliased_import") {
|
|
22026
|
+
const dotted = field(nameNode, "name");
|
|
22027
|
+
if (dotted !== null) {
|
|
22028
|
+
modules.push(dotted.text);
|
|
22029
|
+
}
|
|
20546
22030
|
continue;
|
|
20547
22031
|
}
|
|
20548
|
-
if (
|
|
20549
|
-
|
|
22032
|
+
if (nameNode.type === "dotted_name") {
|
|
22033
|
+
modules.push(nameNode.text);
|
|
20550
22034
|
}
|
|
20551
|
-
callsUnder(child, found);
|
|
20552
22035
|
}
|
|
20553
|
-
return
|
|
22036
|
+
return modules;
|
|
20554
22037
|
}
|
|
20555
|
-
|
|
20556
|
-
|
|
20557
|
-
|
|
20558
|
-
[
|
|
20559
|
-
|
|
20560
|
-
|
|
20561
|
-
|
|
20562
|
-
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
|
|
20568
|
-
for (const node of definitions.values()) {
|
|
20569
|
-
const name = field(node, "name");
|
|
20570
|
-
if (name === null) {
|
|
22038
|
+
function importFromStatementEntries(stmt) {
|
|
22039
|
+
const moduleNode = field(stmt, "module_name");
|
|
22040
|
+
if (moduleNode === null || moduleNode.type === "relative_import") {
|
|
22041
|
+
return [];
|
|
22042
|
+
}
|
|
22043
|
+
const module = moduleNode.text;
|
|
22044
|
+
const entries = [];
|
|
22045
|
+
for (const nameNode of fields(stmt, "name")) {
|
|
22046
|
+
if (nameNode.type === "aliased_import") {
|
|
22047
|
+
const original = field(nameNode, "name");
|
|
22048
|
+
if (original !== null) {
|
|
22049
|
+
entries.push({ module, name: original.text });
|
|
22050
|
+
}
|
|
20571
22051
|
continue;
|
|
20572
22052
|
}
|
|
20573
|
-
|
|
20574
|
-
|
|
22053
|
+
if (nameNode.type === "dotted_name") {
|
|
22054
|
+
entries.push({ module, name: nameNode.text });
|
|
20575
22055
|
}
|
|
20576
22056
|
}
|
|
20577
|
-
|
|
20578
|
-
|
|
22057
|
+
return entries;
|
|
22058
|
+
}
|
|
22059
|
+
function importSitesIn(root) {
|
|
22060
|
+
const found = [];
|
|
22061
|
+
const visit = (node) => {
|
|
22062
|
+
if (node.type === "import_statement") {
|
|
22063
|
+
const line = rangeOf(node).start;
|
|
22064
|
+
for (const module of importStatementModules(node)) {
|
|
22065
|
+
found.push({ module, name: null, line });
|
|
22066
|
+
}
|
|
22067
|
+
} else if (node.type === "import_from_statement") {
|
|
22068
|
+
const line = rangeOf(node).start;
|
|
22069
|
+
for (const entry of importFromStatementEntries(node)) {
|
|
22070
|
+
found.push({ module: entry.module, name: entry.name, line });
|
|
22071
|
+
}
|
|
22072
|
+
}
|
|
22073
|
+
for (const child of node.namedChildren) {
|
|
22074
|
+
if (child !== null) {
|
|
22075
|
+
visit(child);
|
|
22076
|
+
}
|
|
22077
|
+
}
|
|
22078
|
+
};
|
|
22079
|
+
visit(root);
|
|
22080
|
+
return found;
|
|
22081
|
+
}
|
|
22082
|
+
async function pythonImportEvidence(options) {
|
|
22083
|
+
const byModule = /* @__PURE__ */ new Map();
|
|
22084
|
+
for (const file2 of findPythonFiles(options.directory)) {
|
|
22085
|
+
const source = fs5.readFileSync(file2, "utf8");
|
|
22086
|
+
const tree = await parsePython(source);
|
|
22087
|
+
const displayPath = path5.relative(options.directory, file2);
|
|
22088
|
+
for (const site of importSitesIn(tree.rootNode)) {
|
|
22089
|
+
if (!importsSubmoduleOf(site.module, options.packageName)) {
|
|
22090
|
+
continue;
|
|
22091
|
+
}
|
|
22092
|
+
const sites = byModule.get(site.module) ?? [];
|
|
22093
|
+
sites.push({ name: site.name, file: displayPath, line: site.line });
|
|
22094
|
+
byModule.set(site.module, sites);
|
|
22095
|
+
}
|
|
20579
22096
|
}
|
|
20580
|
-
|
|
20581
|
-
return new Set(db.facts("reachesStorage").map((row) => String(row[0])));
|
|
22097
|
+
return [...byModule.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([module, sites]) => ({ module, sites }));
|
|
20582
22098
|
}
|
|
20583
22099
|
export {
|
|
22100
|
+
ADAPTER_VERSION,
|
|
22101
|
+
PythonWhySession,
|
|
20584
22102
|
annotationToShape,
|
|
20585
22103
|
bindModule,
|
|
20586
22104
|
buildRouterIndex,
|
|
@@ -20595,6 +22113,10 @@ export {
|
|
|
20595
22113
|
nodeId,
|
|
20596
22114
|
objectReturnedBy,
|
|
20597
22115
|
parsePython,
|
|
22116
|
+
parsePythonSync,
|
|
22117
|
+
preloadPythonGrammar,
|
|
22118
|
+
pythonImportEvidence,
|
|
22119
|
+
readKey,
|
|
20598
22120
|
resolveAbsoluteModule,
|
|
20599
22121
|
resolveCalls,
|
|
20600
22122
|
resolveModule,
|