@suss/resolution 0.34.0 → 0.34.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/dist/index.cjs CHANGED
@@ -53,6 +53,7 @@ __export(index_exports, {
53
53
  allocationSitesOf: () => allocationSitesOf,
54
54
  alsoSteps: () => alsoSteps,
55
55
  answersFor: () => answersFor,
56
+ answersFromTestsLeftOut: () => answersFromTestsLeftOut,
56
57
  askResolution: () => askResolution,
57
58
  askResolutionUnder: () => askResolutionUnder,
58
59
  askSources: () => askSources,
@@ -1494,11 +1495,11 @@ function exportTrail(proof, describe) {
1494
1495
  }
1495
1496
  function argumentReason(proof, describe) {
1496
1497
  const [, p, a] = proof.tuple;
1497
- if (proof.kind !== "derived") {
1498
- return `${describe(p)} is a parameter, and a call passes it ${describe(a)}`;
1499
- }
1500
- const f = proof.premises[0].tuple[0];
1501
- return `${describe(p)} is a parameter of ${describe(f)}, and a call passes it ${describe(a)}`;
1498
+ const f = proof.kind === "derived" ? proof.premises.find(
1499
+ (premise) => premise.relation === "paramOf" || premise.relation === "paramNamed"
1500
+ )?.tuple[0] : void 0;
1501
+ const ofFunction = f === void 0 ? "" : ` of ${describe(f)}`;
1502
+ return `${describe(p)} is a parameter${ofFunction}, and a call passes it ${describe(a)}`;
1502
1503
  }
1503
1504
  function memberSaid(name) {
1504
1505
  const member = classMemberOf(name);
@@ -1883,6 +1884,9 @@ function startsAtName(node, name, rules) {
1883
1884
 
1884
1885
  // src/nodeKey.ts
1885
1886
  var SPAN_KEY = /^(.*):(\d+)-(\d+)$/;
1887
+ function spanFileOf(key) {
1888
+ return key.match(SPAN_KEY)?.[1] ?? null;
1889
+ }
1886
1890
  function nodeOfKey(rootsByFile, key) {
1887
1891
  const span = key.match(SPAN_KEY);
1888
1892
  if (span === null) {
@@ -2263,12 +2267,27 @@ function hasRow(db, relation, key, member) {
2263
2267
 
2264
2268
  // src/writtenValue.ts
2265
2269
  var WRITTEN_AS = "wantedIsWrittenAs";
2266
- function writtenValueOf(db, key, ask, relation = WRITTEN_AS) {
2267
- const answers = settledByKey(db, [key], ask, relation).get(key) ?? [];
2270
+ function writtenValueOf(db, key, ask, relation = WRITTEN_AS, keep = () => true) {
2271
+ const settled = settledByKey(db, [key], ask, relation).get(key) ?? [];
2272
+ const answers = settled.filter(keep);
2268
2273
  if (answers.length === 1) {
2269
2274
  return answers[0];
2270
2275
  }
2271
- return relation === WRITTEN_AS ? fallbackWrittenAs(db, key, ask) : null;
2276
+ if (answers.length === 0 && settled.length > 0) {
2277
+ return null;
2278
+ }
2279
+ const fallback = relation === WRITTEN_AS ? fallbackWrittenAs(db, key, ask) : null;
2280
+ return fallback !== null && keep(fallback) ? fallback : null;
2281
+ }
2282
+ function answersFromTestsLeftOut(key, isTestFile) {
2283
+ const asked = spanFileOf(key);
2284
+ if (asked === null || isTestFile(asked)) {
2285
+ return () => true;
2286
+ }
2287
+ return (answer) => {
2288
+ const file = spanFileOf(answer);
2289
+ return file === null || !isTestFile(file);
2290
+ };
2272
2291
  }
2273
2292
  var FALLBACK_BEHIND = "wantedFallbackBehind";
2274
2293
  function fallbackWrittenAs(db, key, ask) {
@@ -2948,19 +2967,29 @@ var STATED_RULES = [
2948
2967
  ],
2949
2968
  "construction enters its constructor"
2950
2969
  ),
2970
+ // When methodCallUnder and objectOfUnder bind as many columns, as they do
2971
+ // once contains has run, the rewrite joins the one written first. This
2972
+ // order reads the receiver under the call's own sites, not every site.
2951
2973
  (0, import_datalog5.rule)(
2952
2974
  "entersUnder",
2953
2975
  [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("caller")],
2954
2976
  [
2955
- (0, import_datalog5.lit)("call", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("cal")),
2956
- (0, import_datalog5.lit)("readsProperty", (0, import_datalog5.variable)("cal"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("m")),
2977
+ (0, import_datalog5.lit)("methodCallUnder", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("m"), (0, import_datalog5.variable)("caller")),
2957
2978
  (0, import_datalog5.lit)("objectOfUnder", (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("caller"), (0, import_datalog5.variable)("site")),
2958
2979
  (0, import_datalog5.lit)("allocates", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("cls")),
2959
- (0, import_datalog5.lit)("contains", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("m"), (0, import_datalog5.variable)("f")),
2960
- (0, import_datalog5.lit)("callUnder", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("caller"))
2980
+ (0, import_datalog5.lit)("contains", (0, import_datalog5.variable)("site"), (0, import_datalog5.variable)("m"), (0, import_datalog5.variable)("f"))
2961
2981
  ],
2962
2982
  "method call enters under its receiver"
2963
2983
  ),
2984
+ (0, import_datalog5.rule)(
2985
+ "methodCallUnder",
2986
+ [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("m"), (0, import_datalog5.variable)("caller")],
2987
+ [
2988
+ (0, import_datalog5.lit)("call", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("cal")),
2989
+ (0, import_datalog5.lit)("readsProperty", (0, import_datalog5.variable)("cal"), (0, import_datalog5.variable)("o"), (0, import_datalog5.variable)("m")),
2990
+ (0, import_datalog5.lit)("callUnder", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("caller"))
2991
+ ]
2992
+ ),
2964
2993
  // A call written as a name runs with whatever receiver the body
2965
2994
  // around it has, so a plain function called from a method keeps the
2966
2995
  // site rather than taking every caller of it.
@@ -3248,13 +3277,15 @@ var STATED_RULES = [
3248
3277
  ),
3249
3278
  // An argument arriving at the parameter it is passed to, by position
3250
3279
  // or by the name the caller wrote, keeping the call it went through
3251
- // so a caller can tell two call sites apart.
3280
+ // so a caller can tell two call sites apart. The callee comes before the
3281
+ // parameter, so a walk that starts from the argument asks which function
3282
+ // its call runs instead of trying every function with a parameter there.
3252
3283
  (0, import_datalog5.rule)(
3253
3284
  "passesArgument",
3254
3285
  [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("p"), (0, import_datalog5.variable)("a")],
3255
3286
  [
3256
- (0, import_datalog5.lit)("paramOf", (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("k"), (0, import_datalog5.variable)("p")),
3257
3287
  (0, import_datalog5.lit)("callsFunction", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("f")),
3288
+ (0, import_datalog5.lit)("paramOf", (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("k"), (0, import_datalog5.variable)("p")),
3258
3289
  (0, import_datalog5.lit)("callArg", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("k"), (0, import_datalog5.variable)("a"))
3259
3290
  ]
3260
3291
  ),
@@ -3262,8 +3293,8 @@ var STATED_RULES = [
3262
3293
  "passesArgument",
3263
3294
  [(0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("p"), (0, import_datalog5.variable)("a")],
3264
3295
  [
3265
- (0, import_datalog5.lit)("paramNamed", (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("p")),
3266
3296
  (0, import_datalog5.lit)("callsFunction", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("f")),
3297
+ (0, import_datalog5.lit)("paramNamed", (0, import_datalog5.variable)("f"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("p")),
3267
3298
  (0, import_datalog5.lit)("callKeywordArg", (0, import_datalog5.variable)("r"), (0, import_datalog5.variable)("n"), (0, import_datalog5.variable)("a"))
3268
3299
  ]
3269
3300
  ),
@@ -4659,6 +4690,7 @@ var UNDER_ANSWER_RELATIONS = [
4659
4690
  allocationSitesOf,
4660
4691
  alsoSteps,
4661
4692
  answersFor,
4693
+ answersFromTestsLeftOut,
4662
4694
  askResolution,
4663
4695
  askResolutionUnder,
4664
4696
  askSources,
package/dist/index.d.cts CHANGED
@@ -1087,7 +1087,14 @@ type Ask = (keys: readonly string[]) => void;
1087
1087
  * about as well, through `ask`, so the answer is what that function
1088
1088
  * returns rather than the call itself.
1089
1089
  */
1090
- declare function writtenValueOf(db: Database, key: string, ask: Ask, relation?: string): string | null;
1090
+ declare function writtenValueOf(db: Database, key: string, ask: Ask, relation?: string, keep?: (answer: string) => boolean): string | null;
1091
+ /**
1092
+ * Whether an answer can decide a value read at `key`: an answer in test
1093
+ * code never decides a value production code reads, so a test calling
1094
+ * `authorize(id, "https://test.com")` leaves `authorize`'s parameter as
1095
+ * open as it was. The adapter says which files are test code.
1096
+ */
1097
+ declare function answersFromTestsLeftOut(key: string, isTestFile: (file: string) => boolean): (answer: string) => boolean;
1091
1098
  /**
1092
1099
  * The fallback, `a or b`, that a value was written as, when the rules
1093
1100
  * found several expressions and every one of them came through that
@@ -1196,4 +1203,4 @@ declare const ANSWER_RELATIONS: string[];
1196
1203
  */
1197
1204
  declare const UNDER_ANSWER_RELATIONS: string[];
1198
1205
 
1199
- export { ANSWER_RELATIONS, ASKING_RELATIONS, type AskUnder, type AssociationConstructor, BASE_CLASS_RULE, type CalleeOutcome, type CaseFiles, type ChainReads, type Changes, type ContractCase, type ContractOptions, DEFAULT_IMPORT_NAME, type Dependencies, DependencyLedger, type DescribeAtom, type EntersAsSelf, EntryReuse, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, FactLog, type FactsOf, GLOBAL_MODULE, type GivesBackOne, type GivesBackOneOfArgument, type GivesBackOneOfImport, INSTANCE_STEP, type MountEdge, type MountEdges, NAMED_STORE_NAME, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, NAMESPACE_MEMBER_RULE, NAME_HOP_RULE, NO_CONTEXT, NO_CONTEXT_NAME, type NameReads, type NameWrite, type OrderedWrite, type PackWords, RECEIVER_STORE_NAME, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SourceEnd, type SourceLeaf, type SourceQuestion, type SpannedNode, type StepContext, type StepPhrase, type StoredDependencies, type StoredFacts, type StoredFileRecord, type StoredWalkRecord, type SuppliesParameter, UNDER_ANSWER_RELATIONS, UNDER_QUESTION_ROW_BUDGET, UNDER_RUN_ROW_BUDGET, UNPLACED_STORE_NAME, USES_QUESTION, type UnderOutcome, type UnwrapsByName, VALUE_STEP, type ValueLocation, type ValueUses, WatchedMap, type WhyExplained, addPackWords, agreedMountPrefix, allocationSitesOf, alsoSteps, answersFor, askResolution, askResolutionUnder, askSources, calleeOutcomeOf, calleeOutcomes, checkFactContract, classMemberName, classMemberOf, comesToUnder, couldBeSettled, declaredTypesOf, exitCodeFunctions, explainResolutionProof, explainResolvedKey, fallbackWrittenAs, fileOfKey, hashString, importedFilesByFile, isObserved, isWrittenAsUnder, joinMountedPath, mergeStoredDependencies, mountPathsOf, noDependencies, nodeOfKey, noteKeyRead, noteLookup, objectOfUnder, observeDemand, packHashes, proofRules, queryFacts, readUses, recordsToReplay, renderExplanation, resolutionProgram, resolutionUnderProgram, resolvedFunctions, settledFunction, sourceLeavesOf, startsAtName, staysInItsFunction, stillValid, underQuestionSpend, unpackHashes, valueLeftByWrites, walkRecordsByFile, writesRunInOrder, writtenAnswersFor, writtenSourcesOf, writtenValueOf, writtenValueUnder, writtenValuesByKey, writtenValuesOf };
1206
+ export { ANSWER_RELATIONS, ASKING_RELATIONS, type AskUnder, type AssociationConstructor, BASE_CLASS_RULE, type CalleeOutcome, type CaseFiles, type ChainReads, type Changes, type ContractCase, type ContractOptions, DEFAULT_IMPORT_NAME, type Dependencies, DependencyLedger, type DescribeAtom, type EntersAsSelf, EntryReuse, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, FactLog, type FactsOf, GLOBAL_MODULE, type GivesBackOne, type GivesBackOneOfArgument, type GivesBackOneOfImport, INSTANCE_STEP, type MountEdge, type MountEdges, NAMED_STORE_NAME, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, NAMESPACE_MEMBER_RULE, NAME_HOP_RULE, NO_CONTEXT, NO_CONTEXT_NAME, type NameReads, type NameWrite, type OrderedWrite, type PackWords, RECEIVER_STORE_NAME, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SourceEnd, type SourceLeaf, type SourceQuestion, type SpannedNode, type StepContext, type StepPhrase, type StoredDependencies, type StoredFacts, type StoredFileRecord, type StoredWalkRecord, type SuppliesParameter, UNDER_ANSWER_RELATIONS, UNDER_QUESTION_ROW_BUDGET, UNDER_RUN_ROW_BUDGET, UNPLACED_STORE_NAME, USES_QUESTION, type UnderOutcome, type UnwrapsByName, VALUE_STEP, type ValueLocation, type ValueUses, WatchedMap, type WhyExplained, addPackWords, agreedMountPrefix, allocationSitesOf, alsoSteps, answersFor, answersFromTestsLeftOut, askResolution, askResolutionUnder, askSources, calleeOutcomeOf, calleeOutcomes, checkFactContract, classMemberName, classMemberOf, comesToUnder, couldBeSettled, declaredTypesOf, exitCodeFunctions, explainResolutionProof, explainResolvedKey, fallbackWrittenAs, fileOfKey, hashString, importedFilesByFile, isObserved, isWrittenAsUnder, joinMountedPath, mergeStoredDependencies, mountPathsOf, noDependencies, nodeOfKey, noteKeyRead, noteLookup, objectOfUnder, observeDemand, packHashes, proofRules, queryFacts, readUses, recordsToReplay, renderExplanation, resolutionProgram, resolutionUnderProgram, resolvedFunctions, settledFunction, sourceLeavesOf, startsAtName, staysInItsFunction, stillValid, underQuestionSpend, unpackHashes, valueLeftByWrites, walkRecordsByFile, writesRunInOrder, writtenAnswersFor, writtenSourcesOf, writtenValueOf, writtenValueUnder, writtenValuesByKey, writtenValuesOf };
package/dist/index.d.ts CHANGED
@@ -1087,7 +1087,14 @@ type Ask = (keys: readonly string[]) => void;
1087
1087
  * about as well, through `ask`, so the answer is what that function
1088
1088
  * returns rather than the call itself.
1089
1089
  */
1090
- declare function writtenValueOf(db: Database, key: string, ask: Ask, relation?: string): string | null;
1090
+ declare function writtenValueOf(db: Database, key: string, ask: Ask, relation?: string, keep?: (answer: string) => boolean): string | null;
1091
+ /**
1092
+ * Whether an answer can decide a value read at `key`: an answer in test
1093
+ * code never decides a value production code reads, so a test calling
1094
+ * `authorize(id, "https://test.com")` leaves `authorize`'s parameter as
1095
+ * open as it was. The adapter says which files are test code.
1096
+ */
1097
+ declare function answersFromTestsLeftOut(key: string, isTestFile: (file: string) => boolean): (answer: string) => boolean;
1091
1098
  /**
1092
1099
  * The fallback, `a or b`, that a value was written as, when the rules
1093
1100
  * found several expressions and every one of them came through that
@@ -1196,4 +1203,4 @@ declare const ANSWER_RELATIONS: string[];
1196
1203
  */
1197
1204
  declare const UNDER_ANSWER_RELATIONS: string[];
1198
1205
 
1199
- export { ANSWER_RELATIONS, ASKING_RELATIONS, type AskUnder, type AssociationConstructor, BASE_CLASS_RULE, type CalleeOutcome, type CaseFiles, type ChainReads, type Changes, type ContractCase, type ContractOptions, DEFAULT_IMPORT_NAME, type Dependencies, DependencyLedger, type DescribeAtom, type EntersAsSelf, EntryReuse, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, FactLog, type FactsOf, GLOBAL_MODULE, type GivesBackOne, type GivesBackOneOfArgument, type GivesBackOneOfImport, INSTANCE_STEP, type MountEdge, type MountEdges, NAMED_STORE_NAME, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, NAMESPACE_MEMBER_RULE, NAME_HOP_RULE, NO_CONTEXT, NO_CONTEXT_NAME, type NameReads, type NameWrite, type OrderedWrite, type PackWords, RECEIVER_STORE_NAME, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SourceEnd, type SourceLeaf, type SourceQuestion, type SpannedNode, type StepContext, type StepPhrase, type StoredDependencies, type StoredFacts, type StoredFileRecord, type StoredWalkRecord, type SuppliesParameter, UNDER_ANSWER_RELATIONS, UNDER_QUESTION_ROW_BUDGET, UNDER_RUN_ROW_BUDGET, UNPLACED_STORE_NAME, USES_QUESTION, type UnderOutcome, type UnwrapsByName, VALUE_STEP, type ValueLocation, type ValueUses, WatchedMap, type WhyExplained, addPackWords, agreedMountPrefix, allocationSitesOf, alsoSteps, answersFor, askResolution, askResolutionUnder, askSources, calleeOutcomeOf, calleeOutcomes, checkFactContract, classMemberName, classMemberOf, comesToUnder, couldBeSettled, declaredTypesOf, exitCodeFunctions, explainResolutionProof, explainResolvedKey, fallbackWrittenAs, fileOfKey, hashString, importedFilesByFile, isObserved, isWrittenAsUnder, joinMountedPath, mergeStoredDependencies, mountPathsOf, noDependencies, nodeOfKey, noteKeyRead, noteLookup, objectOfUnder, observeDemand, packHashes, proofRules, queryFacts, readUses, recordsToReplay, renderExplanation, resolutionProgram, resolutionUnderProgram, resolvedFunctions, settledFunction, sourceLeavesOf, startsAtName, staysInItsFunction, stillValid, underQuestionSpend, unpackHashes, valueLeftByWrites, walkRecordsByFile, writesRunInOrder, writtenAnswersFor, writtenSourcesOf, writtenValueOf, writtenValueUnder, writtenValuesByKey, writtenValuesOf };
1206
+ export { ANSWER_RELATIONS, ASKING_RELATIONS, type AskUnder, type AssociationConstructor, BASE_CLASS_RULE, type CalleeOutcome, type CaseFiles, type ChainReads, type Changes, type ContractCase, type ContractOptions, DEFAULT_IMPORT_NAME, type Dependencies, DependencyLedger, type DescribeAtom, type EntersAsSelf, EntryReuse, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, FactLog, type FactsOf, GLOBAL_MODULE, type GivesBackOne, type GivesBackOneOfArgument, type GivesBackOneOfImport, INSTANCE_STEP, type MountEdge, type MountEdges, NAMED_STORE_NAME, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, NAMESPACE_MEMBER_RULE, NAME_HOP_RULE, NO_CONTEXT, NO_CONTEXT_NAME, type NameReads, type NameWrite, type OrderedWrite, type PackWords, RECEIVER_STORE_NAME, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SourceEnd, type SourceLeaf, type SourceQuestion, type SpannedNode, type StepContext, type StepPhrase, type StoredDependencies, type StoredFacts, type StoredFileRecord, type StoredWalkRecord, type SuppliesParameter, UNDER_ANSWER_RELATIONS, UNDER_QUESTION_ROW_BUDGET, UNDER_RUN_ROW_BUDGET, UNPLACED_STORE_NAME, USES_QUESTION, type UnderOutcome, type UnwrapsByName, VALUE_STEP, type ValueLocation, type ValueUses, WatchedMap, type WhyExplained, addPackWords, agreedMountPrefix, allocationSitesOf, alsoSteps, answersFor, answersFromTestsLeftOut, askResolution, askResolutionUnder, askSources, calleeOutcomeOf, calleeOutcomes, checkFactContract, classMemberName, classMemberOf, comesToUnder, couldBeSettled, declaredTypesOf, exitCodeFunctions, explainResolutionProof, explainResolvedKey, fallbackWrittenAs, fileOfKey, hashString, importedFilesByFile, isObserved, isWrittenAsUnder, joinMountedPath, mergeStoredDependencies, mountPathsOf, noDependencies, nodeOfKey, noteKeyRead, noteLookup, objectOfUnder, observeDemand, packHashes, proofRules, queryFacts, readUses, recordsToReplay, renderExplanation, resolutionProgram, resolutionUnderProgram, resolvedFunctions, settledFunction, sourceLeavesOf, startsAtName, staysInItsFunction, stillValid, underQuestionSpend, unpackHashes, valueLeftByWrites, walkRecordsByFile, writesRunInOrder, writtenAnswersFor, writtenSourcesOf, writtenValueOf, writtenValueUnder, writtenValuesByKey, writtenValuesOf };
package/dist/index.js CHANGED
@@ -1391,11 +1391,11 @@ function exportTrail(proof, describe) {
1391
1391
  }
1392
1392
  function argumentReason(proof, describe) {
1393
1393
  const [, p, a] = proof.tuple;
1394
- if (proof.kind !== "derived") {
1395
- return `${describe(p)} is a parameter, and a call passes it ${describe(a)}`;
1396
- }
1397
- const f = proof.premises[0].tuple[0];
1398
- return `${describe(p)} is a parameter of ${describe(f)}, and a call passes it ${describe(a)}`;
1394
+ const f = proof.kind === "derived" ? proof.premises.find(
1395
+ (premise) => premise.relation === "paramOf" || premise.relation === "paramNamed"
1396
+ )?.tuple[0] : void 0;
1397
+ const ofFunction = f === void 0 ? "" : ` of ${describe(f)}`;
1398
+ return `${describe(p)} is a parameter${ofFunction}, and a call passes it ${describe(a)}`;
1399
1399
  }
1400
1400
  function memberSaid(name) {
1401
1401
  const member = classMemberOf(name);
@@ -1780,6 +1780,9 @@ function startsAtName(node, name, rules) {
1780
1780
 
1781
1781
  // src/nodeKey.ts
1782
1782
  var SPAN_KEY = /^(.*):(\d+)-(\d+)$/;
1783
+ function spanFileOf(key) {
1784
+ return key.match(SPAN_KEY)?.[1] ?? null;
1785
+ }
1783
1786
  function nodeOfKey(rootsByFile, key) {
1784
1787
  const span = key.match(SPAN_KEY);
1785
1788
  if (span === null) {
@@ -2160,12 +2163,27 @@ function hasRow(db, relation, key, member) {
2160
2163
 
2161
2164
  // src/writtenValue.ts
2162
2165
  var WRITTEN_AS = "wantedIsWrittenAs";
2163
- function writtenValueOf(db, key, ask, relation = WRITTEN_AS) {
2164
- const answers = settledByKey(db, [key], ask, relation).get(key) ?? [];
2166
+ function writtenValueOf(db, key, ask, relation = WRITTEN_AS, keep = () => true) {
2167
+ const settled = settledByKey(db, [key], ask, relation).get(key) ?? [];
2168
+ const answers = settled.filter(keep);
2165
2169
  if (answers.length === 1) {
2166
2170
  return answers[0];
2167
2171
  }
2168
- return relation === WRITTEN_AS ? fallbackWrittenAs(db, key, ask) : null;
2172
+ if (answers.length === 0 && settled.length > 0) {
2173
+ return null;
2174
+ }
2175
+ const fallback = relation === WRITTEN_AS ? fallbackWrittenAs(db, key, ask) : null;
2176
+ return fallback !== null && keep(fallback) ? fallback : null;
2177
+ }
2178
+ function answersFromTestsLeftOut(key, isTestFile) {
2179
+ const asked = spanFileOf(key);
2180
+ if (asked === null || isTestFile(asked)) {
2181
+ return () => true;
2182
+ }
2183
+ return (answer) => {
2184
+ const file = spanFileOf(answer);
2185
+ return file === null || !isTestFile(file);
2186
+ };
2169
2187
  }
2170
2188
  var FALLBACK_BEHIND = "wantedFallbackBehind";
2171
2189
  function fallbackWrittenAs(db, key, ask) {
@@ -2845,19 +2863,29 @@ var STATED_RULES = [
2845
2863
  ],
2846
2864
  "construction enters its constructor"
2847
2865
  ),
2866
+ // When methodCallUnder and objectOfUnder bind as many columns, as they do
2867
+ // once contains has run, the rewrite joins the one written first. This
2868
+ // order reads the receiver under the call's own sites, not every site.
2848
2869
  rule2(
2849
2870
  "entersUnder",
2850
2871
  [v2("r"), v2("f"), v2("site"), v2("caller")],
2851
2872
  [
2852
- lit2("call", v2("r"), v2("cal")),
2853
- lit2("readsProperty", v2("cal"), v2("o"), v2("m")),
2873
+ lit2("methodCallUnder", v2("r"), v2("o"), v2("m"), v2("caller")),
2854
2874
  lit2("objectOfUnder", v2("o"), v2("caller"), v2("site")),
2855
2875
  lit2("allocates", v2("site"), v2("cls")),
2856
- lit2("contains", v2("site"), v2("m"), v2("f")),
2857
- lit2("callUnder", v2("r"), v2("caller"))
2876
+ lit2("contains", v2("site"), v2("m"), v2("f"))
2858
2877
  ],
2859
2878
  "method call enters under its receiver"
2860
2879
  ),
2880
+ rule2(
2881
+ "methodCallUnder",
2882
+ [v2("r"), v2("o"), v2("m"), v2("caller")],
2883
+ [
2884
+ lit2("call", v2("r"), v2("cal")),
2885
+ lit2("readsProperty", v2("cal"), v2("o"), v2("m")),
2886
+ lit2("callUnder", v2("r"), v2("caller"))
2887
+ ]
2888
+ ),
2861
2889
  // A call written as a name runs with whatever receiver the body
2862
2890
  // around it has, so a plain function called from a method keeps the
2863
2891
  // site rather than taking every caller of it.
@@ -3145,13 +3173,15 @@ var STATED_RULES = [
3145
3173
  ),
3146
3174
  // An argument arriving at the parameter it is passed to, by position
3147
3175
  // or by the name the caller wrote, keeping the call it went through
3148
- // so a caller can tell two call sites apart.
3176
+ // so a caller can tell two call sites apart. The callee comes before the
3177
+ // parameter, so a walk that starts from the argument asks which function
3178
+ // its call runs instead of trying every function with a parameter there.
3149
3179
  rule2(
3150
3180
  "passesArgument",
3151
3181
  [v2("r"), v2("p"), v2("a")],
3152
3182
  [
3153
- lit2("paramOf", v2("f"), v2("k"), v2("p")),
3154
3183
  lit2("callsFunction", v2("r"), v2("f")),
3184
+ lit2("paramOf", v2("f"), v2("k"), v2("p")),
3155
3185
  lit2("callArg", v2("r"), v2("k"), v2("a"))
3156
3186
  ]
3157
3187
  ),
@@ -3159,8 +3189,8 @@ var STATED_RULES = [
3159
3189
  "passesArgument",
3160
3190
  [v2("r"), v2("p"), v2("a")],
3161
3191
  [
3162
- lit2("paramNamed", v2("f"), v2("n"), v2("p")),
3163
3192
  lit2("callsFunction", v2("r"), v2("f")),
3193
+ lit2("paramNamed", v2("f"), v2("n"), v2("p")),
3164
3194
  lit2("callKeywordArg", v2("r"), v2("n"), v2("a"))
3165
3195
  ]
3166
3196
  ),
@@ -4555,6 +4585,7 @@ export {
4555
4585
  allocationSitesOf,
4556
4586
  alsoSteps,
4557
4587
  answersFor,
4588
+ answersFromTestsLeftOut,
4558
4589
  askResolution,
4559
4590
  askResolutionUnder,
4560
4591
  askSources,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/resolution",
3
- "version": "0.34.0",
3
+ "version": "0.34.1",
4
4
  "type": "module",
5
5
  "description": "The rules suss uses to follow a name in TypeScript, Python or Ruby to the function it comes down to.",
6
6
  "keywords": [
@@ -56,6 +56,6 @@
56
56
  "access": "public"
57
57
  },
58
58
  "dependencies": {
59
- "@suss/datalog": "0.34.0"
59
+ "@suss/datalog": "0.34.1"
60
60
  }
61
61
  }