code-gauge 5.0.0 → 6.0.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.
Files changed (88) hide show
  1. package/README.md +204 -72
  2. package/THIRD-PARTY-NOTICES.txt +1152 -798
  3. package/dist/checkCommand.cjs +5 -0
  4. package/dist/checkCommand.cjs.map +1 -0
  5. package/dist/checkCommand.d.ts +12 -0
  6. package/dist/checkCommand.js +5 -0
  7. package/dist/checkCommand.js.map +1 -0
  8. package/dist/cli.cjs +3 -3
  9. package/dist/cli.cjs.map +1 -1
  10. package/dist/cli.js +3 -3
  11. package/dist/cli.js.map +1 -1
  12. package/dist/cliConfig.cjs +1 -1
  13. package/dist/cliConfig.cjs.map +1 -1
  14. package/dist/cliConfig.d.ts +3 -11
  15. package/dist/cliConfig.js +1 -1
  16. package/dist/cliConfig.js.map +1 -1
  17. package/dist/duplication.cjs +1 -1
  18. package/dist/duplication.cjs.map +1 -1
  19. package/dist/duplication.js +1 -1
  20. package/dist/duplication.js.map +1 -1
  21. package/dist/exclusion.cjs +1 -1
  22. package/dist/exclusion.cjs.map +1 -1
  23. package/dist/exclusion.d.ts +2 -2
  24. package/dist/exclusion.js +1 -1
  25. package/dist/exclusion.js.map +1 -1
  26. package/dist/git.cjs +1 -1
  27. package/dist/git.cjs.map +1 -1
  28. package/dist/git.d.ts +13 -12
  29. package/dist/git.js +1 -1
  30. package/dist/git.js.map +1 -1
  31. package/dist/index.cjs +1 -1
  32. package/dist/index.d.ts +0 -4
  33. package/dist/index.js +1 -1
  34. package/dist/metrics.cjs +1 -1
  35. package/dist/metrics.cjs.map +1 -1
  36. package/dist/metrics.d.ts +1 -2
  37. package/dist/metrics.js +1 -1
  38. package/dist/metrics.js.map +1 -1
  39. package/dist/nativeMetrics.cjs +3 -3
  40. package/dist/nativeMetrics.cjs.map +1 -1
  41. package/dist/nativeMetrics.js +3 -3
  42. package/dist/nativeMetrics.js.map +1 -1
  43. package/dist/scan.cjs +1 -1
  44. package/dist/scan.cjs.map +1 -1
  45. package/dist/scan.d.ts +10 -26
  46. package/dist/scan.js +1 -1
  47. package/dist/scan.js.map +1 -1
  48. package/dist/thresholdCheck.cjs +2 -0
  49. package/dist/thresholdCheck.cjs.map +1 -0
  50. package/dist/thresholdCheck.d.ts +36 -0
  51. package/dist/thresholdCheck.js +2 -0
  52. package/dist/thresholdCheck.js.map +1 -0
  53. package/dist/thresholds.cjs +2 -0
  54. package/dist/thresholds.cjs.map +1 -0
  55. package/dist/thresholds.d.ts +38 -0
  56. package/dist/thresholds.js +2 -0
  57. package/dist/thresholds.js.map +1 -0
  58. package/dist/types.d.ts +8 -4
  59. package/dist/worker.cjs +1 -1
  60. package/dist/worker.cjs.map +1 -1
  61. package/dist/worker.js +1 -1
  62. package/dist/worker.js.map +1 -1
  63. package/native/Cargo.lock +73 -73
  64. package/native/Cargo.toml +16 -21
  65. package/native/code-gauge.wasm +0 -0
  66. package/native/src/dep_degree.rs +6 -6
  67. package/native/src/duplication.rs +38 -12
  68. package/native/src/functions.rs +10 -7
  69. package/native/src/languages.rs +14 -13
  70. package/native/src/measure.rs +16 -8
  71. package/native/src/tree_index.rs +4 -4
  72. package/native/src/util.rs +9 -2
  73. package/package.json +10 -9
  74. package/dist/diffCommand.cjs +0 -5
  75. package/dist/diffCommand.cjs.map +0 -1
  76. package/dist/diffCommand.d.ts +0 -17
  77. package/dist/diffCommand.js +0 -5
  78. package/dist/diffCommand.js.map +0 -1
  79. package/dist/newDuplication.cjs +0 -2
  80. package/dist/newDuplication.cjs.map +0 -1
  81. package/dist/newDuplication.d.ts +0 -40
  82. package/dist/newDuplication.js +0 -2
  83. package/dist/newDuplication.js.map +0 -1
  84. package/dist/regressionGate.cjs +0 -2
  85. package/dist/regressionGate.cjs.map +0 -1
  86. package/dist/regressionGate.d.ts +0 -108
  87. package/dist/regressionGate.js +0 -2
  88. package/dist/regressionGate.js.map +0 -1
