@suss/resolution 0.28.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  calleeOutcomeOf: () => calleeOutcomeOf,
35
35
  calleeOutcomes: () => calleeOutcomes,
36
36
  checkFactContract: () => checkFactContract,
37
+ couldBeSettled: () => couldBeSettled,
37
38
  explainResolutionProof: () => explainResolutionProof,
38
39
  explainResolvedKey: () => explainResolvedKey,
39
40
  joinMountedPath: () => joinMountedPath,
@@ -152,6 +153,9 @@ function askAboutSources(db, keys, program) {
152
153
  wave = next;
153
154
  }
154
155
  }
156
+ function couldBeSettled(db, key) {
157
+ return db.has("func", [key]) || db.has("objectValue", [key]) || sourcesOf(db, key).length > 0;
158
+ }
155
159
  function sourcesOf(db, key) {
156
160
  const base = db.lookup("readsProperty", 0, key)[0];
157
161
  if (base !== void 0) {
@@ -584,6 +588,38 @@ var DEFAULT_PHRASES = {
584
588
  ]
585
589
  };
586
590
  },
591
+ "declared finder": ({ tuple, premises, describe }) => {
592
+ const method = String(premises[1].tuple[2]);
593
+ const base = String(premises[4].tuple[0]);
594
+ return {
595
+ reason: `${describe(tuple[0])} calls ${method} on ${describe(tuple[1])}, which gives back one of it`,
596
+ assumptions: [
597
+ `a pack declares that ${method} on a class extending ${base} gives back one of that class`
598
+ ]
599
+ };
600
+ },
601
+ "declared argument finder": ({ tuple, premises, describe }) => {
602
+ const method = String(premises[1].tuple[2]);
603
+ const position = String(premises[2].tuple[1]);
604
+ const base = String(premises[3].tuple[0]);
605
+ return {
606
+ reason: `${describe(tuple[0])} calls ${method} with ${describe(tuple[1])} at argument ${position}, which gives back one of it`,
607
+ assumptions: [
608
+ `a pack declares that ${method} with a class extending ${base} at argument ${position} gives back one of that class`
609
+ ]
610
+ };
611
+ },
612
+ "declared import finder": ({ tuple, premises, describe }) => {
613
+ const module2 = String(premises[1].tuple[1]);
614
+ const name = String(premises[1].tuple[2]);
615
+ const position = String(premises[2].tuple[2]);
616
+ return {
617
+ reason: `${describe(tuple[0])} calls ${name} from ${module2} with ${describe(tuple[1])} at argument ${position}, which gives back one of it`,
618
+ assumptions: [
619
+ `a pack declares that ${name} from ${module2} gives back one of the class at argument ${position}`
620
+ ]
621
+ };
622
+ },
587
623
  "call result": ({ tuple, premises, describe, inline }) => {
588
624
  const invokes = premises[0];
589
625
  const invoked = invokes.tuple[1];
@@ -1070,6 +1106,57 @@ var RESOLUTION_RULES = [
1070
1106
  ],
1071
1107
  "class instance"
1072
1108
  ),
