dsh-plugin-compat-check 0.1.5 → 0.1.7

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.
Files changed (46) hide show
  1. package/README.md +17 -28
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/index.js +1295 -550
  4. package/kernel/lib/api-semantics.mjs +223 -0
  5. package/kernel/lib/author-ci-budget.mjs +25 -0
  6. package/kernel/lib/author-ci-output.mjs +22 -0
  7. package/kernel/lib/author-ci-policy.mjs +6 -1
  8. package/kernel/lib/baseline.mjs +109 -38
  9. package/kernel/lib/catalog.mjs +11 -3
  10. package/kernel/lib/contracts.mjs +16 -1
  11. package/kernel/lib/execution-environment.mjs +52 -0
  12. package/kernel/lib/extract.mjs +57 -13
  13. package/kernel/lib/finding-guidance.mjs +6 -0
  14. package/kernel/lib/finding-review-store.mjs +11 -23
  15. package/kernel/lib/issue-report.mjs +2 -1
  16. package/kernel/lib/patch-mounts.mjs +232 -0
  17. package/kernel/lib/report-identity-worker.mjs +47 -26
  18. package/kernel/lib/report-identity.mjs +4 -3
  19. package/kernel/lib/report-service.mjs +118 -38
  20. package/kernel/lib/report.mjs +13 -17
  21. package/kernel/lib/source-analysis.mjs +258 -42
  22. package/kernel/lib/source-graph.mjs +353 -75
  23. package/kernel/lib/state-lock.mjs +206 -0
  24. package/kernel/lib/state-store.mjs +41 -30
  25. package/kernel/lib/static-input.mjs +111 -0
  26. package/kernel/lib/syntax.mjs +242 -20
  27. package/kernel/lib/upgrade-contract.mjs +1 -0
  28. package/kernel/lib/upgrade-input.mjs +9 -4
  29. package/kernel/lib/upgrade-preview.mjs +11 -15
  30. package/kernel/lib/upgrade-worker.mjs +10 -1
  31. package/kernel/lib/verdict.mjs +28 -5
  32. package/kernel/lib/version.mjs +2 -34
  33. package/kernel/lib/worker-analysis.mjs +74 -3
  34. package/kernel/rules/README.md +30 -208
  35. package/kernel/rules/dsh-0.2.1-rc.3-review.json +63 -0
  36. package/kernel/rules/dsh-api-catalog-0.2.1-rc.3.json +743 -0
  37. package/kernel/rules/severity.json +77 -0
  38. package/lib/client.js +4668 -3423
  39. package/lib/finding-guidance.js +6 -0
  40. package/lib/local-request.js +44 -0
  41. package/lib/report-contract.js +16 -1
  42. package/lib/upgrade-contract.js +1 -0
  43. package/package.json +10 -5
  44. package/scripts/prepublish-check.mjs +1 -22
  45. package/scripts/stage-contract.mjs +7 -0
  46. package/skill/SKILL.md +13 -12
@@ -1,5 +1,10 @@
1
1
  import { parseSource, childNodes } from "./syntax.mjs";
2
2
  import { hasEvidence, hasProof, mergedValue } from "./flow-values.mjs";
3
+ import {
4
+ captureSemanticArguments,
5
+ mergeSemanticArguments,
6
+ semanticTarget,
7
+ } from "./api-semantics.mjs";
3
8
 
4
9
  const members = new Set(["MemberExpression", "OptionalMemberExpression"]);
5
10
  const calls = new Set(["CallExpression", "OptionalCallExpression"]);