@@ -0,0 +1,38 @@
1
+ import type { CodeMetrics, FunctionMetrics } from './types.js';
2
+ /**
3
+ * One limit of `code-gauge check`. Its `key` is the config setting, the per-language override
4
+ * setting, and (kebab-cased) the command-line option, so a new entry here is all a new thresholded
5
+ * metric needs.
6
+ */
7
+ export interface Threshold<Subject = never> {
8
+ key: string;
9
+ /** How the report names the measured value. */
10
+ label: string;
11
+ defaultLimit: number;
12
+ /** What to change in the code when the limit is exceeded. */
13
+ hint: string;
14
+ measure: (subject: Subject) => number;
15
+ }
16
+ /** Limits by threshold key; `Infinity` disables a limit. */
17
+ export type Limits = Readonly<Record<string, number>>;
18
+ export declare const functionThresholds: readonly Threshold<FunctionMetrics>[];
19
+ export declare const fileThresholds: readonly Threshold<CodeMetrics>[];
20
+ /** A duplicated block violates when it spans at least this many lines. */
21
+ export declare const duplicationThreshold: Threshold<{
22
+ startLine: number;
23
+ endLine: number;
24
+ }>;
25
+ export declare const thresholds: readonly Threshold[];
26
+ /** The name a report gives a threshold's measured value: its key without the `max`/`min` prefix. */
27
+ export declare function metricNameOf(threshold: Threshold): string;
28
+ /** The `thresholds` config section; a missing key falls back to the less specific layer. */
29
+ export interface ThresholdsConfig {
30
+ limits: Limits;
31
+ /** Overrides for the files of one language, keyed by language name. */
32
+ languages: Readonly<Record<string, Limits>>;
33
+ }
34
+ /**
35
+ * The limits applying to files of each language, with precedence command-line options > the
36
+ * config's per-language overrides > the config's limits > built-in defaults.
37
+ */
38
+ export declare function resolveLimits(cliLimits: Limits, config: ThresholdsConfig | undefined): (language: string) => Limits;
@@ -0,0 +1,2 @@
1
+ const e=[{key:`maxFunctionCognitiveComplexity`,label:`cognitive complexity`,defaultLimit:15,hint:`flatten nested branching with early returns and extract nested blocks into named functions.`,measure:e=>e.cognitiveComplexity},{key:`maxFunctionCyclomaticComplexity`,label:`cyclomatic complexity`,defaultLimit:10,hint:`the function has too many independent paths; split it by decision or replace condition chains with a lookup table.`,measure:e=>e.cyclomaticComplexity},{key:`maxFunctionNcss`,label:`NCSS`,defaultLimit:60,hint:`split the function into smaller functions that each do one step.`,measure:e=>e.ncss},{key:`maxFunctionNestingDepth`,label:`nesting depth`,defaultLimit:4,hint:`replace nested conditions with guard clauses or move inner blocks into functions.`,measure:e=>e.nestingDepth},{key:`maxFunctionParameterCount`,label:`parameters`,defaultLimit:7,hint:`group related parameters into one object or split the function.`,measure:e=>e.parameterCount},{key:`maxFunctionHalsteadVolume`,label:`Halstead volume`,defaultLimit:2e3,hint:`the function holds too many operators and operands; move cohesive parts into helper functions.`,measure:e=>e.halstead.volume},{key:`maxFunctionHalsteadDifficulty`,label:`Halstead difficulty`,defaultLimit:20,hint:`the function reuses the same operands many times; split it so each part handles fewer values.`,measure:e=>e.halstead.difficulty},{key:`maxFunctionHalsteadEffort`,label:`Halstead effort`,defaultLimit:3e4,hint:`the function is both large and dense; split it into smaller functions with fewer values each.`,measure:e=>e.halstead.effort},{key:`maxFunctionDepDegree`,label:`DepDegree`,defaultLimit:50,hint:`too many values flow between the variables of the function; split it so each part works on fewer variables.`,measure:e=>e.depDegree}],t=[{key:`maxFileNcss`,label:`file NCSS`,defaultLimit:500,hint:`split the file into smaller modules with one responsibility each.`,measure:e=>e.ncssCount}],n={key:`minDuplicateLines`,label:`duplicated lines`,defaultLimit:10,hint:`extract the repeated code into one shared function or module and call it from every location.`,measure:e=>e.endLine-e.startLine+1},r=[...e,...t,n];function i(e){let t=e.key.slice(3);return t.charAt(0).toLowerCase()+t.slice(1)}function a(e,t){let n={...Object.fromEntries(r.map(({key:e,defaultLimit:t})=>[e,t])),...t?.limits};return r=>({...n,...t&&Object.hasOwn(t.languages,r)?t.languages[r]:void 0,...e})}export{n as duplicationThreshold,t as fileThresholds,e as functionThresholds,i as metricNameOf,a as resolveLimits,r as thresholds};
2
+ //# sourceMappingURL=thresholds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thresholds.js","names":[],"sources":["../src/thresholds.ts"],"sourcesContent":["import type { CodeMetrics, FunctionMetrics } from './types.js';\n\n/**\n * One limit of `code-gauge check`. Its `key` is the config setting, the per-language override\n * setting, and (kebab-cased) the command-line option, so a new entry here is all a new thresholded\n * metric needs.\n */\nexport interface Threshold<Subject = never> {\n key: string;\n /** How the report names the measured value. */\n label: string;\n defaultLimit: number;\n /** What to change in the code when the limit is exceeded. */\n hint: string;\n measure: (subject: Subject) => number;\n}\n\n/** Limits by threshold key; `Infinity` disables a limit. */\nexport type Limits = Readonly<Record<string, number>>;\n\nexport const functionThresholds: readonly Threshold<FunctionMetrics>[] = [\n {\n key: 'maxFunctionCognitiveComplexity',\n label: 'cognitive complexity',\n defaultLimit: 15,\n hint: 'flatten nested branching with early returns and extract nested blocks into named functions.',\n measure: (fn) => fn.cognitiveComplexity,\n },\n {\n key: 'maxFunctionCyclomaticComplexity',\n label: 'cyclomatic complexity',\n defaultLimit: 10,\n hint: 'the function has too many independent paths; split it by decision or replace condition chains with a lookup table.',\n measure: (fn) => fn.cyclomaticComplexity,\n },\n {\n key: 'maxFunctionNcss',\n label: 'NCSS',\n defaultLimit: 60,\n hint: 'split the function into smaller functions that each do one step.',\n measure: (fn) => fn.ncss,\n },\n {\n key: 'maxFunctionNestingDepth',\n label: 'nesting depth',\n defaultLimit: 4,\n hint: 'replace nested conditions with guard clauses or move inner blocks into functions.',\n measure: (fn) => fn.nestingDepth,\n },\n {\n key: 'maxFunctionParameterCount',\n label: 'parameters',\n defaultLimit: 7,\n hint: 'group related parameters into one object or split the function.',\n measure: (fn) => fn.parameterCount,\n },\n {\n key: 'maxFunctionHalsteadVolume',\n label: 'Halstead volume',\n defaultLimit: 2000,\n hint: 'the function holds too many operators and operands; move cohesive parts into helper functions.',\n measure: (fn) => fn.halstead.volume,\n },\n {\n key: 'maxFunctionHalsteadDifficulty',\n label: 'Halstead difficulty',\n defaultLimit: 20,\n hint: 'the function reuses the same operands many times; split it so each part handles fewer values.',\n measure: (fn) => fn.halstead.difficulty,\n },\n {\n key: 'maxFunctionHalsteadEffort',\n label: 'Halstead effort',\n defaultLimit: 30_000,\n hint: 'the function is both large and dense; split it into smaller functions with fewer values each.',\n measure: (fn) => fn.halstead.effort,\n },\n {\n key: 'maxFunctionDepDegree',\n label: 'DepDegree',\n defaultLimit: 50,\n hint: 'too many values flow between the variables of the function; split it so each part works on fewer variables.',\n measure: (fn) => fn.depDegree,\n },\n];\n\nexport const fileThresholds: readonly Threshold<CodeMetrics>[] = [\n {\n key: 'maxFileNcss',\n label: 'file NCSS',\n defaultLimit: 500,\n hint: 'split the file into smaller modules with one responsibility each.',\n measure: (file) => file.ncssCount,\n },\n];\n\n/** A duplicated block violates when it spans at least this many lines. */\nexport const duplicationThreshold: Threshold<{ startLine: number; endLine: number }> = {\n key: 'minDuplicateLines',\n label: 'duplicated lines',\n defaultLimit: 10,\n hint: 'extract the repeated code into one shared function or module and call it from every location.',\n measure: (block) => block.endLine - block.startLine + 1,\n};\n\nexport const thresholds: readonly Threshold[] = [...functionThresholds, ...fileThresholds, duplicationThreshold];\n\n/** The name a report gives a threshold's measured value: its key without the `max`/`min` prefix. */\nexport function metricNameOf(threshold: Threshold): string {\n const name = threshold.key.slice(3);\n return name.charAt(0).toLowerCase() + name.slice(1);\n}\n\n/** The `thresholds` config section; a missing key falls back to the less specific layer. */\nexport interface ThresholdsConfig {\n limits: Limits;\n /** Overrides for the files of one language, keyed by language name. */\n languages: Readonly<Record<string, Limits>>;\n}\n\n/**\n * The limits applying to files of each language, with precedence command-line options > the\n * config's per-language overrides > the config's limits > built-in defaults.\n */\nexport function resolveLimits(cliLimits: Limits, config: ThresholdsConfig | undefined): (language: string) => Limits {\n const defaults = Object.fromEntries(thresholds.map(({ key, defaultLimit }) => [key, defaultLimit]));\n const shared = { ...defaults, ...config?.limits };\n return (language) => ({\n ...shared,\n // Object.hasOwn: a language named like an Object.prototype member must not read an inherited value.\n ...(config && Object.hasOwn(config.languages, language) ? config.languages[language] : undefined),\n ...cliLimits,\n });\n}\n"],"mappings":"AAoBA,MAAa,EAA4D,CACvE,CACE,IAAK,iCACL,MAAO,uBACP,aAAc,GACd,KAAM,8FACN,QAAU,GAAO,EAAG,mBACtB,EACA,CACE,IAAK,kCACL,MAAO,wBACP,aAAc,GACd,KAAM,qHACN,QAAU,GAAO,EAAG,oBACtB,EACA,CACE,IAAK,kBACL,MAAO,OACP,aAAc,GACd,KAAM,mEACN,QAAU,GAAO,EAAG,IACtB,EACA,CACE,IAAK,0BACL,MAAO,gBACP,aAAc,EACd,KAAM,oFACN,QAAU,GAAO,EAAG,YACtB,EACA,CACE,IAAK,4BACL,MAAO,aACP,aAAc,EACd,KAAM,kEACN,QAAU,GAAO,EAAG,cACtB,EACA,CACE,IAAK,4BACL,MAAO,kBACP,aAAc,IACd,KAAM,iGACN,QAAU,GAAO,EAAG,SAAS,MAC/B,EACA,CACE,IAAK,gCACL,MAAO,sBACP,aAAc,GACd,KAAM,gGACN,QAAU,GAAO,EAAG,SAAS,UAC/B,EACA,CACE,IAAK,4BACL,MAAO,kBACP,aAAc,IACd,KAAM,gGACN,QAAU,GAAO,EAAG,SAAS,MAC/B,EACA,CACE,IAAK,uBACL,MAAO,YACP,aAAc,GACd,KAAM,8GACN,QAAU,GAAO,EAAG,SACtB,CACF,EAEa,EAAoD,CAC/D,CACE,IAAK,cACL,MAAO,YACP,aAAc,IACd,KAAM,oEACN,QAAU,GAAS,EAAK,SAC1B,CACF,EAGa,EAA0E,CACrF,IAAK,oBACL,MAAO,mBACP,aAAc,GACd,KAAM,gGACN,QAAU,GAAU,EAAM,QAAU,EAAM,UAAY,CACxD,EAEa,EAAmC,CAAC,GAAG,EAAoB,GAAG,EAAgB,CAAoB,EAG/G,SAAgB,EAAa,EAA8B,CACzD,IAAM,EAAO,EAAU,IAAI,MAAM,CAAC,EAClC,OAAO,EAAK,OAAO,CAAC,CAAC,CAAC,YAAY,EAAI,EAAK,MAAM,CAAC,CACpD,CAaA,SAAgB,EAAc,EAAmB,EAAoE,CAEnH,IAAM,EAAS,CAAE,GADA,OAAO,YAAY,EAAW,KAAK,CAAE,MAAK,kBAAmB,CAAC,EAAK,CAAY,CAAC,CACtE,EAAG,GAAG,GAAQ,MAAO,EAChD,MAAQ,KAAc,CACpB,GAAG,EAEH,GAAI,GAAU,OAAO,OAAO,EAAO,UAAW,CAAQ,EAAI,EAAO,UAAU,GAAY,IAAA,GACvF,GAAG,CACL,EACF"}
package/dist/types.d.ts CHANGED
@@ -49,6 +49,11 @@ export interface HalsteadMetrics {
49
49
  vocabulary: number;
50
50
  length: number;
51
51
  volume: number;
52
+ /**
53
+ * (distinctOperators / 2) * (totalOperands / distinctOperands), i.e. effort / volume; 0 when
54
+ * there are no operands.
55
+ */
56
+ difficulty: number;
52
57
  effort: number;
53
58
  }
