unguard 0.11.0 → 0.12.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/README.md CHANGED
@@ -165,6 +165,7 @@ These rules use the TypeScript type checker. Non-nullable types suppress the dia
165
165
  | `unused-export` | info | Exported function with no usages in the project |
166
166
  | `optional-arg-always-used` | warning | Optional param provided at every call site |
167
167
  | `explicit-null-arg` | warning | `fn(null)` / `fn(undefined)` to project functions |
168
+ | `dead-overload` | warning | Overload signature with zero matching project call sites |
168
169
 
169
170
  ### Imports
170
171
 
@@ -1304,6 +1304,177 @@ var duplicateStatementSequence = {
1304
1304
  }
1305
1305
  };
1306
1306
 
1307
+ // src/rules/cross-file/dead-overload.ts
1308
+ import * as ts28 from "typescript";
1309
+ var deadOverload = {
1310
+ id: "dead-overload",
1311
+ severity: "warning",
1312
+ message: "Overload signature has no matching call sites in the project",
1313
+ analyze(project) {
1314
+ const diagnostics = [];
1315
+ for (const [file, { sourceFile }] of project.files) {
1316
+ for (const family of collectOverloadFamilies(sourceFile, file)) {
1317
+ const matchedOverloads = /* @__PURE__ */ new Set();
1318
+ for (const site of project.callSites) {
1319
+ const declaration = site.resolvedDeclaration;
1320
+ if (declaration === void 0) continue;
1321
+ if (family.overloads.includes(declaration)) {
1322
+ matchedOverloads.add(declaration);
1323
+ }
1324
+ }
1325
+ if (matchedOverloads.size === 0) continue;
1326
+ const deadOverloads = family.overloads.filter((overload) => !matchedOverloads.has(overload));
1327
+ const liveOverloads = family.overloads.filter((overload) => matchedOverloads.has(overload));
1328
+ for (const overload of deadOverloads) {
1329
+ diagnostics.push({
1330
+ ruleId: this.id,
1331
+ severity: this.severity,
1332
+ message: buildMessage(family, overload, deadOverloads, liveOverloads),
1333
+ file,
1334
+ line: lineOf(sourceFile, overload),
1335
+ column: 1
1336
+ });
1337
+ }
1338
+ }
1339
+ }
1340
+ return diagnostics;
1341
+ }
1342
+ };
1343
+ function buildMessage(family, overload, deadOverloads, liveOverloads) {
1344
+ const base = `Overload signature for "${family.name}" has no matching call sites in the project`;
1345
+ if (!shouldMentionCascade(family, overload, deadOverloads, liveOverloads)) {
1346
+ return base;
1347
+ }
1348
+ return `${base}; removing it may let you collapse to the remaining constrained signature and drop implementation casts`;
1349
+ }
1350
+ function shouldMentionCascade(family, overload, deadOverloads, liveOverloads) {
1351
+ if (deadOverloads.length !== 1 || liveOverloads.length !== 1) return false;
1352
+ if (deadOverloads[0] !== overload) return false;
1353
+ const liveOverload = liveOverloads[0];
1354
+ if (liveOverload === void 0) return false;
1355
+ if (family.implementation.typeParameters === void 0 || liveOverload.typeParameters === void 0) return false;
1356
+ const body = family.implementation.body;
1357
+ if (body === void 0) return false;
1358
+ const implementationTypeParams = family.implementation.typeParameters;
1359
+ const liveTypeParams = liveOverload.typeParameters;
1360
+ if (implementationTypeParams.length === 0 || implementationTypeParams.length !== liveTypeParams.length) return false;
1361
+ const constrainedParams = implementationTypeParams.flatMap((typeParam, index) => {
1362
+ const liveTypeParam = liveTypeParams[index];
1363
+ if (liveTypeParam === void 0) return [];
1364
+ if (typeParam.constraint !== void 0) return [];
1365
+ if (liveTypeParam.constraint === void 0) return [];
1366
+ return [{
1367
+ paramName: typeParam.name.text,
1368
+ constraintText: normalizeText(liveTypeParam.constraint.getText(family.sourceFile))
1369
+ }];
1370
+ });
1371
+ if (constrainedParams.length === 0) return false;
1372
+ return constrainedParams.some(
1373
+ ({ paramName, constraintText }) => hasIntersectionCast(body, family.sourceFile, paramName, constraintText)
1374
+ );
1375
+ }
1376
+ function hasIntersectionCast(body, sourceFile, paramName, constraintText) {
1377
+ let found = false;
1378
+ function visit(node) {
1379
+ if (found) return;
1380
+ if (ts28.isAsExpression(node) || ts28.isTypeAssertionExpression(node)) {
1381
+ if (isConstraintIntersection(node.type, sourceFile, paramName, constraintText)) {
1382
+ found = true;
1383
+ return;
1384
+ }
1385
+ }
1386
+ ts28.forEachChild(node, visit);
1387
+ }
1388
+ visit(body);
1389
+ return found;
1390
+ }
1391
+ function isConstraintIntersection(typeNode, sourceFile, paramName, constraintText) {
1392
+ const target = unwrapParenthesizedType(typeNode);
1393
+ if (!ts28.isIntersectionTypeNode(target)) return false;
1394
+ let hasParam = false;
1395
+ let hasConstraint = false;
1396
+ for (const part of target.types) {
1397
+ const text = normalizeText(unwrapParenthesizedType(part).getText(sourceFile));
1398
+ if (text === paramName) hasParam = true;
1399
+ if (text === constraintText) hasConstraint = true;
1400
+ }
1401
+ return hasParam && hasConstraint;
1402
+ }
1403
+ function unwrapParenthesizedType(typeNode) {
1404
+ let current = typeNode;
1405
+ while (ts28.isParenthesizedTypeNode(current)) {
1406
+ current = current.type;
1407
+ }
1408
+ return current;
1409
+ }
1410
+ function collectOverloadFamilies(sourceFile, file) {
1411
+ const families = [];
1412
+ collectFromList(sourceFile.statements, sourceFile, file, families);
1413
+ function visit(node) {
1414
+ if (ts28.isClassDeclaration(node) || ts28.isClassExpression(node)) {
1415
+ collectFromList(node.members, sourceFile, file, families);
1416
+ }
1417
+ ts28.forEachChild(node, visit);
1418
+ }
1419
+ ts28.forEachChild(sourceFile, visit);
1420
+ return families;
1421
+ }
1422
+ function collectFromList(nodes, sourceFile, file, families) {
1423
+ for (let index = 0; index < nodes.length; index++) {
1424
+ const current = asOverloadDeclaration(nodes[index]);
1425
+ const name = current ? getDeclarationName(current) : null;
1426
+ if (current === null || name === null) continue;
1427
+ const run = [current];
1428
+ let nextIndex = index + 1;
1429
+ while (nextIndex < nodes.length) {
1430
+ const next = asOverloadDeclaration(nodes[nextIndex]);
1431
+ if (next === null) break;
1432
+ if (getDeclarationName(next) !== name) break;
1433
+ run.push(next);
1434
+ nextIndex++;
1435
+ }
1436
+ const family = toOverloadFamily(run, name, file, sourceFile);
1437
+ if (family !== null) {
1438
+ families.push(family);
1439
+ }
1440
+ index = nextIndex - 1;
1441
+ }
1442
+ }
1443
+ function toOverloadFamily(run, name, file, sourceFile) {
1444
+ if (run.length < 2) return null;
1445
+ const implementation = run.at(-1);
1446
+ if (implementation === void 0 || implementation.body === void 0) return null;
1447
+ if (run.slice(0, -1).some((declaration) => declaration.body !== void 0)) return null;
1448
+ return {
1449
+ name,
1450
+ file,
1451
+ sourceFile,
1452
+ overloads: run.slice(0, -1),
1453
+ implementation
1454
+ };
1455
+ }
1456
+ function asOverloadDeclaration(node) {
1457
+ if (node === void 0) return null;
1458
+ if (ts28.isFunctionDeclaration(node)) return node;
1459
+ if (ts28.isMethodDeclaration(node)) return node;
1460
+ return null;
1461
+ }
1462
+ function getDeclarationName(node) {
1463
+ if (ts28.isFunctionDeclaration(node)) {
1464
+ return node.name?.text ?? null;
1465
+ }
1466
+ if (ts28.isIdentifier(node.name) || ts28.isStringLiteral(node.name) || ts28.isNumericLiteral(node.name)) {
1467
+ return node.name.text;
1468
+ }
1469
+ return null;
1470
+ }
1471
+ function lineOf(sourceFile, node) {
1472
+ return ts28.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
1473
+ }
1474
+ function normalizeText(text) {
1475
+ return text.replace(/\s+/g, "");
1476
+ }
1477
+
1307
1478
  // src/rules/index.ts