@@ -85,7 +90,8 @@ export function analyzeSources(
85
90
  const provideSiteKeys = new Set();
86
91
  let scopeId = 0,
87
92
  invocationCount = 0,
88
- constantCalls = 0;
93
+ constantCalls = 0,
94
+ semanticGeneration = 0;
89
95
  const identifierCache = new WeakMap();
90
96
  const guardSets = new Set();
91
97
  const guardSet = (values = []) => {
@@ -95,7 +101,7 @@ export function analyzeSources(
95
101
  };
96
102
  const serviceCache = new WeakMap();
97
103
  const stateFor = (value) => {
98
- if (value?.kind === "object") return value;
104
+ if (value?.kind === "object" || value?.kind === "literal") return value;
99
105
  if (value?.kind === "context" || value?.kind === "service")
100
106
  return (value.state ||= { fields: new Map(), opaque: false });
101
107
  return null;
@@ -208,6 +214,7 @@ export function analyzeSources(
208
214
  };
209
215
  const writeField = (object, key, value) => {
210
216
  noteMutation(object);
217
+ if (object.injectMetadata || object.kind === "literal") markOpaque(object);
211
218
  if (journals.length) object.fields = new Map(object.fields);
212
219
  object.fields.set(key, value);
213
220
  };
@@ -307,6 +314,7 @@ export function analyzeSources(
307
314
  };
308
315
  modules.set(file, module);
309
316
  const callResults = new WeakMap();
317
+ const semanticSnapshots = new WeakMap();
310
318
  const effects = new WeakMap();
311
319
  const hasEffect = (node, scope) => effects.get(node)?.has(scope.id);
312
320
  const effectValue = (node, scope) =>
@@ -353,6 +361,8 @@ export function analyzeSources(
353
361
  kind: "object",
354
362
  fields: new Map([["exports", cjsExports]]),
355
363
  };
364
+ const cjsInjectCandidates = [],
365
+ entryInjectCandidates = [];
356
366
  let currentDeferred = null;
357
367
  const imported = (spec, name) => ({
358
368
  kind: "import",
@@ -478,8 +488,12 @@ export function analyzeSources(
478
488
  !args.every((v) => v?.kind === "constant") ||
479
489
  !constantOnly(body) ||
480
490
  ++constantCalls > 512
481
- )
491
+ ) {
492
+ // Ordinary closures are not expanded. Their writes can invalidate
493
+ // scalar/options evidence without changing Context value-flow rules.
494
+ semanticGeneration++;
482
495
  return null;
496
+ }
483
497
  }
484
498
  if (invoking.has(key)) {
485
499
  diagnose(
@@ -577,7 +591,7 @@ export function analyzeSources(
577
591
  declared.add(service);
578
592
  if (kind === "optional-read" || kind === "read")
579
593
  softDeclared.add(service);
580
- dependencies.push({
594
+ const row = {
581
595
  service,
582
596
  kind,
583
597
  inferred,
@@ -586,7 +600,9 @@ export function analyzeSources(
586
600
  start: node.start,
587
601
  end: node.end,
588
602
  sides: [...(activeSides || sides)],
589
- });
603
+ };
604
+ dependencies.push(row);
605
+ return row;
590
606
  };
591
607
  const provide = (service, node) => {
592
608
  provided.add(service);
@@ -617,6 +633,24 @@ export function analyzeSources(
617
633
  return value === null ? [] : [value];
618
634
  });
619
635
  };
636
+ // Cordis Inject.resolve treats object keys as service names, not lists
637
+ // named required/optional. Unknown spreads/accessors cannot prove a map.
638
+ const injectServices = (node, scope, value = resolve(node, scope)) => {
639
+ if (value) value.injectMetadata = true;
640
+ if (value?.kind === "literal" && value.complete && !value.opaque)
641
+ return value.strings;
642
+ if (value?.kind === "object" && !value.opaque)
643
+ return [...value.fields.keys()].filter(
644
+ (key) => typeof key === "string",
645
+ );
646
+ diagnose(
647
+ "inject-unresolved",
648
+ file,
649
+ node,
650
+ "Injection declaration cannot be established as an array or service map",
651
+ );
652
+ return [];
653
+ };
620
654
  const keyFor = (node, scope, guards) =>
621
655
  node?.computed
622
656
  ? constant(node.property, scope, guards)
@@ -690,7 +724,7 @@ export function analyzeSources(
690
724
  node.type,
691
725
  )
692
726
  )
693
- return { kind: "constant", value: node.value };
727
+ return { kind: "constant", value: node.value, semanticGeneration };
694
728
  if (node.type === "TemplateLiteral") {
695
729
  const values = node.expressions.map((n) => resolve(n, scope, guards));
696
730
  if (values.every((v) => v?.kind === "constant")) {
@@ -707,7 +741,14 @@ export function analyzeSources(
707
741
  );
708
742
  return null;
709
743
  }
710
- return { kind: "constant", value: chunks.join("") };
744
+ return {
745
+ kind: "constant",
746
+ value: chunks.join(""),
747
+ semanticGeneration: values.reduce(
748
+ (min, v) => Math.min(min, v.semanticGeneration ?? -1),
749
+ semanticGeneration,
750
+ ),
751
+ };
711
752
  }
712
753
  }
713
754
  if (node.type === "BinaryExpression" && node.operator === "+") {
@@ -727,7 +768,14 @@ export function analyzeSources(
727
768
  );
728
769
  return null;
729
770
  }
730
- return { kind: "constant", value: left.value + right.value };
771
+ return {
772
+ kind: "constant",
773
+ value: left.value + right.value,
774
+ semanticGeneration: Math.min(
775
+ left.semanticGeneration ?? -1,
776
+ right.semanticGeneration ?? -1,
777
+ ),
778
+ };
731
779
  }
732
780
  }
733
781
  if (node.type === "ConditionalExpression") {
@@ -772,17 +820,51 @@ export function analyzeSources(
772
820
  }
773
821
  if (node.type === "ThisExpression")
774
822
  return scope.functionScope().receiver || { kind: "instance" };
775
- if (node.type === "ArrayExpression")
776
- return { kind: "literal", strings: literalNames(node, scope) };
823
+ if (node.type === "ArrayExpression") {
824
+ const strings = [];
825
+ let complete = true;
826
+ for (const element of node.elements) {
827
+ const spread = element?.type === "SpreadElement";
828
+ const value = resolve(
829
+ spread ? element.argument : element,
830
+ scope,
831
+ guards,
832
+ );
833
+ if (
834
+ spread &&
835
+ value?.kind === "literal" &&
836
+ value.complete &&
837
+ !value.opaque
838
+ )
839
+ strings.push(...value.strings);
840
+ else if (
841
+ !spread &&
842
+ value?.kind === "constant" &&
843
+ typeof value.value === "string"
844
+ )
845
+ strings.push(value.value);
846
+ else complete = false;
847
+ }
848
+ return {
849
+ kind: "literal",
850
+ strings,
851
+ complete,
852
+ fields: new Map(),
853
+ opaque: false,
854
+ node,
855
+ };
856
+ }
777
857
  if (node.type === "ObjectExpression") {
778
858
  const fields = new Map();
779
859
  for (const p of node.properties)
780
- if (p.type === "ObjectProperty")
781
- fields.set(
782
- p.computed ? constant(p.key, scope, guards) : keyName(p),
783
- resolve(p.value, scope, guards),
784
- );
785
- else if (p.type === "ObjectMethod")
860
+ if (p.type === "ObjectProperty") {
861
+ const key = p.computed
862
+ ? constant(p.key, scope, guards)
863
+ : keyName(p);
864
+ const value = resolve(p.value, scope, guards);
865
+ if (key === "inject" && value) value.injectMetadata = true;
866
+ fields.set(key, value);
867
+ } else if (p.type === "ObjectMethod")
786
868
  fields.set(
787
869
  p.computed ? constant(p.key, scope, guards) : keyName(p),
788
870
  fnValue(p, scope),
@@ -793,7 +875,7 @@ export function analyzeSources(
793
875
  (p.type === "ObjectMethod" && p.kind !== "method") ||
794
876
  (p.computed && constant(p.key, scope, guards) === null),
795
877
  );
796
- return { kind: "object", fields, opaque };
878
+ return { kind: "object", fields, opaque, node, semanticGeneration };
797
879
  }
798
880
  if (members.has(node.type)) {
799
881
  const object = transparent(resolve(node.object, scope, guards), node),
@@ -825,16 +907,14 @@ export function analyzeSources(
825
907
  if (value?.kind === "context")
826
908
  heads.add(text.slice(node.start, node.end));
827
909
  return value;
828
- return null;
829
910
  }
830
911
  if (calls.has(node.type)) {
831
912
  if (
832
913
  node.callee.type === "Identifier" &&
833
914
  node.callee.name === "require" &&
834
- !scope.find("require") &&
835
- node.arguments[0]?.type === "StringLiteral"
915
+ !scope.find("require")
836
916
  )
837
- return namespace(moduleLinks.get(file)?.get(node.arguments[0].value));
917
+ return namespace(moduleLinks.get(file)?.get("require@" + node.start));
838
918
  let memo = callResults.get(node);
839
919
  if (!memo) {
840
920
  memo = new Map();
@@ -853,6 +933,24 @@ export function analyzeSources(
853
933
  visit(arg, scope, guards);
854
934
  return resolve(arg, scope, guards);
855
935
  });
936
+ if (callee?.kind === "method") {
937
+ const captured = captureSemanticArguments(
938
+ callee.service,
939
+ callee.method,
940
+ values,
941
+ node.arguments,
942
+ (arg) =>
943
+ arg?.type === "Identifier" &&
944
+ arg.name === "undefined" &&
945
+ !scope.find("undefined"),
946
+ semanticGeneration,
947
+ );
948
+ if (captured) {
949
+ if (!semanticSnapshots.has(node))
950
+ semanticSnapshots.set(node, new Map());
951
+ semanticSnapshots.get(node).set(scope.id, captured);
952
+ }
953
+ }
856
954
  if (callee?.kind === "function") {
857
955
  const result = callee.invoke(
858
956
  values,
@@ -863,6 +961,17 @@ export function analyzeSources(
863
961
  memo.set(scope.id, result);
864
962
  return result;
865
963
  }
964
+ if (
965
+ !callee ||
966
+ !["method", "service"].includes(callee.kind) ||
967
+ (!semanticTarget(callee.service, callee.method) &&
968
+ values.some((v) =>
969
+ ["object", "literal", "function"].includes(v?.kind),
970
+ ))
971
+ )
972
+ semanticGeneration++;
973
+ if (receiver?.injectMetadata || receiver?.kind === "literal")
974
+ taint(receiver);
866
975
  }
867
976
  if (
868
977
  calls.has(node.type) &&
@@ -973,6 +1082,7 @@ export function analyzeSources(
973
1082
  condition = body[0].argument;
974
1083
  else if (
975
1084
  body[0]?.type === "IfStatement" &&
1085
+ !body[0].alternate &&
976
1086
  body[0].consequent.type === "ReturnStatement" &&
977
1087
  body[0].consequent.argument?.value === true &&
978
1088
  body.at(-1)?.type === "ReturnStatement" &&
@@ -1096,7 +1206,7 @@ export function analyzeSources(
1096
1206
  guards.has(`${value.service}.${value.method}`);
1097
1207
  if (!callMap.has(value.service)) callMap.set(value.service, new Set());
1098
1208
  callMap.get(value.service).add(value.method);
1099
- sites.push({
1209
+ const site = {
1100
1210
  service: value.service,
1101
1211
  method: value.method,
1102
1212
  plugin: pkgName,
@@ -1117,7 +1227,9 @@ export function analyzeSources(
1117
1227
  evidenceSource: "ast",
1118
1228
  inferredReceiver: value.receiver?.inferred === true,
1119
1229
  sides: [...(activeSides || sides)],
1120
- });
1230
+ };
1231
+ sites.push(site);
1232
+ return site;
1121
1233
  };
1122
1234
  const visitFunction = (
1123
1235
  node,
@@ -1346,6 +1458,13 @@ export function analyzeSources(
1346
1458
  }
1347
1459
  if (node.type === "Program")
1348
1460
  return statementList(node.body, scope, guards, depth);
1461
+ if (node.type === "StaticBlock")
1462
+ return statementList(
1463
+ node.body,
1464
+ new Scope(scope, ++scopeId, true),
1465
+ guards,
1466
+ depth,
1467
+ );
1349
1468
  if (node.type === "BlockStatement")
1350
1469
  return statementList(
1351
1470
  node.body,
@@ -1392,15 +1511,20 @@ export function analyzeSources(
1392
1511
  );
1393
1512
  if (node.id.type === "Identifier") {
1394
1513
  const field = node.id.name;
1395
- if (injectNames.has(field) || /^inject(?:Deps|Scope|s)?$/.test(field))
1396
- for (const s of literalNames(node.init, scope))
1397
- dependency(
1398
- s,
1399
- scope === root && isEntry && injectNames.has(field)
1400
- ? "required-inject"
1401
- : "descriptor-inject",
1402
- node,
1403
- );
1514
+ if (
1515
+ injectNames.has(field) ||
1516
+ /^inject(?:Deps|Scope|s)?$/.test(field)
1517
+ ) {
1518
+ const kind =
1519
+ scope === root && isEntry && injectNames.has(field)
1520
+ ? "required-inject"
1521
+ : "descriptor-inject";
1522
+ const rows = injectServices(node.init, scope, value).map((s) =>
1523
+ dependency(s, kind, node),
1524
+ );
1525
+ if (kind === "required-inject")
1526
+ entryInjectCandidates.push({ value, node, rows });
1527
+ }
1404
1528
  if (provideNames.has(field))
1405
1529
  for (const s of literalNames(node.init, scope)) {
1406
1530
  provide(s, node);
@@ -1458,7 +1582,16 @@ export function analyzeSources(
1458
1582
  "Unknown write key may replace existing fields",
1459
1583
  );
1460
1584
  } else {
1461
- writeField(state, slot.key, value);
1585
+ if (
1586
+ node.type === "UnaryExpression" &&
1587
+ node.operator === "delete" &&
1588
+ slot.object.kind === "object"
1589
+ ) {
1590
+ noteMutation(state);
1591
+ if (state.injectMetadata) markOpaque(state);
1592
+ if (journals.length) state.fields = new Map(state.fields);
1593
+ state.fields.delete(slot.key);
1594
+ } else writeField(state, slot.key, value);
1462
1595
  if (["context", "service"].includes(slot.object.kind))
1463
1596
  diagnose(
1464
1597
  "flow-service-mutation",
@@ -1523,11 +1656,19 @@ export function analyzeSources(
1523
1656
  ) {
1524
1657
  const result = slot.old.value + right.value;
1525
1658
  if (typeof result !== "string" || result.length <= 8192)
1526
- value = { kind: "constant", value: result };
1659
+ value = {
1660
+ kind: "constant",
1661
+ value: result,
1662
+ semanticGeneration: Math.min(
1663
+ slot.old.semanticGeneration ?? -1,
1664
+ right.semanticGeneration ?? -1,
1665
+ ),
1666
+ };
1527
1667
  }
1528
1668
  } else if (slot.old?.kind === "constant")
1529
1669
  value = {
1530
1670
  kind: "constant",
1671
+ semanticGeneration: slot.old.semanticGeneration ?? -1,
1531
1672
  value: Number(slot.old.value) + (node.operator === "++" ? 1 : -1),
1532
1673
  };
1533
1674
  if (!value && node.operator !== "=")
@@ -1543,18 +1684,28 @@ export function analyzeSources(
1543
1684
  scope,
1544
1685
  node.type === "UpdateExpression" && !node.prefix ? slot.old : value,
1545
1686
  );
1546
- if (node.type === "AssignmentExpression" && slot.key === "inject")
1547
- for (const name of literalNames(node.right, scope))
1548
- dependency(name, "descriptor-inject", node);
1687
+ if (node.type === "AssignmentExpression" && slot.key === "inject") {
1688
+ const rows = injectServices(node.right, scope, value).map((name) =>
1689
+ dependency(name, "descriptor-inject", node),
1690
+ );
1691
+ cjsInjectCandidates.push({ owner: slot.object, value, node, rows });
1692
+ }
1549
1693
  if (node.type === "AssignmentExpression" && slot.key === "provide")
1550
1694
  for (const name of literalNames(node.right, scope)) {
1551
1695
  provide(name, node);
1552
1696
  }
1553
1697
  return;
1554
1698
  }
1555
- if (node.type === "ObjectProperty" && keyName(node) === "inject")
1556
- for (const s of literalNames(node.value, scope))
1557
- dependency(s, "descriptor-inject", node);
1699
+ if (node.type === "ObjectProperty" && keyName(node) === "inject") {
1700
+ const rows = injectServices(node.value, scope).map((name) =>
1701
+ dependency(name, "descriptor-inject", node),
1702
+ );
1703
+ cjsInjectCandidates.push({
1704
+ value: resolve(node.value, scope),
1705
+ node,
1706
+ rows,
1707
+ });
1708
+ }
1558
1709
  if (node.type === "CatchClause") {
1559
1710
  const caught = new Scope(scope, ++scopeId);
1560
1711
  bind(node.param, null, caught);
@@ -1706,8 +1857,10 @@ export function analyzeSources(
1706
1857
  return;
1707
1858
  }
1708
1859
  if (calls.has(node.type)) {
1709
- recordCall(node, scope, guards);
1860
+ const site = recordCall(node, scope, guards);
1710
1861
  resolve(node, scope, guards);
1862
+ const captured = semanticSnapshots.get(node)?.get(scope.id);
1863
+ if (site && captured) site.semanticArguments = captured;
1711
1864
  let target = resolve(node.callee, scope, guards);
1712
1865
  if (target?.kind === "namespace")
1713
1866
  target = lookupExport(target.target, "default");
@@ -1797,7 +1950,16 @@ export function analyzeSources(
1797
1950
  for (const v of d.declarations)
1798
1951
  for (const name of namesIn(v.id))
1799
1952
  module.exports.set(name, { local: name });
1800
- for (const spec of n.specifiers || [])
1953
+ for (const spec of n.specifiers || []) {
1954
+ if (!spec.local) {
1955
+ diagnose(
1956
+ "module-export-uncertain",
1957
+ file,
1958
+ spec,
1959
+ "Namespace re-export binding is not established",
1960
+ );
1961
+ continue;
1962
+ }
1801
1963
  module.exports.set(
1802
1964
  spec.exported.name ?? spec.exported.value,
1803
1965
  n.source
@@ -1807,6 +1969,7 @@ export function analyzeSources(
1807
1969
  }
1808
1970
  : { local: spec.local.name },
1809
1971
  );
1972
+ }
1810
1973
  }
1811
1974
  if (n.type === "ExportAllDeclaration") module.stars.push(n.source.value);
1812
1975
  }
@@ -1817,6 +1980,48 @@ export function analyzeSources(
1817
1980
  module.cjs = () => cjsModule.fields.get("exports");
1818
1981
  module.entry = () => {
1819
1982
  if (!isEntry) return;
1983
+ for (const candidate of entryInjectCandidates) {
1984
+ if (!candidate.value?.opaque) continue;
1985
+ diagnose(
1986
+ "inject-unresolved",
1987
+ file,
1988
+ candidate.node,
1989
+ "Injection metadata was mutated or escaped before activation",
1990
+ );
1991
+ for (const row of candidate.rows) {
1992
+ row.kind = "descriptor-inject";
1993
+ row.inferred = true;
1994
+ }
1995
+ }
1996
+ const exported = module.cjs();
1997
+ const injected = readField(exported, "inject", program);
1998
+ if (injected?.opaque || (exported?.opaque && cjsInjectCandidates.length))
1999
+ diagnose(
2000
+ "inject-unresolved",
2001
+ file,
2002
+ program,
2003
+ "CommonJS injection metadata or its export owner was mutated or escaped",
2004
+ );
2005
+ if (
2006
+ exported?.kind === "object" &&
2007
+ !exported.opaque &&
2008
+ injected &&
2009
+ !injected.opaque
2010
+ ) {
2011
+ for (const candidate of cjsInjectCandidates) {
2012
+ const sameOwner =
2013
+ candidate.owner === exported ||
2014
+ exported.node?.properties?.includes(candidate.node);
2015
+ const sameInput =
2016
+ candidate.value === injected ||
2017
+ (injected.node && candidate.value?.node === injected.node);
2018
+ if (!sameOwner || !sameInput) continue;
2019
+ for (const row of candidate.rows) {
2020
+ row.kind = "required-inject";
2021
+ row.inferred = false;
2022
+ }
2023
+ }
2024
+ }
1820
2025
  const apply = lookupExport(file, "apply");
1821
2026
  if (apply?.kind !== "function") return;
1822
2027
  const guards = guardSet();
@@ -1873,6 +2078,17 @@ export function analyzeSources(
1873
2078
  const old = unique.get(key);
1874
2079
  if (!old) unique.set(key, site);
1875
2080
  else {
2081
+ if (old.semanticArguments || site.semanticArguments) {
2082
+ if (old.inferredReceiver && !site.inferredReceiver)
2083
+ old.semanticArguments = site.semanticArguments;
2084
+ else if (!old.inferredReceiver && site.inferredReceiver) {
2085
+ /* Preserve the proved call. */
2086
+ } else
2087
+ old.semanticArguments = mergeSemanticArguments(
2088
+ old.semanticArguments,
2089
+ site.semanticArguments,
2090
+ );
2091
+ }
1876
2092
  old.inferredReceiver = old.inferredReceiver && site.inferredReceiver;
1877
2093
  old.guarded = old.guarded && site.guarded;
1878
2094
  old.capabilityGuarded = old.capabilityGuarded && site.capabilityGuarded;