1109
+ // A finder the library declares, keyed on the base a pack named so a
1110
+ // project class with a method of the same name on another hierarchy is
1111
+ // left alone. The DESIGN says what a chain of them composes into.
1112
+ (0, import_datalog4.rule)(
1113
+ "stepsTo",
1114
+ [(0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("cls"), VALUE_STEP],
1115
+ [
1116
+ (0, import_datalog4.lit)("call", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("c")),
1117
+ (0, import_datalog4.lit)("readsProperty", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("m")),
1118
+ (0, import_datalog4.lit)("objectOf", (0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("cls")),
1119
+ (0, import_datalog4.lit)("objectValue", (0, import_datalog4.variable)("cls")),
1120
+ // Before `libraryBase`, so the demand rewrite asks it with the base
1121
+ // bound and walks an ancestry only for a method a pack declared.
1122
+ (0, import_datalog4.lit)("givesBackOne", (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("m")),
1123
+ (0, import_datalog4.lit)("libraryBase", (0, import_datalog4.variable)("cls"), (0, import_datalog4.variable)("n"))
1124
+ ],
1125
+ "declared finder"
1126
+ ),
1127
+ // The same word, for a library that takes the class as an argument
1128
+ // instead of as the receiver. The argument having to reach the base a
1129
+ // pack named is what keeps an unrelated `get` out.
1130
+ (0, import_datalog4.rule)(
1131
+ "stepsTo",
1132
+ [(0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("cls"), VALUE_STEP],
1133
+ [
1134
+ (0, import_datalog4.lit)("call", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("c")),
1135
+ (0, import_datalog4.lit)("readsProperty", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("o"), (0, import_datalog4.variable)("m")),
1136
+ (0, import_datalog4.lit)("callArg", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("k"), (0, import_datalog4.variable)("a")),
1137
+ // Before `libraryBase`, so the demand rewrite asks it with the base
1138
+ // bound and walks an ancestry only for a method a pack declared.
1139
+ (0, import_datalog4.lit)("givesBackOneOfArgument", (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("m"), (0, import_datalog4.variable)("k")),
1140
+ (0, import_datalog4.lit)("objectOf", (0, import_datalog4.variable)("a"), (0, import_datalog4.variable)("cls")),
1141
+ (0, import_datalog4.lit)("libraryBase", (0, import_datalog4.variable)("cls"), (0, import_datalog4.variable)("n"))
1142
+ ],
1143
+ "declared argument finder"
1144
+ ),
1145
+ // The same again for a function called on its own rather than read off
1146
+ // anything, keyed on the module it was imported from so a project
1147
+ // function spelled the same way is not mistaken for it.
1148
+ (0, import_datalog4.rule)(
1149
+ "stepsTo",
1150
+ [(0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("cls"), VALUE_STEP],
1151
+ [
1152
+ (0, import_datalog4.lit)("call", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("c")),
1153
+ (0, import_datalog4.lit)("comesFrom", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("mod"), (0, import_datalog4.variable)("n")),
1154
+ (0, import_datalog4.lit)("givesBackOneOfImport", (0, import_datalog4.variable)("mod"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("k")),
1155
+ (0, import_datalog4.lit)("callArg", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("k"), (0, import_datalog4.variable)("a")),
1156
+ (0, import_datalog4.lit)("objectOf", (0, import_datalog4.variable)("a"), (0, import_datalog4.variable)("cls"))
1157
+ ],
1158
+ "declared import finder"
1159
+ ),
1073
1160
  // Wrapper transparency, derived: calling a factory that returns a
1074
1161
  // function which calls its parameter k steps to argument k.
1075
1162
  (0, import_datalog4.rule)(
@@ -1343,6 +1430,54 @@ var RESOLUTION_RULES = [
1343
1430
  (0, import_datalog4.lit)("contains", (0, import_datalog4.variable)("baseCls"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("held"))
1344
1431
  ]
1345
1432
  ),
1433
+ // An association is read off an instance as a property, and stating
1434
+ // it as `contains` is what puts it on the ancestry rule, so a concern
1435
+ // or a base class can be the one that declares it.
1436
+ (0, import_datalog4.rule)(
1437
+ "contains",
1438
+ [(0, import_datalog4.variable)("cls"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("t")],
1439
+ [(0, import_datalog4.lit)("declaresAssociation", (0, import_datalog4.variable)("cls"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("t"))],
1440
+ "declared association"
1441
+ ),
1442
+ // The same, for a language that buries the declaration in an ordinary
1443
+ // assignment. Keying on the module the callable came from is what
1444
+ // keeps a project function spelled the same way out.
1445
+ (0, import_datalog4.rule)(
1446
+ "contains",
1447
+ [(0, import_datalog4.variable)("cls"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("t")],
1448
+ [
1449
+ (0, import_datalog4.lit)("fieldCall", (0, import_datalog4.variable)("cls"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("t")),
1450
+ (0, import_datalog4.lit)("comesFrom", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("m"), (0, import_datalog4.variable)("name")),
1451
+ (0, import_datalog4.lit)("associationConstructor", (0, import_datalog4.variable)("m"), (0, import_datalog4.variable)("name"))
1452
+ ],
1453
+ "constructed association"
1454
+ ),
1455
+ // The library base a class's ancestry arrives at, however many of a
1456
+ // project's own classes sit in between. A base the run declares has a
1457
+ // node to keep walking from; one it does not has only a name.
1458
+ (0, import_datalog4.rule)("libraryBase", [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("n")], [(0, import_datalog4.lit)("extendsNamed", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("n"))]),
1459
+ (0, import_datalog4.rule)(
1460
+ "libraryBase",
1461
+ [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("n")],
1462
+ [
1463
+ (0, import_datalog4.lit)("extends", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("x")),
1464
+ (0, import_datalog4.lit)("comesTo", (0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("b")),
1465
+ (0, import_datalog4.lit)("libraryBase", (0, import_datalog4.variable)("b"), (0, import_datalog4.variable)("n"))
1466
+ ]
1467
+ ),
1468
+ // A base a library hands back from a call rather than exporting as a
1469
+ // class, SQLAlchemy's `Base = declarative_base()`. The walk stops at
1470
+ // the call, so the function it called is the name to match on.
1471
+ (0, import_datalog4.rule)(
1472
+ "libraryBase",
1473
+ [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("n")],
1474
+ [
1475
+ (0, import_datalog4.lit)("extends", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("x")),
1476
+ (0, import_datalog4.lit)("isWrittenAs", (0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("r")),
1477
+ (0, import_datalog4.lit)("call", (0, import_datalog4.variable)("r"), (0, import_datalog4.variable)("cc")),
1478
+ (0, import_datalog4.lit)("comesFrom", (0, import_datalog4.variable)("cc"), (0, import_datalog4.variable)("m"), (0, import_datalog4.variable)("n"))
1479
+ ]
1480
+ ),
1346
1481
  // Which function a factory returns, and which of its parameters that
1347
1482
  // function calls: together they are what makes it a wrapper.
1348
1483
  (0, import_datalog4.rule)(
@@ -1691,6 +1826,25 @@ var RESOLUTION_QUESTIONS = [
1691
1826
  [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("n")],
1692
1827
  [(0, import_datalog4.lit)("ancestryChain", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("b")), (0, import_datalog4.lit)("extendsNamed", (0, import_datalog4.variable)("b"), (0, import_datalog4.variable)("n"))]
1693
1828
  ),
1829
+ // Each method the same chain declares, so a caller that settled a
1830
+ // receiver on a class can tell a method the project writes itself
1831
+ // from one only the library provides.
1832
+ (0, import_datalog4.rule)(
1833
+ "wantedDeclaredName",
1834
+ [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("n")],
1835
+ [
1836
+ (0, import_datalog4.lit)("ancestryChain", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("b")),
1837
+ (0, import_datalog4.lit)("holdsProperty", (0, import_datalog4.variable)("b"), (0, import_datalog4.variable)("n"), (0, import_datalog4.variable)("held"))
1838
+ ]
1839
+ ),
1840
+ // A method the class gets under a name the source computes rather than
1841
+ // writes out. The adapter settles the name against the same facts and
1842
+ // says so, and the chain reports it beside the ones written out.
1843
+ (0, import_datalog4.rule)(
1844
+ "wantedDeclaredName",
1845
+ [(0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("n")],
1846
+ [(0, import_datalog4.lit)("ancestryChain", (0, import_datalog4.variable)("c"), (0, import_datalog4.variable)("b")), (0, import_datalog4.lit)("declaresName", (0, import_datalog4.variable)("b"), (0, import_datalog4.variable)("n"))]
1847
+ ),
1694
1848
  (0, import_datalog4.rule)("anchorChain", [(0, import_datalog4.variable)("x"), (0, import_datalog4.variable)("x")], [(0, import_datalog4.lit)("wantedAnchor", (0, import_datalog4.variable)("x"))]),
1695
1849
  (0, import_datalog4.rule)(
1696
1850
  "anchorChain",
@@ -1754,6 +1908,7 @@ var ANSWER_RELATIONS = [
1754
1908
  calleeOutcomeOf,
1755
1909
  calleeOutcomes,
1756
1910
  checkFactContract,
1911
+ couldBeSettled,
1757
1912
  explainResolutionProof,
1758
1913
  explainResolvedKey,
1759
1914
  joinMountedPath,
package/dist/index.d.cts CHANGED
@@ -58,6 +58,14 @@ type CalleeOutcome =
58
58
  declare function calleeOutcomes(db: Database, keys: readonly string[], program?: OnDemandRules): Map<string, CalleeOutcome>;
59
59
  /** What one callee is, for a caller with a single key in hand. */
60
60
  declare function calleeOutcomeOf(db: Database, key: string, program?: OnDemandRules): CalleeOutcome;
61
+ /**
62
+ * Whether this run says anything at all about the key: it is a value
63
+ * itself, or something built it. A key that fails this settles on
64
+ * nothing, and asking anyway adds a `wanted` fact that every later
65
+ * question has to derive over, so a caller with a batch of keys and a
66
+ * cheap question about each one puts this in front of the batch.
67
+ */
68
+ declare function couldBeSettled(db: Database, key: string): boolean;
61
69
  declare function writtenSourcesOf(db: Database, key: string): string[];
62
70
 
63
71
  /** The tuples of one relation, as strings, which is all a case compares. */
@@ -531,4 +539,4 @@ declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
531
539
  /** The relations `RESOLUTION_QUESTIONS` answers into. */
532
540
  declare const ANSWER_RELATIONS: string[];
533
541
 
534
- export { ANSWER_RELATIONS, ASKING_RELATIONS, type CalleeOutcome, type CaseFiles, type ChainReads, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, type NameReads, type NameWrite, type OrderedWrite, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SpannedNode, type StepContext, type StepPhrase, VALUE_STEP, type ValueLocation, type WhyExplained, agreedMountPrefix, askResolution, calleeOutcomeOf, calleeOutcomes, checkFactContract, explainResolutionProof, explainResolvedKey, joinMountedPath, mountPathsOf, nodeOfKey, placeholderValues, queryFacts, renderExplanation, resolutionProgram, singleAnswers, startsAtName, valueLeftByWrites, writesRunInOrder, writtenSourcesOf, writtenValueOf };
542
+ export { ANSWER_RELATIONS, ASKING_RELATIONS, type CalleeOutcome, type CaseFiles, type ChainReads, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, type NameReads, type NameWrite, type OrderedWrite, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SpannedNode, type StepContext, type StepPhrase, VALUE_STEP, type ValueLocation, type WhyExplained, agreedMountPrefix, askResolution, calleeOutcomeOf, calleeOutcomes, checkFactContract, couldBeSettled, explainResolutionProof, explainResolvedKey, joinMountedPath, mountPathsOf, nodeOfKey, placeholderValues, queryFacts, renderExplanation, resolutionProgram, singleAnswers, startsAtName, valueLeftByWrites, writesRunInOrder, writtenSourcesOf, writtenValueOf };
package/dist/index.d.ts CHANGED
@@ -58,6 +58,14 @@ type CalleeOutcome =
58
58
  declare function calleeOutcomes(db: Database, keys: readonly string[], program?: OnDemandRules): Map<string, CalleeOutcome>;
59
59
  /** What one callee is, for a caller with a single key in hand. */
60
60
  declare function calleeOutcomeOf(db: Database, key: string, program?: OnDemandRules): CalleeOutcome;
61
+ /**
62
+ * Whether this run says anything at all about the key: it is a value
63
+ * itself, or something built it. A key that fails this settles on
64
+ * nothing, and asking anyway adds a `wanted` fact that every later
65
+ * question has to derive over, so a caller with a batch of keys and a
66
+ * cheap question about each one puts this in front of the batch.
67
+ */
68
+ declare function couldBeSettled(db: Database, key: string): boolean;
61
69
  declare function writtenSourcesOf(db: Database, key: string): string[];
62
70
 
63
71
  /** The tuples of one relation, as strings, which is all a case compares. */
@@ -531,4 +539,4 @@ declare const RESOLUTION_QUESTIONS: _suss_datalog.Rule[];
531
539
  /** The relations `RESOLUTION_QUESTIONS` answers into. */
532
540
  declare const ANSWER_RELATIONS: string[];
533
541
 
534
- export { ANSWER_RELATIONS, ASKING_RELATIONS, type CalleeOutcome, type CaseFiles, type ChainReads, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, type NameReads, type NameWrite, type OrderedWrite, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SpannedNode, type StepContext, type StepPhrase, VALUE_STEP, type ValueLocation, type WhyExplained, agreedMountPrefix, askResolution, calleeOutcomeOf, calleeOutcomes, checkFactContract, explainResolutionProof, explainResolvedKey, joinMountedPath, mountPathsOf, nodeOfKey, placeholderValues, queryFacts, renderExplanation, resolutionProgram, singleAnswers, startsAtName, valueLeftByWrites, writesRunInOrder, writtenSourcesOf, writtenValueOf };
542
+ export { ANSWER_RELATIONS, ASKING_RELATIONS, type CalleeOutcome, type CaseFiles, type ChainReads, type ContractCase, type ContractOptions, type DescribeAtom, type ExplainOptions, type ExplainResolvedKeyOptions, type ExplainStats, FACT_CONTRACT_CASES, type FactsOf, type MountEdge, type MountEdges, NAMESPACE_IMPORT, NAMESPACE_IMPORT_NAME, type NameReads, type NameWrite, type OrderedWrite, RESOLUTION_QUESTIONS, RESOLUTION_RULES, RESULT_STEP, type ReadNode, type ResolutionExplanation, type ResolutionStep, type SpannedNode, type StepContext, type StepPhrase, VALUE_STEP, type ValueLocation, type WhyExplained, agreedMountPrefix, askResolution, calleeOutcomeOf, calleeOutcomes, checkFactContract, couldBeSettled, explainResolutionProof, explainResolvedKey, joinMountedPath, mountPathsOf, nodeOfKey, placeholderValues, queryFacts, renderExplanation, resolutionProgram, singleAnswers, startsAtName, valueLeftByWrites, writesRunInOrder, writtenSourcesOf, writtenValueOf };
package/dist/index.js CHANGED
@@ -98,6 +98,9 @@ function askAboutSources(db, keys, program) {
98
98
  wave = next;
99
99
  }
100
100
  }
101
+ function couldBeSettled(db, key) {
102
+ return db.has("func", [key]) || db.has("objectValue", [key]) || sourcesOf(db, key).length > 0;
103
+ }
101
104
  function sourcesOf(db, key) {
102
105
  const base = db.lookup("readsProperty", 0, key)[0];
103
106
  if (base !== void 0) {
@@ -530,6 +533,38 @@ var DEFAULT_PHRASES = {
530
533
  ]
531
534
  };
532
535
  },
536
+ "declared finder": ({ tuple, premises, describe }) => {
537
+ const method = String(premises[1].tuple[2]);
538
+ const base = String(premises[4].tuple[0]);
539
+ return {
540
+ reason: `${describe(tuple[0])} calls ${method} on ${describe(tuple[1])}, which gives back one of it`,
541
+ assumptions: [
542
+ `a pack declares that ${method} on a class extending ${base} gives back one of that class`
543
+ ]
544
+ };
545
+ },
546
+ "declared argument finder": ({ tuple, premises, describe }) => {
547
+ const method = String(premises[1].tuple[2]);
548
+ const position = String(premises[2].tuple[1]);
549
+ const base = String(premises[3].tuple[0]);
550
+ return {
551
+ reason: `${describe(tuple[0])} calls ${method} with ${describe(tuple[1])} at argument ${position}, which gives back one of it`,
552
+ assumptions: [
553
+ `a pack declares that ${method} with a class extending ${base} at argument ${position} gives back one of that class`
554
+ ]
555
+ };
556
+ },
557
+ "declared import finder": ({ tuple, premises, describe }) => {
558
+ const module = String(premises[1].tuple[1]);
559
+ const name = String(premises[1].tuple[2]);
560
+ const position = String(premises[2].tuple[2]);
561
+ return {
562
+ reason: `${describe(tuple[0])} calls ${name} from ${module} with ${describe(tuple[1])} at argument ${position}, which gives back one of it`,
563
+ assumptions: [
564
+ `a pack declares that ${name} from ${module} gives back one of the class at argument ${position}`
565
+ ]
566
+ };
567
+ },
533
568
  "call result": ({ tuple, premises, describe, inline }) => {
534
569
  const invokes = premises[0];
535
570
  const invoked = invokes.tuple[1];
@@ -1016,6 +1051,57 @@ var RESOLUTION_RULES = [
1016
1051
  ],
1017
1052
  "class instance"
1018
1053
  ),
1054
+ // A finder the library declares, keyed on the base a pack named so a
1055
+ // project class with a method of the same name on another hierarchy is
1056
+ // left alone. The DESIGN says what a chain of them composes into.
1057
+ rule(
1058
+ "stepsTo",
1059
+ [v("r"), v("cls"), VALUE_STEP],
1060
+ [
1061
+ lit("call", v("r"), v("c")),
1062
+ lit("readsProperty", v("c"), v("o"), v("m")),
1063
+ lit("objectOf", v("o"), v("cls")),
1064
+ lit("objectValue", v("cls")),
1065
+ // Before `libraryBase`, so the demand rewrite asks it with the base
1066
+ // bound and walks an ancestry only for a method a pack declared.
1067
+ lit("givesBackOne", v("n"), v("m")),
1068
+ lit("libraryBase", v("cls"), v("n"))
1069
+ ],
1070
+ "declared finder"
1071
+ ),
1072
+ // The same word, for a library that takes the class as an argument
1073
+ // instead of as the receiver. The argument having to reach the base a
1074
+ // pack named is what keeps an unrelated `get` out.
1075
+ rule(
1076
+ "stepsTo",
1077
+ [v("r"), v("cls"), VALUE_STEP],
1078
+ [
1079
+ lit("call", v("r"), v("c")),
1080
+ lit("readsProperty", v("c"), v("o"), v("m")),
1081
+ lit("callArg", v("r"), v("k"), v("a")),
1082
+ // Before `libraryBase`, so the demand rewrite asks it with the base
1083
+ // bound and walks an ancestry only for a method a pack declared.
1084
+ lit("givesBackOneOfArgument", v("n"), v("m"), v("k")),
1085
+ lit("objectOf", v("a"), v("cls")),
1086
+ lit("libraryBase", v("cls"), v("n"))
1087
+ ],
1088
+ "declared argument finder"
1089
+ ),
1090
+ // The same again for a function called on its own rather than read off
1091
+ // anything, keyed on the module it was imported from so a project
1092
+ // function spelled the same way is not mistaken for it.
1093
+ rule(
1094
+ "stepsTo",
1095
+ [v("r"), v("cls"), VALUE_STEP],
1096
+ [
1097
+ lit("call", v("r"), v("c")),
1098
+ lit("comesFrom", v("c"), v("mod"), v("n")),
1099
+ lit("givesBackOneOfImport", v("mod"), v("n"), v("k")),
1100
+ lit("callArg", v("r"), v("k"), v("a")),
1101
+ lit("objectOf", v("a"), v("cls"))
1102
+ ],
1103
+ "declared import finder"
1104
+ ),
1019
1105
  // Wrapper transparency, derived: calling a factory that returns a
1020
1106
  // function which calls its parameter k steps to argument k.
1021
1107
  rule(
@@ -1289,6 +1375,54 @@ var RESOLUTION_RULES = [
1289
1375
  lit("contains", v("baseCls"), v("n"), v("held"))
1290
1376
  ]
1291
1377
  ),
1378
+ // An association is read off an instance as a property, and stating
1379
+ // it as `contains` is what puts it on the ancestry rule, so a concern
1380
+ // or a base class can be the one that declares it.
1381
+ rule(
1382
+ "contains",
1383
+ [v("cls"), v("n"), v("t")],
1384
+ [lit("declaresAssociation", v("cls"), v("n"), v("t"))],
1385
+ "declared association"
1386
+ ),
1387
+ // The same, for a language that buries the declaration in an ordinary
1388
+ // assignment. Keying on the module the callable came from is what
1389
+ // keeps a project function spelled the same way out.
1390
+ rule(
1391
+ "contains",
1392
+ [v("cls"), v("n"), v("t")],
1393
+ [
1394
+ lit("fieldCall", v("cls"), v("n"), v("c"), v("t")),
1395
+ lit("comesFrom", v("c"), v("m"), v("name")),
1396
+ lit("associationConstructor", v("m"), v("name"))
1397
+ ],
1398
+ "constructed association"
1399
+ ),
1400
+ // The library base a class's ancestry arrives at, however many of a
1401
+ // project's own classes sit in between. A base the run declares has a
1402
+ // node to keep walking from; one it does not has only a name.
1403
+ rule("libraryBase", [v("c"), v("n")], [lit("extendsNamed", v("c"), v("n"))]),
1404
+ rule(
1405
+ "libraryBase",
1406
+ [v("c"), v("n")],
1407
+ [
1408
+ lit("extends", v("c"), v("x")),
1409
+ lit("comesTo", v("x"), v("b")),
1410
+ lit("libraryBase", v("b"), v("n"))
1411
+ ]
1412
+ ),
1413
+ // A base a library hands back from a call rather than exporting as a
1414
+ // class, SQLAlchemy's `Base = declarative_base()`. The walk stops at
1415
+ // the call, so the function it called is the name to match on.
1416
+ rule(
1417
+ "libraryBase",
1418
+ [v("c"), v("n")],
1419
+ [
1420
+ lit("extends", v("c"), v("x")),
1421
+ lit("isWrittenAs", v("x"), v("r")),
1422
+ lit("call", v("r"), v("cc")),
1423
+ lit("comesFrom", v("cc"), v("m"), v("n"))
1424
+ ]
1425
+ ),
1292
1426
  // Which function a factory returns, and which of its parameters that
1293
1427
  // function calls: together they are what makes it a wrapper.
1294
1428
  rule(
@@ -1637,6 +1771,25 @@ var RESOLUTION_QUESTIONS = [
1637
1771
  [v("c"), v("n")],
1638
1772
  [lit("ancestryChain", v("c"), v("b")), lit("extendsNamed", v("b"), v("n"))]
1639
1773
  ),
1774
+ // Each method the same chain declares, so a caller that settled a
1775
+ // receiver on a class can tell a method the project writes itself
1776
+ // from one only the library provides.
1777
+ rule(
1778
+ "wantedDeclaredName",
1779
+ [v("c"), v("n")],
1780
+ [
1781
+ lit("ancestryChain", v("c"), v("b")),
1782
+ lit("holdsProperty", v("b"), v("n"), v("held"))
1783
+ ]
1784
+ ),
1785
+ // A method the class gets under a name the source computes rather than
1786
+ // writes out. The adapter settles the name against the same facts and
1787
+ // says so, and the chain reports it beside the ones written out.
1788
+ rule(
1789
+ "wantedDeclaredName",
1790
+ [v("c"), v("n")],
1791
+ [lit("ancestryChain", v("c"), v("b")), lit("declaresName", v("b"), v("n"))]
1792
+ ),
1640
1793
  rule("anchorChain", [v("x"), v("x")], [lit("wantedAnchor", v("x"))]),
1641
1794
  rule(
1642
1795
  "anchorChain",
@@ -1699,6 +1852,7 @@ export {
1699
1852
  calleeOutcomeOf,
1700
1853
  calleeOutcomes,
1701
1854
  checkFactContract,
1855
+ couldBeSettled,
1702
1856
  explainResolutionProof,
1703
1857
  explainResolvedKey,
1704
1858
  joinMountedPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/resolution",
3
- "version": "0.28.0",
3
+ "version": "0.30.0",
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.28.0"
59
+ "@suss/datalog": "0.30.0"
60
60
  }
61
61
  }