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.
- package/README.md +204 -72
- package/THIRD-PARTY-NOTICES.txt +1152 -798
- package/dist/checkCommand.cjs +5 -0
- package/dist/checkCommand.cjs.map +1 -0
- package/dist/checkCommand.d.ts +12 -0
- package/dist/checkCommand.js +5 -0
- package/dist/checkCommand.js.map +1 -0
- package/dist/cli.cjs +3 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/cliConfig.cjs +1 -1
- package/dist/cliConfig.cjs.map +1 -1
- package/dist/cliConfig.d.ts +3 -11
- package/dist/cliConfig.js +1 -1
- package/dist/cliConfig.js.map +1 -1
- package/dist/duplication.cjs +1 -1
- package/dist/duplication.cjs.map +1 -1
- package/dist/duplication.js +1 -1
- package/dist/duplication.js.map +1 -1
- package/dist/exclusion.cjs +1 -1
- package/dist/exclusion.cjs.map +1 -1
- package/dist/exclusion.d.ts +2 -2
- package/dist/exclusion.js +1 -1
- package/dist/exclusion.js.map +1 -1
- package/dist/git.cjs +1 -1
- package/dist/git.cjs.map +1 -1
- package/dist/git.d.ts +13 -12
- package/dist/git.js +1 -1
- package/dist/git.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.js +1 -1
- package/dist/metrics.cjs +1 -1
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.ts +1 -2
- package/dist/metrics.js +1 -1
- package/dist/metrics.js.map +1 -1
- package/dist/nativeMetrics.cjs +3 -3
- package/dist/nativeMetrics.cjs.map +1 -1
- package/dist/nativeMetrics.js +3 -3
- package/dist/nativeMetrics.js.map +1 -1
- package/dist/scan.cjs +1 -1
- package/dist/scan.cjs.map +1 -1
- package/dist/scan.d.ts +10 -26
- package/dist/scan.js +1 -1
- package/dist/scan.js.map +1 -1
- package/dist/thresholdCheck.cjs +2 -0
- package/dist/thresholdCheck.cjs.map +1 -0
- package/dist/thresholdCheck.d.ts +36 -0
- package/dist/thresholdCheck.js +2 -0
- package/dist/thresholdCheck.js.map +1 -0
- package/dist/thresholds.cjs +2 -0
- package/dist/thresholds.cjs.map +1 -0
- package/dist/thresholds.d.ts +38 -0
- package/dist/thresholds.js +2 -0
- package/dist/thresholds.js.map +1 -0
- package/dist/types.d.ts +8 -4
- package/dist/worker.cjs +1 -1
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/native/Cargo.lock +73 -73
- package/native/Cargo.toml +16 -21
- package/native/code-gauge.wasm +0 -0
- package/native/src/dep_degree.rs +6 -6
- package/native/src/duplication.rs +38 -12
- package/native/src/functions.rs +10 -7
- package/native/src/languages.rs +14 -13
- package/native/src/measure.rs +16 -8
- package/native/src/tree_index.rs +4 -4
- package/native/src/util.rs +9 -2
- package/package.json +10 -9
- package/dist/diffCommand.cjs +0 -5
- package/dist/diffCommand.cjs.map +0 -1
- package/dist/diffCommand.d.ts +0 -17
- package/dist/diffCommand.js +0 -5
- package/dist/diffCommand.js.map +0 -1
- package/dist/newDuplication.cjs +0 -2
- package/dist/newDuplication.cjs.map +0 -1
- package/dist/newDuplication.d.ts +0 -40
- package/dist/newDuplication.js +0 -2
- package/dist/newDuplication.js.map +0 -1
- package/dist/regressionGate.cjs +0 -2
- package/dist/regressionGate.cjs.map +0 -1
- package/dist/regressionGate.d.ts +0 -108
- package/dist/regressionGate.js +0 -2
- 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.
|
|
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
|
|
96
|
-
* one reaching definition
|
|
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")
|
|
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
|
package/dist/worker.cjs.map
CHANGED
|
@@ -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":"
|
|
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
|
|
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
|
package/dist/worker.js.map
CHANGED
|
@@ -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":"
|
|
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
|
|
58
|
-
"tree-sitter-
|
|
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.
|
|
428
|
+
name = "tree-sitter-go"
|
|
429
|
+
version = "0.25.0"
|
|
430
430
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
431
|
-
checksum = "
|
|
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-
|
|
443
|
-
version = "0.
|
|
438
|
+
name = "tree-sitter-java"
|
|
439
|
+
version = "0.23.5"
|
|
444
440
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
445
|
-
checksum = "
|
|
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-
|
|
453
|
-
version = "0.
|
|
448
|
+
name = "tree-sitter-language"
|
|
449
|
+
version = "0.1.8"
|
|
454
450
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
455
|
-
checksum = "
|
|
456
|
-
dependencies = [
|
|
457
|
-
"cc",
|
|
458
|
-
"tree-sitter",
|
|
459
|
-
]
|
|
451
|
+
checksum = "ca0d1bf6fdd806e43ae5198f82f527056d359def39e54e67a0f478ac09dac081"
|
|
460
452
|
|
|
461
453
|
[[package]]
|
|
462
|
-
name = "tree-sitter-
|
|
463
|
-
version = "0.
|
|
454
|
+
name = "tree-sitter-python"
|
|
455
|
+
version = "0.25.0"
|
|
464
456
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
465
|
-
checksum = "
|
|
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-
|
|
473
|
-
version = "0.
|
|
464
|
+
name = "tree-sitter-ruby"
|
|
465
|
+
version = "0.23.1"
|
|
474
466
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
475
|
-
checksum = "
|
|
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 = "
|
|
483
|
-
version = "0.
|
|
474
|
+
name = "unicode-ident"
|
|
475
|
+
version = "1.0.24"
|
|
484
476
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
485
|
-
checksum = "
|
|
486
|
-
dependencies = [
|
|
487
|
-
"cc",
|
|
488
|
-
"tree-sitter",
|
|
489
|
-
]
|
|
477
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
490
478
|
|
|
491
479
|
[[package]]
|
|
492
|
-
name = "
|
|
493
|
-
version = "
|
|
480
|
+
name = "unicode-segmentation"
|
|
481
|
+
version = "1.13.3"
|
|
494
482
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
495
|
-
checksum = "
|
|
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-
|
|
503
|
-
version = "
|
|
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 = "
|
|
502
|
+
checksum = "5cee6f1a4e0f64f337d28aeaed73879d7e8a87d77c287afb989ce29adea54700"
|
|
503
|
+
dependencies = [
|
|
504
|
+
"cc",
|
|
505
|
+
"tree-sitter-language",
|
|
506
|
+
]
|
|
506
507
|
|
|
507
508
|
[[package]]
|
|
508
|
-
name = "tree-sitter-
|
|
509
|
-
version = "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 = "
|
|
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-
|
|
519
|
-
version = "
|
|
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 = "
|
|
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-
|
|
529
|
-
version = "0.
|
|
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 = "
|
|
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-
|
|
539
|
-
version = "
|
|
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 = "
|
|
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 = "
|
|
549
|
-
version = "1.
|
|
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 = "
|
|
552
|
+
checksum = "324c9f18cfc29e4da7335ad8cb76beb6ac1ae91a8037a44724bc55fce04f26f7"
|
|
553
|
+
dependencies = [
|
|
554
|
+
"cc",
|
|
555
|
+
"tree-sitter-language",
|
|
556
|
+
]
|
|
552
557
|
|
|
553
558
|
[[package]]
|
|
554
|
-
name = "
|
|
555
|
-
version = "1.
|
|
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 = "
|
|
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
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# `
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
tree-sitter = "=0.
|
|
26
|
-
tree-sitter-
|
|
27
|
-
tree-sitter-
|
|
28
|
-
tree-sitter-
|
|
29
|
-
tree-sitter-
|
|
30
|
-
tree-sitter-
|
|
31
|
-
tree-sitter-
|
|
32
|
-
tree-sitter-
|
|
33
|
-
tree-sitter-
|
|
34
|
-
tree-sitter-
|
|
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]
|
package/native/code-gauge.wasm
CHANGED
|
Binary file
|
package/native/src/dep_degree.rs
CHANGED
|
@@ -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<&'
|
|
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<&'
|
|
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
|
|
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<&'
|
|
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<'
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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(
|
|
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(),
|