1308
1479
  var allRules = [
1309
1480
  noEmptyCatch,
@@ -1338,7 +1509,8 @@ var allRules = [
1338
1509
  trivialWrapper,
1339
1510
  unusedExport,
1340
1511
  duplicateFile,
1341
- duplicateStatementSequence
1512
+ duplicateStatementSequence,
1513
+ deadOverload
1342
1514
  ];
1343
1515
  var ruleMetadata = {
1344
1516
  "no-any-cast": { category: "type-evasion", tags: ["safety"] },
@@ -1373,7 +1545,8 @@ var ruleMetadata = {
1373
1545
  "trivial-wrapper": { category: "cross-file", tags: ["duplicate"] },
1374
1546
  "unused-export": { category: "cross-file", tags: ["api"] },
1375
1547
  "duplicate-file": { category: "cross-file", tags: ["duplicate"] },
1376
- "duplicate-statement-sequence": { category: "cross-file", tags: ["duplicate"] }
1548
+ "duplicate-statement-sequence": { category: "cross-file", tags: ["duplicate"] },
1549
+ "dead-overload": { category: "cross-file", tags: ["api", "type-evasion"] }
1377
1550
  };
1378
1551
  function getRuleMetadata(ruleId) {
1379
1552
  const metadata = ruleMetadata[ruleId];
@@ -1433,32 +1606,32 @@ function isFailOn(value) {
1433
1606
  }
1434
1607
 
1435
1608
  // src/collect/index.ts
1436
- import * as ts30 from "typescript";
1609
+ import * as ts31 from "typescript";
1437
1610
  import { createHash as createHash2 } from "crypto";
1438
1611
 
1439
1612
  // src/utils/hash.ts
1440
1613
  import { createHash } from "crypto";
1441
- import * as ts28 from "typescript";
1614
+ import * as ts29 from "typescript";
1442
1615
  function hashTypeShape(node, sourceFile) {
1443
1616
  const normalized = normalizeTypeNode(node, sourceFile);
1444
1617
  return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
1445
1618
  }
1446
1619
  function normalizeTypeNode(node, sourceFile) {
1447
- if (ts28.isTypeLiteralNode(node)) {
1620
+ if (ts29.isTypeLiteralNode(node)) {
1448
1621
  const normalized = node.members.map((m) => normalizeTypeNode(m, sourceFile)).sort().join(";");
1449
1622
  return `{${normalized}}`;
1450
1623
  }
1451
- if (ts28.isInterfaceDeclaration(node)) {
1624
+ if (ts29.isInterfaceDeclaration(node)) {
1452
1625
  const normalized = node.members.map((m) => normalizeTypeNode(m, sourceFile)).sort().join(";");
1453
1626
  return `{${normalized}}`;
1454
1627
  }
1455
- if (ts28.isPropertySignature(node)) {
1628
+ if (ts29.isPropertySignature(node)) {
1456
1629
  const keyName = node.name.getText(sourceFile);
1457
1630
  const optional = node.questionToken ? "?" : "";
1458
1631
  const type = node.type ? normalizeTypeNode(node.type, sourceFile) : "any";
1459
1632
  return `${keyName}${optional}:${type}`;
1460
1633
  }
1461
- if (ts28.isTypeAliasDeclaration(node)) {
1634
+ if (ts29.isTypeAliasDeclaration(node)) {
1462
1635
  return normalizeTypeNode(node.type, sourceFile);
1463
1636
  }
1464
1637
  return node.getText(sourceFile).replace(/\s+/g, " ").trim();
@@ -1491,7 +1664,7 @@ function normalizeBody(node, sourceFile, paramNames) {
1491
1664
  text = text.replace(/\s+/g, " ").trim();
1492
1665
  return text;
1493
1666
  }
1494
- function normalizeText(text) {
1667
+ function normalizeText2(text) {
1495
1668
  let t = text.replace(/\/\/[^\n]*/g, "");
1496
1669
  t = t.replace(/\/\*[\s\S]*?\*\//g, "");
1497
1670
  t = t.replace(/"(?:[^"\\]|\\.)*"/g, '"__STR__"');
@@ -1624,7 +1797,7 @@ var InlineParamTypeRegistry = class extends BaseRegistry {
1624
1797
  };
1625
1798
 
1626
1799
  // src/typecheck/walk.ts
1627
- import * as ts29 from "typescript";
1800
+ import * as ts30 from "typescript";
1628
1801
  function buildContext(rule, sourceFile, checker, source, filename, diagnostics) {
1629
1802
  return {
1630
1803
  filename,
@@ -1632,7 +1805,7 @@ function buildContext(rule, sourceFile, checker, source, filename, diagnostics)
1632
1805
  sourceFile,
1633
1806
  checker,
1634
1807
  report(node, message) {
1635
- const { line, character } = ts29.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
1808
+ const { line, character } = ts30.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
1636
1809
  diagnostics.push({
1637
1810
  ruleId: rule.id,
1638
1811
  severity: rule.severity,
@@ -1643,7 +1816,7 @@ function buildContext(rule, sourceFile, checker, source, filename, diagnostics)
1643
1816
  });
1644
1817
  },
1645
1818
  reportAtOffset(offset, message) {
1646
- const { line, character } = ts29.getLineAndCharacterOfPosition(sourceFile, offset);
1819
+ const { line, character } = ts30.getLineAndCharacterOfPosition(sourceFile, offset);
1647
1820
  diagnostics.push({
1648
1821
  ruleId: rule.id,
1649
1822
  severity: rule.severity,
@@ -1694,7 +1867,7 @@ function collectProject(program, tsRules, allowedFiles) {
1694
1867
  rule.visit(node, ctx);
1695
1868
  }
1696
1869
  }
1697
- ts30.forEachChild(node, visit2);
1870
+ ts31.forEachChild(node, visit2);
1698
1871
  };
1699
1872
  var visit = visit2;
1700
1873
  const file = sourceFile.fileName;
@@ -1708,7 +1881,7 @@ function collectProject(program, tsRules, allowedFiles) {
1708
1881
  rule,
1709
1882
  ctx: buildContext(rule, sourceFile, checker, source, file, diagnostics)
1710
1883
  }));
1711
- ts30.forEachChild(sourceFile, visit2);
1884
+ ts31.forEachChild(sourceFile, visit2);
1712
1885
  }
1713
1886
  const fileHashes = /* @__PURE__ */ new Map();
1714
1887
  for (const [file, { source }] of fileMap) {
@@ -1724,52 +1897,52 @@ function collectProject(program, tsRules, allowedFiles) {
1724
1897
  return { index: { types, functions, constants, callSites, imports, files: fileMap, fileHashes, statementSequences, inlineParamTypes }, diagnostics };
1725
1898
  }
1726
1899
  function hasNonPublicModifier(node) {
1727
- if (!ts30.canHaveModifiers(node)) return false;
1728
- const mods = ts30.getModifiers(node);
1900
+ if (!ts31.canHaveModifiers(node)) return false;
1901
+ const mods = ts31.getModifiers(node);
1729
1902
  if (mods === void 0) return false;
1730
1903
  return mods.some(
1731
- (m) => m.kind === ts30.SyntaxKind.PrivateKeyword || m.kind === ts30.SyntaxKind.ProtectedKeyword
1904
+ (m) => m.kind === ts31.SyntaxKind.PrivateKeyword || m.kind === ts31.SyntaxKind.ProtectedKeyword
1732
1905
  );
1733
1906
  }
1734
1907
  function isExported(node) {
1735
- if (!ts30.canHaveModifiers(node)) return false;
1736
- const mods = ts30.getModifiers(node);
1908
+ if (!ts31.canHaveModifiers(node)) return false;
1909
+ const mods = ts31.getModifiers(node);
1737
1910
  if (mods === void 0) return false;
1738
- return mods.some((m) => m.kind === ts30.SyntaxKind.ExportKeyword);
1911
+ return mods.some((m) => m.kind === ts31.SyntaxKind.ExportKeyword);
1739
1912
  }
1740
1913
  function collectTypes(node, file, sourceFile, registry) {
1741
- if (ts30.isTypeAliasDeclaration(node)) {
1742
- const line = ts30.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
1914
+ if (ts31.isTypeAliasDeclaration(node)) {
1915
+ const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
1743
1916
  registry.add(node.name.text, file, line, node.type, sourceFile, isExported(node));
1744
1917
  }
1745
- if (ts30.isInterfaceDeclaration(node)) {
1746
- const line = ts30.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
1918
+ if (ts31.isInterfaceDeclaration(node)) {
1919
+ const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
1747
1920
  registry.add(node.name.text, file, line, node, sourceFile, isExported(node));
1748
1921
  }
1749
1922
  }
1750
1923
  function isConstantValue(node) {
1751
- if (ts30.isStringLiteral(node)) return true;
1752
- if (ts30.isNumericLiteral(node)) return true;
1753
- if (ts30.isNoSubstitutionTemplateLiteral(node)) return true;
1754
- if (ts30.isPrefixUnaryExpression(node)) return isConstantValue(node.operand);
1755
- if (ts30.isBinaryExpression(node)) return isConstantValue(node.left) && isConstantValue(node.right);
1924
+ if (ts31.isStringLiteral(node)) return true;
1925
+ if (ts31.isNumericLiteral(node)) return true;
1926
+ if (ts31.isNoSubstitutionTemplateLiteral(node)) return true;
1927
+ if (ts31.isPrefixUnaryExpression(node)) return isConstantValue(node.operand);
1928
+ if (ts31.isBinaryExpression(node)) return isConstantValue(node.left) && isConstantValue(node.right);
1756
1929
  return false;
1757
1930
  }
1758
1931
  function collectConstants(node, file, sourceFile, registry) {
1759
- if (!ts30.isVariableStatement(node)) return;
1760
- if (!(node.declarationList.flags & ts30.NodeFlags.Const)) return;
1932
+ if (!ts31.isVariableStatement(node)) return;
1933
+ if (!(node.declarationList.flags & ts31.NodeFlags.Const)) return;
1761
1934
  const exported = isExported(node);
1762
1935
  for (const decl of node.declarationList.declarations) {
1763
- if (!decl.initializer || !ts30.isIdentifier(decl.name)) continue;
1936
+ if (!decl.initializer || !ts31.isIdentifier(decl.name)) continue;
1764
1937
  if (!isConstantValue(decl.initializer)) continue;
1765
1938
  const valueText = decl.initializer.getText(sourceFile).replace(/\s+/g, " ").trim();
1766
1939
  const valueHash = createHash2("sha256").update(valueText).digest("hex").slice(0, 16);
1767
- const line = ts30.getLineAndCharacterOfPosition(sourceFile, decl.getStart(sourceFile)).line + 1;
1940
+ const line = ts31.getLineAndCharacterOfPosition(sourceFile, decl.getStart(sourceFile)).line + 1;
1768
1941
  registry.add({ name: decl.name.text, file, line, valueHash, valueText, exported });
1769
1942
  }
1770
1943
  }
1771
1944
  function buildFunctionEntry(body, parameters, sourceFile, file, lineNode, name, extra) {
1772
- const line = ts30.getLineAndCharacterOfPosition(sourceFile, lineNode.getStart(sourceFile)).line + 1;
1945
+ const line = ts31.getLineAndCharacterOfPosition(sourceFile, lineNode.getStart(sourceFile)).line + 1;
1773
1946
  const params = extractParams(parameters, sourceFile);
1774
1947
  const paramNames = params.map((p) => p.name);
1775
1948
  const hash = hashFunctionBody(body, sourceFile);
@@ -1791,17 +1964,17 @@ function buildFunctionEntry(body, parameters, sourceFile, file, lineNode, name,
1791
1964
  };
1792
1965
  }
1793
1966
  function collectFunctions(node, file, sourceFile, checker, registry) {
1794
- if (ts30.isFunctionDeclaration(node) && node.name && node.body) {
1967
+ if (ts31.isFunctionDeclaration(node) && node.name && node.body) {
1795
1968
  registry.add(buildFunctionEntry(node.body, node.parameters, sourceFile, file, node, node.name.text, {
1796
1969
  exported: isExported(node),
1797
1970
  symbol: checker.getSymbolAtLocation(node.name),
1798
1971
  node
1799
1972
  }));
1800
1973
  }
1801
- if (ts30.isVariableStatement(node)) {
1974
+ if (ts31.isVariableStatement(node)) {
1802
1975
  const exported = isExported(node);
1803
1976
  for (const decl of node.declarationList.declarations) {
1804
- if (decl.initializer && ts30.isArrowFunction(decl.initializer) && ts30.isIdentifier(decl.name)) {
1977
+ if (decl.initializer && ts31.isArrowFunction(decl.initializer) && ts31.isIdentifier(decl.name)) {
1805
1978
  const arrow = decl.initializer;
1806
1979
  registry.add(buildFunctionEntry(arrow.body, arrow.parameters, sourceFile, file, decl, decl.name.text, {
1807
1980
  exported,
@@ -1809,7 +1982,7 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
1809
1982
  node: arrow
1810
1983
  }));
1811
1984
  }
1812
- if (decl.initializer && ts30.isFunctionExpression(decl.initializer) && ts30.isIdentifier(decl.name)) {
1985
+ if (decl.initializer && ts31.isFunctionExpression(decl.initializer) && ts31.isIdentifier(decl.name)) {
1813
1986
  const fn = decl.initializer;
1814
1987
  if (fn.body) {
1815
1988
  registry.add(buildFunctionEntry(fn.body, fn.parameters, sourceFile, file, decl, decl.name.text, {
@@ -1821,22 +1994,22 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
1821
1994
  }
1822
1995
  }
1823
1996
  }
1824
- if (ts30.isPropertyAssignment(node) && (ts30.isIdentifier(node.name) || ts30.isStringLiteral(node.name))) {
1997
+ if (ts31.isPropertyAssignment(node) && (ts31.isIdentifier(node.name) || ts31.isStringLiteral(node.name))) {
1825
1998
  const init = node.initializer;
1826
1999
  const propName = node.name.text;
1827
- if (ts30.isArrowFunction(init)) {
2000
+ if (ts31.isArrowFunction(init)) {
1828
2001
  registry.add(buildFunctionEntry(init.body, init.parameters, sourceFile, file, node, propName, { node: init }));
1829
2002
  }
1830
- if (ts30.isFunctionExpression(init) && init.body) {
2003
+ if (ts31.isFunctionExpression(init) && init.body) {
1831
2004
  registry.add(buildFunctionEntry(init.body, init.parameters, sourceFile, file, node, propName, { node: init }));
1832
2005
  }
1833
2006
  }
1834
- if (ts30.isArrowFunction(node) || ts30.isFunctionExpression(node)) {
2007
+ if (ts31.isArrowFunction(node) || ts31.isFunctionExpression(node)) {
1835
2008
  const parent = node.parent;
1836
- if (ts30.isVariableDeclaration(parent) && ts30.isIdentifier(parent.name)) {
1837
- } else if (ts30.isPropertyAssignment(parent)) {
2009
+ if (ts31.isVariableDeclaration(parent) && ts31.isIdentifier(parent.name)) {
2010
+ } else if (ts31.isPropertyAssignment(parent)) {
1838
2011
  } else {
1839
- const body = ts30.isArrowFunction(node) ? node.body : node.body;
2012
+ const body = ts31.isArrowFunction(node) ? node.body : node.body;
1840
2013
  if (body) {
1841
2014
  const MIN_ANON_BODY = 64;
1842
2015
  if (bodyTextLength(body, sourceFile) >= MIN_ANON_BODY) {
@@ -1846,14 +2019,14 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
1846
2019
  }
1847
2020
  }
1848
2021
  }
1849
- if (ts30.isMethodDeclaration(node) && node.body && ts30.isIdentifier(node.name)) {
2022
+ if (ts31.isMethodDeclaration(node) && node.body && ts31.isIdentifier(node.name)) {
1850
2023
  const parent = node.parent;
1851
- if (ts30.isClassDeclaration(parent)) {
2024
+ if (ts31.isClassDeclaration(parent)) {
1852
2025
  if (hasNonPublicModifier(node)) return;
1853
2026
  const className = parent.name ? parent.name.text : "<anonymous>";
1854
2027
  const name = `${className}.${node.name.text}`;
1855
2028
  const implementsInterface = parent.heritageClauses?.some(
1856
- (c) => c.token === ts30.SyntaxKind.ImplementsKeyword
2029
+ (c) => c.token === ts31.SyntaxKind.ImplementsKeyword
1857
2030
  ) ?? false;
1858
2031
  registry.add(buildFunctionEntry(node.body, node.parameters, sourceFile, file, node, name, {
1859
2032
  exported: isExported(parent),
@@ -1876,16 +2049,16 @@ function extractParams(parameters, sourceFile) {
1876
2049
  }
1877
2050
  function deriveAnonymousName(node, sourceFile) {
1878
2051
  const parent = node.parent;
1879
- const line = ts30.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
1880
- if (ts30.isCallExpression(parent)) {
2052
+ const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2053
+ if (ts31.isCallExpression(parent)) {
1881
2054
  const grandparent = parent.parent;
1882
- if (ts30.isPropertyAssignment(grandparent) && ts30.isIdentifier(grandparent.name)) {
2055
+ if (ts31.isPropertyAssignment(grandparent) && ts31.isIdentifier(grandparent.name)) {
1883
2056
  return grandparent.name.text;
1884
2057
  }
1885
2058
  let calleeName = null;
1886
- if (ts30.isIdentifier(parent.expression)) {
2059
+ if (ts31.isIdentifier(parent.expression)) {
1887
2060
  calleeName = parent.expression.text;
1888
- } else if (ts30.isPropertyAccessExpression(parent.expression)) {
2061
+ } else if (ts31.isPropertyAccessExpression(parent.expression)) {
1889
2062
  calleeName = parent.expression.name.text;
1890
2063
  }
1891
2064
  if (calleeName) {
@@ -1896,7 +2069,7 @@ function deriveAnonymousName(node, sourceFile) {
1896
2069
  return `<anonymous>:${line}`;
1897
2070
  }
1898
2071
  function collectImports(node, file, imports) {
1899
- if (ts30.isImportDeclaration(node) && ts30.isStringLiteral(node.moduleSpecifier)) {
2072
+ if (ts31.isImportDeclaration(node) && ts31.isStringLiteral(node.moduleSpecifier)) {
1900
2073
  const moduleSource = node.moduleSpecifier.text;
1901
2074
  const clause = node.importClause;
1902
2075
  if (!clause) return;
@@ -1908,7 +2081,7 @@ function collectImports(node, file, imports) {
1908
2081
  source: moduleSource
1909
2082
  });
1910
2083
  }
1911
- if (clause.namedBindings && ts30.isNamedImports(clause.namedBindings)) {
2084
+ if (clause.namedBindings && ts31.isNamedImports(clause.namedBindings)) {
1912
2085
  for (const el of clause.namedBindings.elements) {
1913
2086
  imports.push({
1914
2087
  file,
@@ -1919,9 +2092,9 @@ function collectImports(node, file, imports) {
1919
2092
  }
1920
2093
  }
1921
2094
  }
1922
- if (ts30.isExportDeclaration(node) && node.moduleSpecifier && ts30.isStringLiteral(node.moduleSpecifier)) {
2095
+ if (ts31.isExportDeclaration(node) && node.moduleSpecifier && ts31.isStringLiteral(node.moduleSpecifier)) {
1923
2096
  const moduleSource = node.moduleSpecifier.text;
1924
- if (node.exportClause && ts30.isNamedExports(node.exportClause)) {
2097
+ if (node.exportClause && ts31.isNamedExports(node.exportClause)) {
1925
2098
  for (const el of node.exportClause.elements) {
1926
2099
  imports.push({
1927
2100
  file,
@@ -1934,7 +2107,7 @@ function collectImports(node, file, imports) {
1934
2107
  }
1935
2108
  }
1936
2109
  function collectStatementSequences(node, file, sourceFile, registry) {
1937
- if (!ts30.isBlock(node)) return;
2110
+ if (!ts31.isBlock(node)) return;
1938
2111
  const stmts = node.statements;
1939
2112
  const n = stmts.length;
1940
2113
  if (n < 3) return;
@@ -1944,43 +2117,46 @@ function collectStatementSequences(node, file, sourceFile, registry) {
1944
2117
  const window = stmts.slice(start, start + size);
1945
2118
  const texts = window.map((s) => s.getText(sourceFile));
1946
2119
  const joined = texts.join("\n");
1947
- const normalized = normalizeText(joined);
2120
+ const normalized = normalizeText2(joined);
1948
2121
  if (normalized.length < 128) continue;
1949
2122
  const hash = hashText(joined.replace(/\s+/g, " ").trim());
1950
2123
  const normalizedHash = hashText(normalized);
1951
2124
  const firstStmt = window[0];
1952
2125
  const lastStmt = window[window.length - 1];
1953
2126
  if (firstStmt === void 0 || lastStmt === void 0) continue;
1954
- const line = ts30.getLineAndCharacterOfPosition(sourceFile, firstStmt.getStart(sourceFile)).line + 1;
1955
- const endLine = ts30.getLineAndCharacterOfPosition(sourceFile, lastStmt.getEnd()).line + 1;
2127
+ const line = ts31.getLineAndCharacterOfPosition(sourceFile, firstStmt.getStart(sourceFile)).line + 1;
2128
+ const endLine = ts31.getLineAndCharacterOfPosition(sourceFile, lastStmt.getEnd()).line + 1;
1956
2129
  registry.add({ file, line, endLine, hash, normalizedHash, statementCount: size, normalizedBodyLength: normalized.length });
1957
2130
  }
1958
2131
  }
1959
2132
  }
1960
2133
  function collectInlineParamTypes(node, file, sourceFile, registry) {
1961
- if (!ts30.isTypeLiteralNode(node)) return;
2134
+ if (!ts31.isTypeLiteralNode(node)) return;
1962
2135
  const parent = node.parent;
1963
- if (!parent || !ts30.isParameter(parent)) return;
2136
+ if (!parent || !ts31.isParameter(parent)) return;
1964
2137
  if (parent.type !== node) return;
1965
- const line = ts30.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2138
+ const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
1966
2139
  registry.add(file, line, node, sourceFile);
1967
2140
  }
1968
2141
  function collectCallSites(node, file, sourceFile, checker, sites) {
1969
- if (!ts30.isCallExpression(node)) return;
2142
+ if (!ts31.isCallExpression(node)) return;
1970
2143
  let calleeName = null;
1971
- if (ts30.isIdentifier(node.expression)) {
2144
+ if (ts31.isIdentifier(node.expression)) {
1972
2145
  calleeName = node.expression.text;
1973
- } else if (ts30.isPropertyAccessExpression(node.expression)) {
2146
+ } else if (ts31.isPropertyAccessExpression(node.expression)) {
1974
2147
  calleeName = node.expression.name.text;
1975
2148
  }
1976
2149
  if (calleeName) {
1977
- const line = ts30.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2150
+ const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
1978
2151
  let symbol;
2152
+ let resolvedDeclaration;
1979
2153
  try {
1980
2154
  symbol = checker.getSymbolAtLocation(node.expression);
1981
- if (symbol && symbol.flags & ts30.SymbolFlags.Alias) {
2155
+ if (symbol && symbol.flags & ts31.SymbolFlags.Alias) {
1982
2156
  symbol = checker.getAliasedSymbol(symbol);
1983
2157
  }
2158
+ const signature = checker.getResolvedSignature(node);
2159
+ resolvedDeclaration = signature?.declaration;
1984
2160
  } catch {
1985
2161
  }
1986
2162
  sites.push({
@@ -1989,7 +2165,8 @@ function collectCallSites(node, file, sourceFile, checker, sites) {
1989
2165
  line,
1990
2166
  argCount: node.arguments.length,
1991
2167
  node,
1992
- symbol
2168
+ symbol,
2169
+ resolvedDeclaration
1993
2170
  });
1994
2171
  }
1995
2172
  }
@@ -1998,12 +2175,12 @@ function collectAllComments(sourceFile) {
1998
2175
  const source = sourceFile.getFullText();
1999
2176
  const seen = /* @__PURE__ */ new Set();
2000
2177
  function visit(node) {
2001
- const leading = ts30.getLeadingCommentRanges(source, node.getFullStart());
2178
+ const leading = ts31.getLeadingCommentRanges(source, node.getFullStart());
2002
2179
  if (leading) {
2003
2180
  for (const r of leading) {
2004
2181
  if (seen.has(r.pos)) continue;
2005
2182
  seen.add(r.pos);
2006
- const isLine = r.kind === ts30.SyntaxKind.SingleLineCommentTrivia;
2183
+ const isLine = r.kind === ts31.SyntaxKind.SingleLineCommentTrivia;
2007
2184
  comments.push({
2008
2185
  type: isLine ? "Line" : "Block",
2009
2186
  value: source.slice(r.pos + 2, isLine ? r.end : r.end - 2),
@@ -2012,12 +2189,12 @@ function collectAllComments(sourceFile) {
2012
2189
  });
2013
2190
  }
2014
2191
  }
2015
- const trailing = ts30.getTrailingCommentRanges(source, node.getEnd());
2192
+ const trailing = ts31.getTrailingCommentRanges(source, node.getEnd());
2016
2193
  if (trailing) {
2017
2194
  for (const r of trailing) {
2018
2195
  if (seen.has(r.pos)) continue;
2019
2196
  seen.add(r.pos);
2020
- const isLine = r.kind === ts30.SyntaxKind.SingleLineCommentTrivia;
2197
+ const isLine = r.kind === ts31.SyntaxKind.SingleLineCommentTrivia;
2021
2198
  comments.push({
2022
2199
  type: isLine ? "Line" : "Block",
2023
2200
  value: source.slice(r.pos + 2, isLine ? r.end : r.end - 2),
@@ -2026,34 +2203,34 @@ function collectAllComments(sourceFile) {
2026
2203
  });
2027
2204
  }
2028
2205
  }
2029
- ts30.forEachChild(node, visit);
2206
+ ts31.forEachChild(node, visit);
2030
2207
  }
2031
2208
  visit(sourceFile);
2032
2209
  return comments;
2033
2210
  }
2034
2211
 
2035
2212
  // src/typecheck/program.ts
2036
- import * as ts31 from "typescript";
2213
+ import * as ts32 from "typescript";
2037
2214
  import { dirname as dirname2 } from "path";
2038
2215
  function createProgramFromFiles(files) {
2039
2216
  let configPath;
2040
2217
  if (files.length > 0) {
2041
- configPath = ts31.findConfigFile(dirname2(files[0]), ts31.sys.fileExists, "tsconfig.json");
2218
+ configPath = ts32.findConfigFile(dirname2(files[0]), ts32.sys.fileExists, "tsconfig.json");
2042
2219
  }
2043
2220
  if (configPath) {
2044
- const configFile = ts31.readConfigFile(configPath, ts31.sys.readFile);
2045
- const parsed = ts31.parseJsonConfigFileContent(configFile.config, ts31.sys, dirname2(configPath));
2046
- return ts31.createProgram({
2221
+ const configFile = ts32.readConfigFile(configPath, ts32.sys.readFile);
2222
+ const parsed = ts32.parseJsonConfigFileContent(configFile.config, ts32.sys, dirname2(configPath));
2223
+ return ts32.createProgram({
2047
2224
  rootNames: files,
2048
2225
  options: { ...parsed.options, skipLibCheck: true }
2049
2226
  });
2050
2227
  }
2051
- return ts31.createProgram({
2228
+ return ts32.createProgram({
2052
2229
  rootNames: files,
2053
2230
  options: {
2054
- target: ts31.ScriptTarget.ESNext,
2055
- module: ts31.ModuleKind.ESNext,
2056
- moduleResolution: ts31.ModuleResolutionKind.Bundler,
2231
+ target: ts32.ScriptTarget.ESNext,
2232
+ module: ts32.ModuleKind.ESNext,
2233
+ moduleResolution: ts32.ModuleResolutionKind.Bundler,
2057
2234
  strict: true,
2058
2235
  skipLibCheck: true,
2059
2236
  noEmit: true
@@ -2293,4 +2470,4 @@ export {
2293
2470
  executeScan,
2294
2471
  scan
2295
2472
  };
2296
- //# sourceMappingURL=chunk-ZNOFYNU4.js.map
2473
+ //# sourceMappingURL=chunk-HE647T6E.js.map