54
59
  export interface FunctionMetrics {
@@ -72,7 +77,7 @@ export interface FunctionMetrics {
72
77
  * short-circuit operator (`&&`, `||`, `and`, `or`) wherever it appears, and per case-labelled
73
78
  * statement or match arm (stacked labels share one; default and catch-all arms add none).
74
79
  * Full-evaluation operators and `throw` add nothing. Nested function bodies are excluded, so
75
- * summing over functions counts each decision once. Not used by ranking or the regression gate.
80
+ * summing over functions counts each decision once.
76
81
  */
77
82
  cyclomaticComplexity: number;
78
83
  cognitiveComplexity: number;
@@ -92,9 +97,8 @@ export interface FunctionMetrics {
92
97
  /**
93
98
  * Approximate def-use dependency degree (DepDegree, Beyer & Fararooy 2010): the number of
94
99
  * variable reads whose name has a preceding definition (declaration, assignment, or parameter)
95
- * within the same function. A file-local single-assignment approximation — each read is charged
96
- * one reaching definition — which is stable enough for regression ratcheting where only the
97
- * delta matters.
100
+ * within the same function. A file-local single-assignment approximation: each read is charged
101
+ * one reaching definition.
98
102
  */
99
103
  depDegree: number;
100
104
  }
package/dist/worker.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_virtual/_rolldown/runtime.cjs"),t=require("./crossFileDuplication.cjs"),n=require("./languages.cjs"),r=require("./nativeMetrics.cjs"),i=require("./metrics.cjs"),a=require("./newDuplication.cjs"),o=require("./regressionGate.cjs");require("./index.cjs");const s=require("./wasmBinding.cjs");let c=require("../native/code-gauge.wasm");c=e.__toESM(c,1),r.setNativeBinding(s.createWasmBinding(c.default)),exports.TreeMeasurer=i.TreeMeasurer,exports.collectCrossFileDuplicationFileData=i.collectCrossFileDuplicationFileData,exports.collectDuplicationCandidates=i.collectDuplicationCandidates,exports.collectFunctionTokenSequences=i.collectFunctionTokenSequences,exports.defaultGateOptions=o.defaultGateOptions,exports.defaultLanguages=n.defaultLanguages,exports.defaultMeasurer=i.defaultMeasurer,exports.detectLanguage=n.detectLanguage,exports.evaluateRegressionGate=o.evaluateRegressionGate,exports.findNewlyDuplicatedLines=a.findNewlyDuplicatedLines,exports.measureCode=i.measureCode,exports.measureCrossFileDuplication=t.measureCrossFileDuplication,exports.supportedLanguages=n.supportedLanguages;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./_virtual/_rolldown/runtime.cjs"),t=require("./crossFileDuplication.cjs"),n=require("./languages.cjs"),r=require("./nativeMetrics.cjs"),i=require("./metrics.cjs");require("./index.cjs");const a=require("./wasmBinding.cjs");let o=require("../native/code-gauge.wasm");o=e.__toESM(o,1),r.setNativeBinding(a.createWasmBinding(o.default)),exports.TreeMeasurer=i.TreeMeasurer,exports.collectCrossFileDuplicationFileData=i.collectCrossFileDuplicationFileData,exports.collectDuplicationCandidates=i.collectDuplicationCandidates,exports.collectFunctionTokenSequences=i.collectFunctionTokenSequences,exports.defaultLanguages=n.defaultLanguages,exports.defaultMeasurer=i.defaultMeasurer,exports.detectLanguage=n.detectLanguage,exports.measureCode=i.measureCode,exports.measureCrossFileDuplication=t.measureCrossFileDuplication,exports.supportedLanguages=n.supportedLanguages;
2
2
  //# sourceMappingURL=worker.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker.cjs","names":["setNativeBinding","createWasmBinding","wasmModule"],"sources":["../src/worker.ts"],"sourcesContent":["// The entry point for Cloudflare Workers (the `workerd` export condition), which cannot load N-API\n// addons: the same API runs on the WebAssembly build of the native addon.\nimport wasmModule from '../native/code-gauge.wasm';\nimport { setNativeBinding } from './nativeMetrics.js';\nimport { createWasmBinding } from './wasmBinding.js';\n\nsetNativeBinding(createWasmBinding(wasmModule));\n\nexport * from './index.js';\n"],"mappings":"icAMAA,EAAAA,iBAAiBC,EAAAA,kBAAkBC,EAAAA,OAAU,CAAC"}
1
+ {"version":3,"file":"worker.cjs","names":["setNativeBinding","createWasmBinding","wasmModule"],"sources":["../src/worker.ts"],"sourcesContent":["// The entry point for Cloudflare Workers (the `workerd` export condition), which cannot load N-API\n// addons: the same API runs on the WebAssembly build of the native addon.\nimport wasmModule from '../native/code-gauge.wasm';\nimport { setNativeBinding } from './nativeMetrics.js';\nimport { createWasmBinding } from './wasmBinding.js';\n\nsetNativeBinding(createWasmBinding(wasmModule));\n\nexport * from './index.js';\n"],"mappings":"6XAMAA,EAAAA,iBAAiBC,EAAAA,kBAAkBC,EAAAA,OAAU,CAAC"}
package/dist/worker.js CHANGED
@@ -1,2 +1,2 @@
1
- import{measureCrossFileDuplication as e}from"./crossFileDuplication.js";import{defaultLanguages as t,detectLanguage as n,supportedLanguages as r}from"./languages.js";import{setNativeBinding as i}from"./nativeMetrics.js";import{TreeMeasurer as a,collectCrossFileDuplicationFileData as o,collectDuplicationCandidates as s,collectFunctionTokenSequences as c,defaultMeasurer as l,measureCode as u}from"./metrics.js";import{findNewlyDuplicatedLines as d}from"./newDuplication.js";import{defaultGateOptions as f,evaluateRegressionGate as p}from"./regressionGate.js";import"./index.js";import{createWasmBinding as m}from"./wasmBinding.js";import h from"../native/code-gauge.wasm";i(m(h));export{a as TreeMeasurer,o as collectCrossFileDuplicationFileData,s as collectDuplicationCandidates,c as collectFunctionTokenSequences,f as defaultGateOptions,t as defaultLanguages,l as defaultMeasurer,n as detectLanguage,p as evaluateRegressionGate,d as findNewlyDuplicatedLines,u as measureCode,e as measureCrossFileDuplication,r as supportedLanguages};
1
+ import{measureCrossFileDuplication as e}from"./crossFileDuplication.js";import{defaultLanguages as t,detectLanguage as n,supportedLanguages as r}from"./languages.js";import{setNativeBinding as i}from"./nativeMetrics.js";import{TreeMeasurer as a,collectCrossFileDuplicationFileData as o,collectDuplicationCandidates as s,collectFunctionTokenSequences as c,defaultMeasurer as l,measureCode as u}from"./metrics.js";import"./index.js";import{createWasmBinding as d}from"./wasmBinding.js";import f from"../native/code-gauge.wasm";i(d(f));export{a as TreeMeasurer,o as collectCrossFileDuplicationFileData,s as collectDuplicationCandidates,c as collectFunctionTokenSequences,t as defaultLanguages,l as defaultMeasurer,n as detectLanguage,u as measureCode,e as measureCrossFileDuplication,r as supportedLanguages};
2
2
  //# sourceMappingURL=worker.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker.js","names":[],"sources":["../src/worker.ts"],"sourcesContent":["// The entry point for Cloudflare Workers (the `workerd` export condition), which cannot load N-API\n// addons: the same API runs on the WebAssembly build of the native addon.\nimport wasmModule from '../native/code-gauge.wasm';\nimport { setNativeBinding } from './nativeMetrics.js';\nimport { createWasmBinding } from './wasmBinding.js';\n\nsetNativeBinding(createWasmBinding(wasmModule));\n\nexport * from './index.js';\n"],"mappings":"iqBAMA,EAAiB,EAAkB,CAAU,CAAC"}
1
+ {"version":3,"file":"worker.js","names":[],"sources":["../src/worker.ts"],"sourcesContent":["// The entry point for Cloudflare Workers (the `workerd` export condition), which cannot load N-API\n// addons: the same API runs on the WebAssembly build of the native addon.\nimport wasmModule from '../native/code-gauge.wasm';\nimport { setNativeBinding } from './nativeMetrics.js';\nimport { createWasmBinding } from './wasmBinding.js';\n\nsetNativeBinding(createWasmBinding(wasmModule));\n\nexport * from './index.js';\n"],"mappings":"6gBAMA,EAAiB,EAAkB,CAAU,CAAC"}
package/native/Cargo.lock CHANGED
@@ -45,18 +45,18 @@ dependencies = [
45
45
  "rustc-hash",
46
46
  "serde",
47
47
  "serde_json",
48
- "tree-sitter",
49
- "tree-sitter-c",
50
- "tree-sitter-c-sharp",
51
- "tree-sitter-cpp",
52
48
  "tree-sitter-go",
53
49
  "tree-sitter-java",
54
- "tree-sitter-javascript",
55
50
  "tree-sitter-python",
56
51
  "tree-sitter-ruby",
57
- "tree-sitter-rust",
58
- "tree-sitter-typescript",
52
+ "willbooster-tree-sitter",
53
+ "willbooster-tree-sitter-c",
54
+ "willbooster-tree-sitter-c-sharp",
55
+ "willbooster-tree-sitter-cpp",
56
+ "willbooster-tree-sitter-javascript",
59
57
  "willbooster-tree-sitter-kotlin",
58
+ "willbooster-tree-sitter-rust",
59
+ "willbooster-tree-sitter-typescript",
60
60
  ]
61
61
 
62
62
  [[package]]
@@ -425,141 +425,141 @@ dependencies = [
425
425
  ]
426
426
 
427
427
  [[package]]
428
- name = "tree-sitter"
429
- version = "0.25.10"
428
+ name = "tree-sitter-go"
429
+ version = "0.25.0"
430
430
  source = "registry+https://github.com/rust-lang/crates.io-index"
431
- checksum = "78f873475d258561b06f1c595d93308a7ed124d9977cb26b148c2084a4a3cc87"
431
+ checksum = "c8560a4d2f835cc0d4d2c2e03cbd0dde2f6114b43bc491164238d333e28b16ea"
432
432
  dependencies = [
433
433
  "cc",
434
- "regex",
435
- "regex-syntax",
436
- "serde_json",
437
- "streaming-iterator",
438
434
  "tree-sitter-language",
439
435
  ]
440
436
 
441
437
  [[package]]
442
- name = "tree-sitter-c"
443
- version = "0.21.4"
438
+ name = "tree-sitter-java"
439
+ version = "0.23.5"
444
440
  source = "registry+https://github.com/rust-lang/crates.io-index"
445
- checksum = "f956d5351d62652864a4ff3ae861747e7a1940dc96c9998ae400ac0d3ce30427"
441
+ checksum = "0aa6cbcdc8c679b214e616fd3300da67da0e492e066df01bcf5a5921a71e90d6"
446
442
  dependencies = [
447
443
  "cc",
448
- "tree-sitter",
444
+ "tree-sitter-language",
449
445
  ]
450
446
 
451
447
  [[package]]
452
- name = "tree-sitter-c-sharp"
453
- version = "0.21.3"
448
+ name = "tree-sitter-language"
449
+ version = "0.1.8"
454
450
  source = "registry+https://github.com/rust-lang/crates.io-index"
455
- checksum = "8997ad04502208449025114e434c9024a33a74e700513c702a9d2cac6522a771"
456
- dependencies = [
457
- "cc",
458
- "tree-sitter",
459
- ]
451
+ checksum = "ca0d1bf6fdd806e43ae5198f82f527056d359def39e54e67a0f478ac09dac081"
460
452
 
461
453
  [[package]]
462
- name = "tree-sitter-cpp"
463
- version = "0.22.3"
454
+ name = "tree-sitter-python"
455
+ version = "0.25.0"
464
456
  source = "registry+https://github.com/rust-lang/crates.io-index"
465
- checksum = "1d509a22a992790d38f2c291961ff8a1ff016c437c7ec6befc9220b8eec8918c"
457
+ checksum = "6bf85fd39652e740bf60f46f4cda9492c3a9ad75880575bf14960f775cb74a1c"
466
458
  dependencies = [
467
459
  "cc",
468
- "tree-sitter",
460
+ "tree-sitter-language",
469
461
  ]
470
462
 
471
463
  [[package]]
472
- name = "tree-sitter-go"
473
- version = "0.21.2"
464
+ name = "tree-sitter-ruby"
465
+ version = "0.23.1"
474
466
  source = "registry+https://github.com/rust-lang/crates.io-index"
475
- checksum = "b8d702a98d3c7e70e466456e58ff2b1ac550bf1e29b97e5770676d2fdabec00d"
467
+ checksum = "be0484ea4ef6bb9c575b4fdabde7e31340a8d2dbc7d52b321ac83da703249f95"
476
468
  dependencies = [
477
469
  "cc",
478
- "tree-sitter",
470
+ "tree-sitter-language",
479
471
  ]
480
472
 
481
473
  [[package]]
482
- name = "tree-sitter-java"
483
- version = "0.21.0"
474
+ name = "unicode-ident"
475
+ version = "1.0.24"
484
476
  source = "registry+https://github.com/rust-lang/crates.io-index"
485
- checksum = "33bc21adf831a773c075d9d00107ab43965e6a6ea7607b47fd9ec6f3db4b481b"
486
- dependencies = [
487
- "cc",
488
- "tree-sitter",
489
- ]
477
+ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
490
478
 
491
479
  [[package]]
492
- name = "tree-sitter-javascript"
493
- version = "0.25.0"
480
+ name = "unicode-segmentation"
481
+ version = "1.13.3"
494
482
  source = "registry+https://github.com/rust-lang/crates.io-index"
495
- checksum = "68204f2abc0627a90bdf06e605f5c470aa26fdcb2081ea553a04bdad756693f5"
483
+ checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
484
+
485
+ [[package]]
486
+ name = "willbooster-tree-sitter"
487
+ version = "1.0.5"
488
+ source = "registry+https://github.com/rust-lang/crates.io-index"
489
+ checksum = "7c6ca3007235ade028b793d6090a5ea16618ea3ab9b53427d1f782b15bf64064"
496
490
  dependencies = [
497
491
  "cc",
492
+ "regex",
493
+ "serde_json",
494
+ "streaming-iterator",
498
495
  "tree-sitter-language",
499
496
  ]
500
497
 
501
498
  [[package]]
502
- name = "tree-sitter-language"
503
- version = "0.1.8"
499
+ name = "willbooster-tree-sitter-c"
500
+ version = "1.1.0"
504
501
  source = "registry+https://github.com/rust-lang/crates.io-index"
505
- checksum = "ca0d1bf6fdd806e43ae5198f82f527056d359def39e54e67a0f478ac09dac081"
502
+ checksum = "5cee6f1a4e0f64f337d28aeaed73879d7e8a87d77c287afb989ce29adea54700"
503
+ dependencies = [
504
+ "cc",
505
+ "tree-sitter-language",
506
+ ]
506
507
 
507
508
  [[package]]
508
- name = "tree-sitter-python"
509
- version = "0.21.0"
509
+ name = "willbooster-tree-sitter-c-sharp"
510
+ version = "1.0.3"
510
511
  source = "registry+https://github.com/rust-lang/crates.io-index"
511
- checksum = "b4066c6cf678f962f8c2c4561f205945c84834cce73d981e71392624fdc390a9"
512
+ checksum = "99b224dcd59cda9b24b588557cca3827abaccaf10da72d125c31e7cdb2e6f7fb"
512
513
  dependencies = [
513
514
  "cc",
514
- "tree-sitter",
515
+ "tree-sitter-language",
515
516
  ]
516
517
 
517
518
  [[package]]
518
- name = "tree-sitter-ruby"
519
- version = "0.21.0"
519
+ name = "willbooster-tree-sitter-cpp"
520
+ version = "1.1.1"
520
521
  source = "registry+https://github.com/rust-lang/crates.io-index"
521
- checksum = "c0031f687c0772f2dad7b77104c43428611099a1804c81244ada21560f41f0b1"
522
+ checksum = "72b92a9fcb0dc27bd4af18e06fe6c085dd14d91f009f60ecd92c0308b9cbd068"
522
523
  dependencies = [
523
524
  "cc",
524
- "tree-sitter",
525
+ "tree-sitter-language",
525
526
  ]
526
527
 
527
528
  [[package]]
528
- name = "tree-sitter-rust"
529
- version = "0.21.2"
529
+ name = "willbooster-tree-sitter-javascript"
530
+ version = "1.0.3"
530
531
  source = "registry+https://github.com/rust-lang/crates.io-index"
531
- checksum = "277690f420bf90741dea984f3da038ace46c4fe6047cba57a66822226cde1c93"
532
+ checksum = "e6c40d41b303b525194daa503a71ec94d8269fe8d1648b2d9138fe902623beff"
532
533
  dependencies = [
533
534
  "cc",
534
- "tree-sitter",
535
+ "tree-sitter-language",
535
536
  ]
536
537
 
537
538
  [[package]]
538
- name = "tree-sitter-typescript"
539
- version = "0.21.2"
539
+ name = "willbooster-tree-sitter-kotlin"
540
+ version = "1.4.0"
540
541
  source = "registry+https://github.com/rust-lang/crates.io-index"
541
- checksum = "ecb35d98a688378e56c18c9c159824fd16f730ccbea19aacf4f206e5d5438ed9"
542
+ checksum = "529ae914939b559754e6dd8b34d2705f04d6dcb73f09cb52f024b49eda9b5b88"
542
543
  dependencies = [
543
544
  "cc",
544
- "tree-sitter",
545
+ "tree-sitter-language",
545
546
  ]
546
547
 
547
548
  [[package]]
548
- name = "unicode-ident"
549
- version = "1.0.24"
549
+ name = "willbooster-tree-sitter-rust"
550
+ version = "1.1.1"
550
551
  source = "registry+https://github.com/rust-lang/crates.io-index"
551
- checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
552
+ checksum = "324c9f18cfc29e4da7335ad8cb76beb6ac1ae91a8037a44724bc55fce04f26f7"
553
+ dependencies = [
554
+ "cc",
555
+ "tree-sitter-language",
556
+ ]
552
557
 
553
558
  [[package]]
554
- name = "unicode-segmentation"
555
- version = "1.13.3"
559
+ name = "willbooster-tree-sitter-typescript"
560
+ version = "1.1.1"
556
561
  source = "registry+https://github.com/rust-lang/crates.io-index"
557
- checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
558
-
559
- [[package]]
560
- name = "willbooster-tree-sitter-kotlin"
561
- version = "0.0.0"
562
- source = "git+https://github.com/WillBooster/tree-sitter-kotlin?tag=v1.1.0#a05912e6f07e2cedd44bfb117940361089d4c3e3"
562
+ checksum = "f8c51b6c9e10d6ae34c2f84c4d44fff79a0210890ede63fda558a6a0f042eb28"
563
563
  dependencies = [
564
564
  "cc",
565
565
  "tree-sitter-language",
package/native/Cargo.toml CHANGED
@@ -16,27 +16,22 @@ regex = "1"
16
16
  rustc-hash = "2"
17
17
 
18
18
  # Grammar crates are pinned exactly: every metric (and the golden-based test suite) is calibrated
19
- # against these grammar versions, so upgrades are deliberate, test-verified changes. The Kotlin
20
- # grammar is WillBooster's fork, which is not on crates.io, pinned to a release tag. Core 0.25 is
21
- # the first to load that grammar's ABI 15 parser, and tree-sitter-javascript 0.21.x pins
22
- # `cc ~1.0`, which conflicts with the core's `cc ^1.2`, hence 0.25.0. The core reports no field for
23
- # extra (error-recovery) children in ts_node_field_name_for_child, which
24
- # util::find_children_by_field_name compensates for.
25
- tree-sitter = "=0.25.10"
26
- tree-sitter-c = "=0.21.4"
27
- tree-sitter-c-sharp = "=0.21.3"
28
- tree-sitter-cpp = "=0.22.3"
29
- tree-sitter-go = "=0.21.2"
30
- tree-sitter-java = "=0.21.0"
31
- tree-sitter-kotlin = { package = "willbooster-tree-sitter-kotlin", git = "https://github.com/WillBooster/tree-sitter-kotlin", tag = "v1.1.0" }
32
- tree-sitter-javascript = "=0.25.0"
33
- tree-sitter-python = "=0.21.0"
34
- tree-sitter-ruby = "=0.21.0"
35
- # 0.21.0 breaks MSVC (its build script passes the GCC-only -Wno-unused-parameter
36
- # unconditionally; cl.exe fails with D8021); 0.21.2 gates the flag and the golden-based test
37
- # suite verifies its grammar changes leave every measured metric unchanged.
38
- tree-sitter-rust = "=0.21.2"
39
- tree-sitter-typescript = "=0.21.2"
19
+ # against these grammar versions, so upgrades are deliberate, test-verified changes. The
20
+ # `willbooster-*` crates are WillBooster's forks of the runtime and grammars. Every grammar crate
21
+ # depends only on `tree-sitter-language`, never on upstream's `tree-sitter` crate, whose
22
+ # `links = "tree-sitter"` would conflict with the forked runtime's.
23
+ tree-sitter = { package = "willbooster-tree-sitter", version = "1" }
24
+ tree-sitter-c = { package = "willbooster-tree-sitter-c", version = "=1.1.0" }
25
+ tree-sitter-c-sharp = { package = "willbooster-tree-sitter-c-sharp", version = "=1.0.3" }
26
+ tree-sitter-cpp = { package = "willbooster-tree-sitter-cpp", version = "=1.1.1" }
27
+ tree-sitter-go = "=0.25.0"
28
+ tree-sitter-java = "=0.23.5"
29
+ tree-sitter-javascript = { package = "willbooster-tree-sitter-javascript", version = "=1.0.3" }
30
+ tree-sitter-kotlin = { package = "willbooster-tree-sitter-kotlin", version = "=1.4.0" }
31
+ tree-sitter-python = "=0.25.0"
32
+ tree-sitter-ruby = "=0.23.1"
33
+ tree-sitter-rust = { package = "willbooster-tree-sitter-rust", version = "=1.1.1" }
34
+ tree-sitter-typescript = { package = "willbooster-tree-sitter-typescript", version = "=1.1.1" }
40
35
 
41
36
  # The WebAssembly build (src/wasm.rs) exports a plain C ABI instead of N-API.
42
37
  [target.'cfg(not(target_family = "wasm"))'.dependencies]
Binary file
@@ -93,7 +93,7 @@ const NON_BINDING_PARAMETER_FIELDS: &[&str] = &["type", "value"];
93
93
  /// One leaf of the def-use walk: its field in the parent and its function-scope chain.
94
94
  struct DepDegreeLeaf<'t> {
95
95
  node: Node<'t>,
96
- field_name: Option<&'static str>,
96
+ field_name: Option<&'t str>,
97
97
  /// Chain of nested-function scope ids below the measured function: "" for its own body,
98
98
  /// then "/0", "/0/1", ...
99
99
  scope: String,
@@ -221,7 +221,7 @@ fn implicit_accessor_definitions<'a>(function_node: Node<'_>, code: &Source<'a>)
221
221
  #[allow(clippy::too_many_arguments)]
222
222
  fn collect_dep_degree_leaves<'t>(
223
223
  node: Node<'t>,
224
- field_name: Option<&'static str>,
224
+ field_name: Option<&'t str>,
225
225
  scope: &str,
226
226
  next_scope_id: &mut usize,
227
227
  function_nodes: &FxHashSet<&'static str>,
@@ -234,7 +234,7 @@ fn collect_dep_degree_leaves<'t>(
234
234
  ) {
235
235
  return;
236
236
  }
237
- if node.child_count() == 0 {
237
+ if crate::util::is_token(node) {
238
238
  leaves.push(DepDegreeLeaf {
239
239
  node,
240
240
  field_name,
@@ -341,7 +341,7 @@ fn is_definition_field(holder: Node<'_>, field_name: Option<&str>) -> bool {
341
341
  /// fieldless child; a member pointer's `pointer_type_declarator` is the `name` of a
342
342
  /// `qualified_identifier`; an `array_declarator` size or a nested parameter has another field and
343
343
  /// stops the climb) to the outermost wrapper and its field in the declaration.
344
- fn unwrap_declarator_wrappers<'t>(leaf: &DepDegreeLeaf<'t>) -> (Node<'t>, Option<&'static str>) {
344
+ fn unwrap_declarator_wrappers<'t>(leaf: &DepDegreeLeaf<'t>) -> (Node<'t>, Option<&'t str>) {
345
345
  let mut current = leaf.node;
346
346
  let mut field_name = leaf.field_name;
347
347
  while let Some(parent) = current
@@ -439,11 +439,11 @@ fn is_kotlin_parameter(node: Node<'_>) -> bool {
439
439
  }
440
440
 
441
441
  /// Only called with small-arity parents (declarator wrappers, definition-list holders).
442
- fn field_name_in_parent(node: Node<'_>, parent: Node<'_>) -> Option<&'static str> {
442
+ fn field_name_in_parent<'t>(node: Node<'_>, parent: Node<'t>) -> Option<&'t str> {
443
443
  for index in 0..parent.child_count() {
444
444
  if let Some(child) = parent.child(index) {
445
445
  if child.id() == node.id() {
446
- return parent.field_name_for_child(index as u32);
446
+ return parent.field_name_for_child(index);
447
447
  }
448
448
  }
449
449
  }
@@ -10,7 +10,7 @@ use crate::types::{
10
10
  CrossFileCandidate, CrossFileToken, CrossFileTokenRange, DuplicateBlockOccurrence,
11
11
  DuplicationMetrics,
12
12
  };
13
- use crate::util::{all_children, named_children, node_text, to_int32, Source};
13
+ use crate::util::{all_children, is_token, named_children, node_text, to_int32, Source};
14
14
 
15
15
  /// Block-like nodes considered as whole-subtree duplicate candidates.
16
16
  const DUPLICATE_BLOCK_TYPES: &[&str] = &[
@@ -103,6 +103,7 @@ const STATEMENT_CONTAINER_TYPES: &[&str] = &[
103
103
  "default_case",
104
104
  "compilation_unit",
105
105
  "switch_section",
106
+ "extension_body",
106
107
  "enum_class_body",
107
108
  "function_body",
108
109
  "lambda_literal",
@@ -229,6 +230,8 @@ const STRING_FRAGMENT_TYPES: &[&str] = &[
229
230
  "string_literal_content",
230
231
  "character_literal_content",
231
232
  "raw_string_content",
233
+ "interpreted_string_literal_content",
234
+ "raw_string_literal_content",
232
235
  "raw_string_start",
233
236
  "raw_string_end",
234
237
  "escape_sequence",
@@ -292,6 +295,8 @@ const STRING_CONTENT_FRAGMENT_TYPES: &[&str] = &[
292
295
  "string_literal_content",
293
296
  "character_literal_content",
294
297
  "raw_string_content",
298
+ "interpreted_string_literal_content",
299
+ "raw_string_literal_content",
295
300
  "escape_sequence",
296
301
  "heredoc_content",
297
302
  ];
@@ -548,20 +553,25 @@ fn collect_tokens<'a>(
548
553
  block_ranges: &mut Vec<TokenRange>,
549
554
  container_statement_ranges: &mut Vec<Vec<TokenRange>>,
550
555
  ) {
556
+ /// `statement_list_statements` receives the statements of a Go `statement_list`, which wraps
557
+ /// the statements of blocks and case clauses and whose statements belong to the run of the
558
+ /// enclosing container.
551
559
  fn visit<'a>(
552
560
  node: Node<'_>,
553
561
  code: &Source<'a>,
554
562
  tokens: &mut Vec<Token<'a>>,
555
563
  block_ranges: &mut Vec<TokenRange>,
556
564
  container_statement_ranges: &mut Vec<Vec<TokenRange>>,
565
+ statement_list_statements: &mut Vec<TokenRange>,
557
566
  ) -> TokenRange {
558
567
  let start_token_index = tokens.len();
559
- let atomic_kind = if node.child_count() == 0 {
568
+ let is_token = is_token(node);
569
+ let atomic_kind = if is_token {
560
570
  None
561
571
  } else {
562
572
  atomic_literal_kind(node)
563
573
  };
564
- if node.child_count() == 0 {
574
+ if is_token {
565
575
  append_leaf_token(node, code, tokens);
566
576
  } else if let Some(atomic_kind) = atomic_kind {
567
577
  // Interpolation-free strings collapse to their kind tag so copies differing only in
@@ -575,14 +585,17 @@ fn collect_tokens<'a>(
575
585
  ));
576
586
  } else if !is_tokenless(node, code) {
577
587
  let mut statement_ranges: Vec<TokenRange> = Vec::new();
578
- let is_container = is_statement_container(node);
588
+ let is_statement_list = node.kind_name() == "statement_list";
589
+ let is_container = is_statement_list || is_statement_container(node);
579
590
  for child in all_children(node) {
591
+ let mut child_statements = Vec::new();
580
592
  let child_range = visit(
581
593
  child,
582
594
  code,
583
595
  tokens,
584
596
  block_ranges,
585
597
  container_statement_ranges,
598
+ &mut child_statements,
586
599
  );
587
600
  // A Kotlin lambda's parameters sit beside its statements.
588
601
  if is_container
@@ -590,13 +603,19 @@ fn collect_tokens<'a>(
590
603
  && !is_tokenless(child, code)
591
604
  && child.kind_name() != "lambda_parameters"
592
605
  {
593
- statement_ranges.push(child_range);
606
+ if child.kind_name() == "statement_list" {
607
+ statement_ranges.append(&mut child_statements);
608
+ } else {
609
+ statement_ranges.push(child_range);
610
+ }
594
611
  }
595
612
  }
596
- // Single-statement containers are recorded too: window enumeration needs two
597
- // statements and yields nothing for them, but cross-file matching catalogues each
598
- // container's full run.
599
- if is_container && !statement_ranges.is_empty() {
613
+ if is_statement_list {
614
+ *statement_list_statements = statement_ranges;
615
+ } else if is_container && !statement_ranges.is_empty() {
616
+ // Single-statement containers are recorded too: window enumeration needs two
617
+ // statements and yields nothing for them, but cross-file matching catalogues each
618
+ // container's full run.
600
619
  container_statement_ranges.push(statement_ranges);
601
620
  }
602
621
  }
@@ -615,7 +634,14 @@ fn collect_tokens<'a>(
615
634
  range
616
635
  }
617
636
 
618
- visit(root, code, tokens, block_ranges, container_statement_ranges);
637
+ visit(
638
+ root,
639
+ code,
640
+ tokens,
641
+ block_ranges,
642
+ container_statement_ranges,
643
+ &mut Vec::new(),
644
+ );
619
645
  }
620
646
 
621
647
  /// Comments and dependency declarations contribute no tokens, so no clone consists of them; the
@@ -935,7 +961,7 @@ fn is_semantic_name_leaf(node: Node<'_>, code: &Source<'_>) -> bool {
935
961
  .named_child(0)
936
962
  .is_some_and(|first| first.id() == node.id());
937
963
  let is_last_named = parent
938
- .named_child(parent.named_child_count().saturating_sub(1))
964
+ .named_child(parent.named_child_count().saturating_sub(1) as u32)
939
965
  .is_some_and(|last| last.id() == node.id());
940
966
  if parent.kind_name() == "navigation_expression" && is_last_named && !is_first_named {
941
967
  return true;
@@ -943,7 +969,7 @@ fn is_semantic_name_leaf(node: Node<'_>, code: &Source<'_>) -> bool {
943
969
  if parent.kind_name() == "call_expression"
944
970
  && is_first_named
945
971
  && parent
946
- .named_child(parent.named_child_count().saturating_sub(1))
972
+ .named_child(parent.named_child_count().saturating_sub(1) as u32)
947
973
  .is_some_and(|arguments| {
948
974
  matches!(
949
975
  arguments.kind_name(),