code-gauge 1.13.0 → 1.14.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 +4 -2
- 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 +1 -0
- 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.d.ts +3 -1
- package/dist/duplication.js +1 -1
- package/dist/duplication.js.map +1 -1
- package/dist/metrics.cjs +1 -1
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/package.json +1 -1
package/dist/duplication.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duplication.js","names":[],"sources":["../src/duplication.ts"],"sourcesContent":["import type Parser from 'tree-sitter';\nimport { dedupeByRegion, selectMaximalGroups } from './duplicateSelection.js';\nimport type { DuplicationMetrics, DuplicationOptions } from './types.js';\n\n/**\n * Block-like nodes considered as whole-subtree duplicate candidates. Detection itself is\n * token-based, so this set only decides which subtrees are compared; Ruby's keyword-like node\n * types (`if`, `case`, ...) are safe here because only named nodes become candidates.\n */\nconst duplicateBlockTypes = new Set([\n 'statement_block',\n 'block',\n 'compound_statement',\n 'body_statement',\n 'constructor_body',\n 'do_block',\n 'if_statement',\n 'for_statement',\n 'for_in_statement',\n 'enhanced_for_statement',\n 'for_range_loop',\n 'while_statement',\n 'do_statement',\n 'try_statement',\n 'try_with_resources_statement',\n 'with_statement',\n 'switch_statement',\n 'switch_expression',\n 'switch_case',\n 'switch_block_statement_group',\n 'switch_rule',\n 'case_clause',\n 'case_statement',\n 'match_statement',\n 'match_arm',\n 'except_clause',\n 'catch_clause',\n 'finally_clause',\n 'elif_clause',\n 'ensure',\n 'expression_statement',\n 'return_statement',\n 'return_expression',\n 'if_expression',\n 'for_expression',\n 'while_expression',\n 'loop_expression',\n 'match_expression',\n 'jsx_element',\n 'jsx_self_closing_element',\n // Ruby\n 'if',\n 'unless',\n 'case',\n 'case_match',\n 'while',\n 'until',\n 'for',\n 'begin',\n 'when',\n]);\n\n/** Nodes whose direct named children form statement sequences scanned for copy-pasted runs. */\nconst statementContainerTypes = new Set([\n 'program',\n 'source_file',\n 'translation_unit',\n 'module',\n 'statement_block',\n 'block',\n 'compound_statement',\n 'body_statement',\n 'constructor_body',\n 'class_body',\n 'block_body',\n 'do_block',\n // Ruby loop bodies are a named `do` node, and `ensure` holds statements directly.\n 'do',\n 'ensure',\n 'then',\n 'else',\n // Case-like nodes hold their statements directly, without an inner block.\n 'case_statement',\n 'switch_block_statement_group',\n 'switch_rule',\n 'expression_case',\n 'type_case',\n 'communication_case',\n 'default_case',\n]);\n\n/**\n * Identifier leaves anonymized by occurrence order so consistently renamed copies still match.\n * Member/type names (`property_identifier`, `field_identifier`, `type_identifier`, ...) are kept\n * verbatim instead: calling a different API is a semantic difference, not a rename.\n */\nconst anonymizedIdentifierTypes = new Set([\n 'identifier',\n 'constant',\n 'instance_variable',\n 'class_variable',\n 'global_variable',\n]);\n\n/**\n * JS shorthand properties (`{ alpha }`) both emit the property name (semantic output shape) and\n * reference the binding, so they tokenize as the desugared `name: binding` — one verbatim text\n * token plus one anonymized id token — matching how the explicit form is tokenized.\n */\nconst shorthandPropertyTypes = new Set(['shorthand_property_identifier', 'shorthand_property_identifier_pattern']);\n\n/** Literal leaves normalized to a kind tag so copies differing only in literal values still match. */\nconst literalKindByType = new Map([\n ['number', '#num'],\n ['number_literal', '#num'],\n ['integer', '#num'],\n ['float', '#num'],\n ['integer_literal', '#num'],\n ['float_literal', '#num'],\n ['int_literal', '#num'],\n ['rune_literal', '#char'],\n ['imaginary_literal', '#num'],\n ['decimal_integer_literal', '#num'],\n ['hex_integer_literal', '#num'],\n ['octal_integer_literal', '#num'],\n ['binary_integer_literal', '#num'],\n ['decimal_floating_point_literal', '#num'],\n ['hex_floating_point_literal', '#num'],\n ['string_fragment', '#str'],\n ['multiline_string_fragment', '#str'],\n ['string_content', '#str'],\n ['raw_string_content', '#str'],\n ['heredoc_content', '#str'],\n // Heredoc marker names (`<<~SQL` vs `<<~QUERY`) have no string-value significance.\n ['heredoc_beginning', '#heredoc'],\n ['heredoc_end', '#heredoc'],\n // Strings are leaves in some grammars (Go/Rust) and fragment containers in others.\n ['string', '#str'],\n ['template_string', '#str'],\n ['string_literal', '#str'],\n ['interpreted_string_literal', '#str'],\n ['raw_string_literal', '#str'],\n ['raw_string', '#str'],\n ['escape_sequence', '#str'],\n ['char_literal', '#char'],\n ['character_literal', '#char'],\n ['character', '#char'],\n ['regex_pattern', '#regex'],\n]);\n\n/**\n * Kind tags whose raw source text re-enters the fingerprint in literal-dense (data-like) regions.\n * `#heredoc` is excluded: heredoc marker names are naming choices, not data values.\n */\nconst valueCarryingLiteralKinds = new Set(['#num', '#str', '#char', '#regex']);\n\nconst commentTypes = new Set(['comment', 'line_comment', 'block_comment']);\n\n/**\n * String children that carry actual content, i.e. stringFragmentTypes minus the delimiter nodes\n * (Python's `string_start`/`string_end`), for delimiter-independent literal values.\n */\nconst stringContentFragmentTypes = new Set([\n 'string_fragment',\n 'multiline_string_fragment',\n 'string_content',\n 'raw_string_content',\n 'escape_sequence',\n 'heredoc_content',\n]);\n\n/** Children of a string node that carry only literal content; anything else is interpolation. */\nconst stringFragmentTypes = new Set([\n 'string_fragment',\n 'multiline_string_fragment',\n 'string_content',\n 'raw_string_content',\n 'escape_sequence',\n 'heredoc_content',\n // Python string delimiters are named children; they never carry interpolation.\n 'string_start',\n 'string_end',\n]);\n\n/**\n * Where a grammar names callees/members with a plain `identifier` (Java `method_invocation.name`,\n * Ruby `call.method`, Python `attribute.attribute`, plain calls elsewhere), the leaf in that field\n * must stay verbatim like `property_identifier` does: calling a different API is a semantic\n * difference, not a rename.\n */\nconst semanticNameFieldByParentType = new Map([\n ['call_expression', 'function'],\n ['method_invocation', 'name'],\n ['call', 'method'],\n ['attribute', 'attribute'],\n ['macro_invocation', 'macro'],\n // Java names accessed fields with a plain identifier in the `field` field.\n ['field_access', 'field'],\n // JS/TS `new Foo(...)` names the constructed API in the `constructor` field.\n ['new_expression', 'constructor'],\n // Python `f(timeout=...)` and Java `@Anno(key=...)` name parameters of the callee's API.\n ['keyword_argument', 'name'],\n ['element_value_pair', 'key'],\n // Rust turbofish and C++ template callees (`compute::<u32>(...)`); type arguments stay\n // anonymized via their own node types.\n ['generic_function', 'function'],\n ['template_function', 'name'],\n]);\n\nexport const defaultDuplicationOptions: Required<DuplicationOptions> = {\n minTokens: 40,\n maxGapTokens: 30,\n};\n\n/** Minimum consecutive statements for a statement-sequence duplicate candidate. */\nconst minSequenceStatementCount = 2;\n/**\n * Caps the window length so statement-sequence enumeration stays linear in the statement count.\n * Heterogeneous clones longer than the cap are reported as capped windows (a deliberate\n * conservative undercount trading completeness for bounded discovery cost).\n */\nconst maxSequenceStatementCount = 100;\n\n/**\n * A region whose normalized tokens are at least 20% literal values is data-like (a lookup table, a\n * constant list, a value-mapping switch), not logic: literal values re-enter its fingerprint so\n * tables that merely share their shape stop counting as copy-paste. Logic-heavy code sits well\n * below the bound (5-10% literals) while object/array tables sit above it (25-50%); punctuation\n * and member names dilute tables, which is why the bound is far below half. Compared in integer\n * math (5 * literals >= total) so the TypeScript and native backends cannot disagree on the\n * boundary.\n */\nfunction isLiteralDense(literalCount: number, tokenCount: number): boolean {\n return literalCount * 5 >= tokenCount;\n}\n\ninterface Token {\n /** Normalization target: identifiers to anonymize, literal kind tags, or the raw token text. */\n kind: 'id' | 'text';\n text: string;\n /**\n * Two INDEPENDENT hashes of `text` (djb2 and FNV-1a), precomputed so fingerprinting nested\n * regions never re-hashes a token. Feeding the same per-token hash to both fingerprint\n * accumulators would collapse the key to 32 effective bits: one djb2 collision between two\n * token texts would then equate whole regions.\n */\n textHash: number;\n textHash2: number;\n /** Hash pair of a value-carrying literal's value, folded into data-like region fingerprints. */\n literalHash?: number;\n literalHash2?: number;\n /** 0-based source rows the token occupies, so line coverage counts only matched-token lines. */\n startRow: number;\n endRow: number;\n}\n\ninterface TokenRange {\n startTokenIndex: number;\n endTokenIndex: number;\n node: Parser.SyntaxNode;\n}\n\n/** A contiguous run of matched tokens; gapped (merged) duplicates carry several per occurrence. */\ninterface TokenSegment {\n startTokenIndex: number;\n endTokenIndex: number;\n}\n\ninterface DuplicateCandidate {\n fingerprint: string;\n tokenCount: number;\n startTokenIndex: number;\n endTokenIndex: number;\n startIndex: number;\n endIndex: number;\n startLine: number;\n endLine: number;\n}\n\ninterface CountedOccurrence {\n /** Matched token runs; more than one once gapped groups are merged. */\n segments: TokenSegment[];\n /** Sum of segment token counts (the gap tokens are not matched content). */\n tokenCount: number;\n startTokenIndex: number;\n endTokenIndex: number;\n startIndex: number;\n endIndex: number;\n startLine: number;\n endLine: number;\n}\n\n/** A duplicate region found in one file, exported for cross-file matching by fingerprint. */\nexport interface CrossFileDuplicateCandidate {\n /** Content key: equal fingerprints mean equal normalized token sequences (up to hash collision). */\n fingerprint: string;\n tokenCount: number;\n startIndex: number;\n endIndex: number;\n startLine: number;\n endLine: number;\n}\n\n/**\n * Detects copy-pasted regions within a file. Regions are compared by their normalized token\n * sequence: identifiers are anonymized consistently by first-occurrence order (`a.f(a, b)` matches\n * `x.f(x, y)` but not `x.f(y, z)`), literals are normalized by kind, and member/type names and all\n * keywords/operators are kept verbatim. Literal-dense (data-like) regions additionally require\n * equal literal values. Candidates are whole block-like subtrees plus runs of consecutive sibling\n * statements, so a copy pasted into the middle of a longer block is still found. Only maximal,\n * non-overlapping regions are counted, and adjacent groups separated by a small token gap merge\n * into one gapped (Type-3) clone group.\n */\nexport function measureDuplication(\n root: Parser.SyntaxNode,\n codeLineNumbers: Set<number>,\n options?: DuplicationOptions\n): DuplicationMetrics {\n const minTokens = options?.minTokens ?? defaultDuplicationOptions.minTokens;\n const maxGapTokens = options?.maxGapTokens ?? defaultDuplicationOptions.maxGapTokens;\n const tokens: Token[] = [];\n const blockRanges: TokenRange[] = [];\n const containerStatementRanges: TokenRange[][] = [];\n collectTokens(root, tokens, blockRanges, containerStatementRanges);\n const literalCountPrefix = buildLiteralCountPrefix(tokens);\n\n const candidates = [\n ...collectBlockCandidates(tokens, literalCountPrefix, blockRanges, minTokens),\n ...collectSequenceCandidates(tokens, literalCountPrefix, containerStatementRanges, minTokens),\n ];\n const counted = selectMaximalGroups(candidates, (group) => group.length >= 2);\n const groups = mergeAdjacentGroups(toCountedGroups(counted), maxGapTokens);\n return summarizeDuplicates(groups, codeLineNumbers, tokens);\n}\n\n/**\n * Collects this file's duplicate-candidate fingerprints for cross-file clone detection: whole\n * block-like subtrees plus each statement container's full run (so wholly copied files and class\n * bodies match even when no inner block clears the threshold on its own). Nested and overlapping\n * candidates are all returned; the project-level selection keeps only maximal ones.\n */\nexport function collectCrossFileDuplicateCandidates(\n root: Parser.SyntaxNode,\n options?: DuplicationOptions\n): CrossFileDuplicateCandidate[] {\n const minTokens = options?.minTokens ?? defaultDuplicationOptions.minTokens;\n const tokens: Token[] = [];\n const blockRanges: TokenRange[] = [];\n const containerStatementRanges: TokenRange[][] = [];\n collectTokens(root, tokens, blockRanges, containerStatementRanges);\n const literalCountPrefix = buildLiteralCountPrefix(tokens);\n\n const candidates = collectBlockCandidates(tokens, literalCountPrefix, blockRanges, minTokens);\n for (const statements of containerStatementRanges) {\n const first = statements[0];\n const last = statements.at(-1);\n if (!first || !last) {\n continue;\n }\n const tokenCount = last.endTokenIndex - first.startTokenIndex;\n if (tokenCount < minTokens) {\n continue;\n }\n candidates.push(\n toCandidate(\n `s:${fingerprintKey(tokens, literalCountPrefix, first.startTokenIndex, last.endTokenIndex)}`,\n first.startTokenIndex,\n last.endTokenIndex,\n first.node,\n last.node\n )\n );\n }\n return dedupeByRegion(candidates).map(({ fingerprint, tokenCount, startIndex, endIndex, startLine, endLine }) => ({\n fingerprint,\n tokenCount,\n startIndex,\n endIndex,\n startLine,\n endLine,\n }));\n}\n\nfunction collectTokens(\n root: Parser.SyntaxNode,\n tokens: Token[],\n blockRanges: TokenRange[],\n containerStatementRanges: TokenRange[][]\n): void {\n function visit(node: Parser.SyntaxNode): TokenRange {\n const startTokenIndex = tokens.length;\n const atomicKind = node.childCount === 0 ? undefined : atomicLiteralKind(node);\n if (node.childCount === 0) {\n appendLeafToken(node, tokens);\n } else if (atomicKind !== undefined) {\n // Interpolation-free strings collapse to their kind tag so copies differing only in quote\n // style or content still match; delimiter tokens would otherwise break the equivalence.\n tokens.push(\n makeTextToken(atomicKind, literalValueText(node, atomicKind), node.startPosition.row, node.endPosition.row)\n );\n } else if (!commentTypes.has(node.type)) {\n const statementRanges: TokenRange[] = [];\n const isContainer = node.isNamed && statementContainerTypes.has(node.type);\n for (const child of node.children) {\n const childRange = visit(child);\n if (isContainer && child.isNamed && !commentTypes.has(child.type)) {\n statementRanges.push(childRange);\n }\n }\n // Single-statement containers are recorded too: within-file window enumeration needs two\n // statements and simply yields nothing for them, but cross-file matching must still see a\n // file whose only top-level statement is not a catalogued block type (a lone exported table).\n if (isContainer && statementRanges.length > 0) {\n containerStatementRanges.push(statementRanges);\n }\n }\n\n const range = { startTokenIndex, endTokenIndex: tokens.length, node };\n if (node.isNamed && duplicateBlockTypes.has(node.type)) {\n blockRanges.push(range);\n }\n return range;\n }\n\n visit(root);\n}\n\n/** The kind tag of a string-like node with no interpolation, or undefined to descend normally. */\nfunction atomicLiteralKind(node: Parser.SyntaxNode): string | undefined {\n const kind = node.isNamed ? literalKindByType.get(node.type) : undefined;\n if (kind === undefined) {\n return undefined;\n }\n return node.namedChildren.every((child) => stringFragmentTypes.has(child.type)) ? kind : undefined;\n}\n\nfunction appendLeafToken(node: Parser.SyntaxNode, tokens: Token[]): void {\n if (commentTypes.has(node.type)) {\n return;\n }\n\n const startRow = node.startPosition.row;\n const endRow = node.endPosition.row;\n if (node.isNamed && shorthandPropertyTypes.has(node.type)) {\n tokens.push(\n makeTextToken(node.text, undefined, startRow, endRow),\n makeTextToken(':', undefined, startRow, endRow),\n { kind: 'id', text: node.text, textHash: 0, textHash2: 0, startRow, endRow }\n );\n return;\n }\n\n if (node.isNamed && anonymizedIdentifierTypes.has(node.type) && !isSemanticNameLeaf(node)) {\n tokens.push({ kind: 'id', text: node.text, textHash: 0, textHash2: 0, startRow, endRow });\n return;\n }\n\n // Anything else keeps its text: keywords, operators, punctuation, and semantic names such as\n // `property_identifier`/`type_identifier`, which must distinguish otherwise-identical structures.\n const literalKind = node.isNamed ? literalKindByType.get(node.type) : undefined;\n if (literalKind === undefined) {\n tokens.push(makeTextToken(node.text, undefined, startRow, endRow));\n } else {\n tokens.push(makeTextToken(literalKind, literalValueText(node, literalKind), startRow, endRow));\n }\n}\n\nfunction makeTextToken(text: string, literalValueText: string | undefined, startRow: number, endRow: number): Token {\n const token: Token = { kind: 'text', text, textHash: hashText(text), textHash2: hashText2(text), startRow, endRow };\n if (literalValueText !== undefined && valueCarryingLiteralKinds.has(text)) {\n token.literalHash = hashText(literalValueText);\n token.literalHash2 = hashText2(literalValueText);\n }\n return token;\n}\n\n/**\n * The value of a literal as folded into literal-dense fingerprints. Strings hash their CONTENT,\n * not their source spelling: formatters rewrite quote style on paste (`'one'` vs `\"one\"`), so\n * delimiters must not make two copied tables differ. Content comes from the fragment children when\n * the grammar provides them (which also drops Python's `string_start`/`string_end` delimiter\n * nodes), else from the text with one matching pair of surrounding quotes stripped. Numbers keep\n * their raw text: formatters preserve numeric spelling, and canonicalizing values (`0x10` vs `16`)\n * identically in JavaScript and Rust would be far riskier than the rare mismatch it would unify.\n */\nfunction literalValueText(node: Parser.SyntaxNode, kind: string): string {\n if (kind !== '#str' && kind !== '#char') {\n return node.text;\n }\n // Fragment leaves (string_fragment, escape_sequence, heredoc_content, ...) already carry bare\n // content; a quote appearing there is content, not a delimiter.\n if (stringContentFragmentTypes.has(node.type)) {\n return node.text;\n }\n const fragments = node.namedChildren.filter((child) => stringContentFragmentTypes.has(child.type));\n if (fragments.length > 0) {\n return fragments.map((child) => child.text).join('');\n }\n return stripMatchingQuotes(node.text);\n}\n\nconst quoteCharacters = new Set(['\"', \"'\", '`']);\n\nfunction stripMatchingQuotes(text: string): string {\n const first = text[0];\n return text.length >= 2 && first !== undefined && quoteCharacters.has(first) && text.endsWith(first)\n ? text.slice(1, -1)\n : text;\n}\n\n/** literalCountPrefix[i] = value-carrying literal tokens in tokens[0..i), for O(1) density checks. */\nfunction buildLiteralCountPrefix(tokens: Token[]): Int32Array {\n const prefix = new Int32Array(tokens.length + 1);\n for (const [index, token] of tokens.entries()) {\n prefix[index + 1] = (prefix[index] ?? 0) + (token.literalHash === undefined ? 0 : 1);\n }\n return prefix;\n}\n\nfunction isSemanticNameLeaf(node: Parser.SyntaxNode): boolean {\n const parent = node.parent;\n if (!parent) {\n return false;\n }\n\n // Java method references (`Foo::bar`) name their identifiers without grammar fields; both the\n // type/object and the referenced method are semantic.\n if (parent.type === 'method_reference') {\n return true;\n }\n\n // `call` names its callee `method` in Ruby but `function` in Python; accept both fields.\n if (parent.type === 'call' && parent.childForFieldName('function')?.id === node.id) {\n return true;\n }\n\n // A Ruby constant receiving a call (`Alpha.new(...)`) names the invoked API; constants used as\n // plain values stay anonymized so renamed clones referencing constants still match.\n if (node.type === 'constant' && parent.type === 'call' && parent.childForFieldName('receiver')?.id === node.id) {\n return true;\n }\n\n // Java static receivers (`Alpha.run(...)`) name the invoked type. The tokenizer has no symbol\n // table, so PascalCase — Java's universal type-naming convention — is the discriminator;\n // camelCase instance receivers stay anonymized for rename tolerance.\n if (\n parent.type === 'method_invocation' &&\n parent.childForFieldName('object')?.id === node.id &&\n /^\\p{Lu}/u.test(node.text)\n ) {\n return true;\n }\n\n // Qualified callees (Rust `crate::alpha::make(...)`, C++ `detail::make(...)`) and generic\n // callees (`compute::<u32>(...)`, `compute<int>(...)`) wrap their identifiers arbitrarily deep;\n // every path/name segment is semantic there — but only in call position, so renamed clones that\n // merely reference scoped constants or `use` paths still match.\n if (\n (parent.type === 'scoped_identifier' || parent.type === 'qualified_identifier') &&\n (parent.childForFieldName('name')?.id === node.id || parent.childForFieldName('path')?.id === node.id)\n ) {\n let outer = parent;\n while (\n outer.parent &&\n (outer.parent.type === 'scoped_identifier' ||\n outer.parent.type === 'qualified_identifier' ||\n outer.parent.type === 'generic_function' ||\n outer.parent.type === 'template_function')\n ) {\n outer = outer.parent;\n }\n if (outer.parent?.type === 'call_expression' && outer.parent.childForFieldName('function')?.id === outer.id) {\n return true;\n }\n }\n\n // Go struct-literal keys (`Config{Timeout: ...}`) have no `key` field in the grammar: the key\n // is the keyed_element's first named child, a literal_element wrapping the identifier.\n if (\n parent.type === 'literal_element' &&\n parent.parent?.type === 'keyed_element' &&\n parent.parent.namedChild(0)?.id === parent.id\n ) {\n return true;\n }\n\n const field = semanticNameFieldByParentType.get(parent.type);\n return field !== undefined && parent.childForFieldName(field)?.id === node.id;\n}\n\nfunction collectBlockCandidates(\n tokens: Token[],\n literalCountPrefix: Int32Array,\n blockRanges: TokenRange[],\n minTokens: number\n): DuplicateCandidate[] {\n const candidates: DuplicateCandidate[] = [];\n for (const range of blockRanges) {\n const tokenCount = range.endTokenIndex - range.startTokenIndex;\n if (tokenCount < minTokens) {\n continue;\n }\n candidates.push(\n toCandidate(\n `b:${fingerprintKey(tokens, literalCountPrefix, range.startTokenIndex, range.endTokenIndex)}`,\n range.startTokenIndex,\n range.endTokenIndex,\n range.node,\n range.node\n )\n );\n }\n return candidates;\n}\n\ninterface WindowOccurrences {\n count: number;\n /** -1 once occurrences span more than one container. */\n containerIndex: number;\n minStart: number;\n maxStart: number;\n}\n\ninterface SequenceWindow {\n containerIndex: number;\n start: number;\n length: number;\n}\n\n/**\n * Enumerates runs of consecutive sibling statements. Every container statement participates; only\n * the window length is capped, so enumeration stays linear in the statement count. Windows are\n * grouped by a cheap rolling hash of per-statement fingerprints, and only locally maximal repeated\n * windows — those whose one-statement extensions stop repeating — become candidates with an exact\n * (window-consistent) fingerprint. Without the maximality filter a degenerate file of\n * near-identical statements would fingerprint every sub-window of every repeated region.\n */\nfunction collectSequenceCandidates(\n tokens: Token[],\n literalCountPrefix: Int32Array,\n containers: TokenRange[][],\n minTokens: number\n): DuplicateCandidate[] {\n const candidates: DuplicateCandidate[] = [];\n const occurrencesByWindowKey = new Map<number, WindowOccurrences>();\n const containerWindows = containers.map((statements) => enumerateContainerWindows(tokens, statements, minTokens));\n for (const [containerIndex, windows] of containerWindows.entries()) {\n for (const [start, row] of windows.windowKeysByStart.entries()) {\n for (const windowKey of row) {\n if (windowKey === undefined) {\n continue;\n }\n const occurrences = occurrencesByWindowKey.get(windowKey);\n if (occurrences) {\n occurrences.count += 1;\n if (occurrences.containerIndex !== containerIndex) {\n occurrences.containerIndex = -1;\n }\n occurrences.minStart = Math.min(occurrences.minStart, start);\n occurrences.maxStart = Math.max(occurrences.maxStart, start);\n } else {\n occurrencesByWindowKey.set(windowKey, { count: 1, containerIndex, minStart: start, maxStart: start });\n }\n }\n }\n }\n\n // A window only \"repeats\" when two of its occurrences can coexist without overlapping: sliding\n // matches inside a homogeneous run (start spread smaller than the window length) can never both\n // be counted and must neither qualify a window nor dominate its sub-windows.\n const repeats = (windowKey: number | undefined, length: number): boolean => {\n if (windowKey === undefined) {\n return false;\n }\n const occurrences = occurrencesByWindowKey.get(windowKey);\n return (\n occurrences !== undefined &&\n occurrences.count >= 2 &&\n (occurrences.containerIndex === -1 || occurrences.maxStart - occurrences.minStart >= length)\n );\n };\n\n // A window whose statements all share one normalized shape (sixteen `let x = 0;` declarations,\n // a constant table) is a homogeneous preamble, not a copy-paste: requiring two distinct\n // per-statement shapes keeps such runs out of duplicate groups and the duplication ratio.\n const hasDistinctStatements = (window: SequenceWindow): boolean => {\n const hashes = containerWindows[window.containerIndex]?.statementHashes ?? [];\n const firstHash = hashes[window.start];\n for (let index = window.start + 1; index < window.start + window.length; index += 1) {\n if (hashes[index] !== firstHash) {\n return true;\n }\n }\n return false;\n };\n\n const maximalWindows: SequenceWindow[] = [];\n for (const [containerIndex, windows] of containerWindows.entries()) {\n for (const [start, row] of windows.windowKeysByStart.entries()) {\n for (const [length, windowKey] of row.entries()) {\n if (!repeats(windowKey, length) || !hasDistinctStatements({ containerIndex, start, length })) {\n continue;\n }\n // Dominated windows are skipped: the one-statement extension also repeats, so a larger\n // candidate covering this window exists.\n const extendedRight = windows.windowKeysByStart[start]?.[length + 1];\n const extendedLeft = windows.windowKeysByStart[start - 1]?.[length + 1];\n if (repeats(extendedRight, length + 1) || repeats(extendedLeft, length + 1)) {\n continue;\n }\n maximalWindows.push({ containerIndex, start, length });\n }\n }\n }\n\n // The rolling hash anonymizes identifiers per statement, so a window can look repeated coarsely\n // while its exact (window-consistent) fingerprints differ, and a longer window's match can\n // dominate sub-windows that other copies still need (three copies where only two extend one\n // statement further). Every emitted window therefore exposes its repeating, unvisited\n // sub-windows; `visited` bounds the worklist and lengths strictly decrease, so it terminates.\n const visited = new Set(maximalWindows.map(windowId));\n let frontier = maximalWindows;\n while (frontier.length > 0) {\n const emitted: SequenceWindow[] = [];\n for (const window of frontier) {\n const statements = containers[window.containerIndex];\n const first = statements?.[window.start];\n const last = statements?.[window.start + window.length - 1];\n if (!first || !last) {\n continue;\n }\n const fingerprint = `s:${fingerprintKey(tokens, literalCountPrefix, first.startTokenIndex, last.endTokenIndex)}`;\n candidates.push(toCandidate(fingerprint, first.startTokenIndex, last.endTokenIndex, first.node, last.node));\n emitted.push(window);\n }\n frontier = [];\n for (const window of emitted) {\n for (const start of [window.start, window.start + 1]) {\n const subWindow = { containerIndex: window.containerIndex, start, length: window.length - 1 };\n const subWindowKey = containerWindows[window.containerIndex]?.windowKeysByStart[start]?.[subWindow.length];\n if (\n visited.has(windowId(subWindow)) ||\n !repeats(subWindowKey, subWindow.length) ||\n !hasDistinctStatements(subWindow)\n ) {\n continue;\n }\n visited.add(windowId(subWindow));\n frontier.push(subWindow);\n }\n }\n }\n return candidates;\n}\n\nfunction windowId(window: SequenceWindow): string {\n return `${window.containerIndex}:${window.start}:${window.length}`;\n}\n\ninterface ContainerWindows {\n /** windowKeysByStart[start][length] is the rolling-hash key of the window, or undefined if it is below the size thresholds. */\n windowKeysByStart: (number | undefined)[][];\n /** Per-statement fingerprint hashes, for the distinct-shape requirement on windows. */\n statementHashes: number[];\n}\n\nfunction enumerateContainerWindows(tokens: Token[], statements: TokenRange[], minTokens: number): ContainerWindows {\n const statementHashes = statements.map((statement) =>\n fingerprintHash(tokens, statement.startTokenIndex, statement.endTokenIndex)\n );\n const windowKeysByStart: (number | undefined)[][] = [];\n for (let start = 0; start < statements.length; start += 1) {\n const row: (number | undefined)[] = [];\n let hash = 5381;\n let tokenCount = 0;\n const maxEnd = Math.min(statements.length, start + maxSequenceStatementCount);\n for (let end = start; end < maxEnd; end += 1) {\n const statement = statements[end];\n const statementHash = statementHashes[end];\n if (!statement || statementHash === undefined) {\n break;\n }\n hash = combineHashes(hash, statementHash);\n tokenCount += statement.endTokenIndex - statement.startTokenIndex;\n const statementCount = end - start + 1;\n row[statementCount] =\n statementCount >= minSequenceStatementCount && tokenCount >= minTokens\n ? combineHashes(hash, statementCount)\n : undefined;\n }\n windowKeysByStart.push(row);\n }\n return { windowKeysByStart, statementHashes };\n}\n\nfunction toCandidate(\n fingerprint: string,\n startTokenIndex: number,\n endTokenIndex: number,\n firstNode: Parser.SyntaxNode,\n lastNode: Parser.SyntaxNode\n): DuplicateCandidate {\n return {\n fingerprint,\n tokenCount: endTokenIndex - startTokenIndex,\n startTokenIndex,\n endTokenIndex,\n startIndex: firstNode.startIndex,\n endIndex: lastNode.endIndex,\n startLine: firstNode.startPosition.row + 1,\n endLine: lastNode.endPosition.row + 1,\n };\n}\n\n/** Caches of hashText/hashText2 over '$0', '$1', ... so anonymized identifiers hash without allocating. */\nconst anonymizedIndexHashes: number[] = [];\nconst anonymizedIndexHashes2: number[] = [];\n\nfunction anonymizedIndexHash(index: number): number {\n let hash = anonymizedIndexHashes[index];\n if (hash === undefined) {\n hash = hashText(`$${index}`);\n anonymizedIndexHashes[index] = hash;\n }\n return hash;\n}\n\nfunction anonymizedIndexHash2(index: number): number {\n let hash = anonymizedIndexHashes2[index];\n if (hash === undefined) {\n hash = hashText2(`$${index}`);\n anonymizedIndexHashes2[index] = hash;\n }\n return hash;\n}\n\n/**\n * Content key of a token range: two independent 32-bit hashes over the normalized token sequence\n * (identifiers anonymized consistently by first-occurrence order) plus the token count. Regions\n * with equal keys are treated as equal content; a collision would need both 32-bit hashes and the\n * length to coincide, which is negligible for a metrics report. Hashing per-token instead of\n * serializing the whole range to a string keeps fingerprinting allocation-free for nested regions.\n */\nfunction fingerprintKey(\n tokens: Token[],\n literalCountPrefix: Int32Array,\n startTokenIndex: number,\n endTokenIndex: number\n): string {\n const literalCount = (literalCountPrefix[endTokenIndex] ?? 0) - (literalCountPrefix[startTokenIndex] ?? 0);\n const literalDense = isLiteralDense(literalCount, endTokenIndex - startTokenIndex);\n const [primary, secondary] = fingerprintHashPair(tokens, startTokenIndex, endTokenIndex, literalDense);\n return `${primary}:${secondary}:${endTokenIndex - startTokenIndex}`;\n}\n\n/**\n * A single 32-bit summary of a range for the coarse rolling-hash phase. Deliberately\n * density-agnostic: density is a property of the final candidate REGION, and folding literal\n * values into per-statement hashes would make a dense statement inside a logic-heavy window\n * (`const weights = [1, 2, 3];`) block the window from ever being enumerated. The coarse phase\n * over-approximates on shape alone; the exact region fingerprint still applies the density rule.\n */\nfunction fingerprintHash(tokens: Token[], startTokenIndex: number, endTokenIndex: number): number {\n const [primary, secondary] = fingerprintHashPair(tokens, startTokenIndex, endTokenIndex, false);\n // XOR already coerces to int32, matching the native backend's i32 arithmetic.\n return primary ^ Math.imul(secondary, 31);\n}\n\nfunction fingerprintHashPair(\n tokens: Token[],\n startTokenIndex: number,\n endTokenIndex: number,\n foldLiteralValues: boolean\n): [number, number] {\n const indexByIdentifier = new Map<string, number>();\n let primary = 5381;\n let secondary = 52_711;\n for (let index = startTokenIndex; index < endTokenIndex; index += 1) {\n const token = tokens[index];\n if (!token) {\n continue;\n }\n // Each accumulator consumes its own independent per-token hash: sharing one would collapse\n // the key to 32 effective bits (a single djb2 collision would equate whole regions).\n let part: number;\n let part2: number;\n if (token.kind === 'id') {\n let identifierIndex = indexByIdentifier.get(token.text);\n if (identifierIndex === undefined) {\n identifierIndex = indexByIdentifier.size;\n indexByIdentifier.set(token.text, identifierIndex);\n }\n part = anonymizedIndexHash(identifierIndex);\n part2 = anonymizedIndexHash2(identifierIndex);\n } else {\n part = token.textHash;\n part2 = token.textHash2;\n }\n // oxlint-disable-next-line unicorn/prefer-math-trunc -- `| 0` wraps the sum to int32 (Math.trunc does not), which must match the native backend's wrapping i32 arithmetic.\n primary = (Math.imul(primary, 31) + part) | 0;\n secondary = Math.imul(secondary, 37) ^ part2;\n if (foldLiteralValues && token.literalHash !== undefined && token.literalHash2 !== undefined) {\n // oxlint-disable-next-line unicorn/prefer-math-trunc -- `| 0` wraps the sum to int32 (Math.trunc does not), which must match the native backend's wrapping i32 arithmetic.\n primary = (Math.imul(primary, 31) + token.literalHash) | 0;\n secondary = Math.imul(secondary, 37) ^ token.literalHash2;\n }\n }\n return [primary, secondary];\n}\n\n/** djb2-style hash; XOR keeps the value in signed 32-bit range, which is fine for a grouping key. */\nfunction hashText(text: string): number {\n let hash = 5381;\n for (let index = 0; index < text.length; index += 1) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- djb2 hashes UTF-16 code units; codePointAt would hash surrogate pairs twice (full code point, then the lone low surrogate).\n hash = Math.imul(hash, 33) ^ text.charCodeAt(index);\n }\n return hash;\n}\n\n/** FNV-1a over UTF-16 code units: independent of hashText so the two accumulators never share input. */\nfunction hashText2(text: string): number {\n let hash = -2_128_831_035; // 2166136261 as int32 (the FNV-1a offset basis)\n for (let index = 0; index < text.length; index += 1) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- hashes UTF-16 code units like hashText.\n hash = Math.imul(hash ^ text.charCodeAt(index), 16_777_619);\n }\n return hash;\n}\n\nfunction combineHashes(hash: number, value: number): number {\n return Math.imul(hash, 31) + value;\n}\n\nfunction toCountedGroups(counted: Map<string, DuplicateCandidate[]>): CountedOccurrence[][] {\n const groups: CountedOccurrence[][] = [];\n for (const group of counted.values()) {\n const occurrences = group.map((candidate) => ({\n segments: [{ startTokenIndex: candidate.startTokenIndex, endTokenIndex: candidate.endTokenIndex }],\n tokenCount: candidate.tokenCount,\n startTokenIndex: candidate.startTokenIndex,\n endTokenIndex: candidate.endTokenIndex,\n startIndex: candidate.startIndex,\n endIndex: candidate.endIndex,\n startLine: candidate.startLine,\n endLine: candidate.endLine,\n }));\n occurrences.sort(\n (left, right) => left.startTokenIndex - right.startTokenIndex || left.endTokenIndex - right.endTokenIndex\n );\n groups.push(occurrences);\n }\n return groups;\n}\n\n/**\n * Merges duplicate groups separated by a small token gap into one gapped (Type-3) clone group: a\n * copy edited in one spot splits into two exact groups whose occurrences sit side by side in the\n * same order. Two groups merge when they have the same number of occurrences and, pairing\n * occurrences in source order, every pair is gap-adjacent without crossing into the next pair.\n * Merging repeats to a fixpoint so a clone edited in several spots still reassembles. Gap tokens\n * are not matched content: line coverage and sizes count only the matched segments.\n */\nfunction mergeAdjacentGroups(groups: CountedOccurrence[][], maxGapTokens: number): CountedOccurrence[][] {\n if (maxGapTokens <= 0 || groups.length < 2) {\n return groups;\n }\n // Deterministic processing order (mirrored by the native backend): by first occurrence position.\n groups.sort(compareGroups);\n for (let restart = true; restart;) {\n restart = false;\n for (let leftIndex = 0; leftIndex < groups.length && !restart; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < groups.length; rightIndex += 1) {\n const left = groups[leftIndex];\n const right = groups[rightIndex];\n if (!left || !right) {\n continue;\n }\n const merged = mergeGroups(left, right, maxGapTokens) ?? mergeGroups(right, left, maxGapTokens);\n if (merged) {\n groups[leftIndex] = merged;\n groups.splice(rightIndex, 1);\n groups.sort(compareGroups);\n restart = true;\n break;\n }\n }\n }\n }\n return groups;\n}\n\nfunction compareGroups(left: CountedOccurrence[], right: CountedOccurrence[]): number {\n const leftFirst = left[0];\n const rightFirst = right[0];\n return (\n (leftFirst?.startTokenIndex ?? 0) - (rightFirst?.startTokenIndex ?? 0) ||\n (leftFirst?.endTokenIndex ?? 0) - (rightFirst?.endTokenIndex ?? 0)\n );\n}\n\n/** The merged group when every `second` occurrence gap-follows its `first` counterpart, else undefined. */\nfunction mergeGroups(\n first: CountedOccurrence[],\n second: CountedOccurrence[],\n maxGapTokens: number\n): CountedOccurrence[] | undefined {\n if (first.length !== second.length) {\n return undefined;\n }\n for (const [index, leading] of first.entries()) {\n const trailing = second[index];\n if (!trailing) {\n return undefined;\n }\n const gap = trailing.startTokenIndex - leading.endTokenIndex;\n if (gap < 0 || gap > maxGapTokens) {\n return undefined;\n }\n // The merged span must stay clear of the next pair, or spans would overlap.\n const next = first[index + 1];\n if (next && trailing.endTokenIndex > next.startTokenIndex) {\n return undefined;\n }\n }\n return first.map((leading, index) => {\n const trailing = second[index];\n if (!trailing) {\n return leading;\n }\n return {\n segments: [...leading.segments, ...trailing.segments],\n tokenCount: leading.tokenCount + trailing.tokenCount,\n startTokenIndex: leading.startTokenIndex,\n endTokenIndex: trailing.endTokenIndex,\n startIndex: leading.startIndex,\n endIndex: trailing.endIndex,\n startLine: leading.startLine,\n endLine: trailing.endLine,\n };\n });\n}\n\nfunction summarizeDuplicates(\n groups: CountedOccurrence[][],\n codeLineNumbers: Set<number>,\n tokens: Token[]\n): DuplicationMetrics {\n let duplicateBlockCount = 0;\n let maxDuplicateBlockSize = 0;\n const duplicateBlockGroups: { startLine: number; endLine: number }[][] = [];\n const duplicatedLines = new Set<number>();\n for (const group of groups) {\n // Each redundant occurrence contributes one count per matched fragment, so merging a gapped\n // clone's fragments into one group does not halve the count a `duplicateBlock` threshold sees:\n // an edited two-fragment pair still counts 2, exactly as its unmerged fragments did.\n duplicateBlockCount += (group.length - 1) * (group[0]?.segments.length ?? 1);\n for (const occurrence of group) {\n maxDuplicateBlockSize = Math.max(maxDuplicateBlockSize, occurrence.tokenCount);\n // Only CODE lines carrying matched tokens count: comments and blank gaps inside an\n // occurrence's bounding range — the unmatched gap of a merged clone, and blank rows inside a\n // multi-row token (heredocs, template literals) — are not duplicated content and would push\n // the ratio past 1.\n for (const segment of occurrence.segments) {\n for (let index = segment.startTokenIndex; index < segment.endTokenIndex; index += 1) {\n const token = tokens[index];\n for (let row = token?.startRow ?? 0; row <= (token?.endRow ?? -1); row += 1) {\n if (codeLineNumbers.has(row + 1)) {\n duplicatedLines.add(row + 1);\n }\n }\n }\n }\n }\n duplicateBlockGroups.push(\n group\n .map(({ startLine, endLine }) => ({ startLine, endLine }))\n .toSorted((left, right) => left.startLine - right.startLine)\n );\n }\n duplicateBlockGroups.sort((left, right) => (left[0]?.startLine ?? 0) - (right[0]?.startLine ?? 0));\n\n return {\n duplicateBlockCount,\n duplicateBlockGroupCount: groups.length,\n duplicateBlockGroups,\n duplicateLineCount: duplicatedLines.size,\n duplicationRatio: codeLineNumbers.size === 0 ? 0 : duplicatedLines.size / codeLineNumbers.size,\n maxDuplicateBlockSize,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"duplication.js","names":[],"sources":["../src/duplication.ts"],"sourcesContent":["import type Parser from 'tree-sitter';\nimport { dedupeByRegion, selectMaximalGroups } from './duplicateSelection.js';\nimport type { DuplicationMetrics, DuplicationOptions } from './types.js';\n\n/**\n * Block-like nodes considered as whole-subtree duplicate candidates. Detection itself is\n * token-based, so this set only decides which subtrees are compared; Ruby's keyword-like node\n * types (`if`, `case`, ...) are safe here because only named nodes become candidates.\n */\nconst duplicateBlockTypes = new Set([\n 'statement_block',\n 'block',\n 'compound_statement',\n 'body_statement',\n 'constructor_body',\n 'do_block',\n 'if_statement',\n 'for_statement',\n 'for_in_statement',\n 'enhanced_for_statement',\n 'for_range_loop',\n 'while_statement',\n 'do_statement',\n 'try_statement',\n 'try_with_resources_statement',\n 'with_statement',\n 'switch_statement',\n 'switch_expression',\n 'switch_case',\n 'switch_block_statement_group',\n 'switch_rule',\n 'case_clause',\n 'case_statement',\n 'match_statement',\n 'match_arm',\n 'except_clause',\n 'catch_clause',\n 'finally_clause',\n 'elif_clause',\n 'ensure',\n 'expression_statement',\n 'return_statement',\n 'return_expression',\n 'if_expression',\n 'for_expression',\n 'while_expression',\n 'loop_expression',\n 'match_expression',\n 'jsx_element',\n 'jsx_self_closing_element',\n // Ruby\n 'if',\n 'unless',\n 'case',\n 'case_match',\n 'while',\n 'until',\n 'for',\n 'begin',\n 'when',\n]);\n\n/** Nodes whose direct named children form statement sequences scanned for copy-pasted runs. */\nconst statementContainerTypes = new Set([\n 'program',\n 'source_file',\n 'translation_unit',\n 'module',\n 'statement_block',\n 'block',\n 'compound_statement',\n 'body_statement',\n 'constructor_body',\n 'class_body',\n 'block_body',\n 'do_block',\n // Ruby loop bodies are a named `do` node, and `ensure` holds statements directly.\n 'do',\n 'ensure',\n 'then',\n 'else',\n // Case-like nodes hold their statements directly, without an inner block.\n 'case_statement',\n 'switch_block_statement_group',\n 'switch_rule',\n 'expression_case',\n 'type_case',\n 'communication_case',\n 'default_case',\n]);\n\n/**\n * Identifier leaves anonymized by occurrence order so consistently renamed copies still match.\n * Member/type names (`property_identifier`, `field_identifier`, `type_identifier`, ...) are kept\n * verbatim instead: calling a different API is a semantic difference, not a rename.\n */\nconst anonymizedIdentifierTypes = new Set([\n 'identifier',\n 'constant',\n 'instance_variable',\n 'class_variable',\n 'global_variable',\n]);\n\n/**\n * JS shorthand properties (`{ alpha }`) both emit the property name (semantic output shape) and\n * reference the binding, so they tokenize as the desugared `name: binding` — one verbatim text\n * token plus one anonymized id token — matching how the explicit form is tokenized.\n */\nconst shorthandPropertyTypes = new Set(['shorthand_property_identifier', 'shorthand_property_identifier_pattern']);\n\n/** Literal leaves normalized to a kind tag so copies differing only in literal values still match. */\nconst literalKindByType = new Map([\n ['number', '#num'],\n ['number_literal', '#num'],\n ['integer', '#num'],\n ['float', '#num'],\n ['integer_literal', '#num'],\n ['float_literal', '#num'],\n ['int_literal', '#num'],\n ['rune_literal', '#char'],\n ['imaginary_literal', '#num'],\n ['decimal_integer_literal', '#num'],\n ['hex_integer_literal', '#num'],\n ['octal_integer_literal', '#num'],\n ['binary_integer_literal', '#num'],\n ['decimal_floating_point_literal', '#num'],\n ['hex_floating_point_literal', '#num'],\n ['string_fragment', '#str'],\n ['multiline_string_fragment', '#str'],\n ['string_content', '#str'],\n ['raw_string_content', '#str'],\n ['heredoc_content', '#str'],\n // Heredoc marker names (`<<~SQL` vs `<<~QUERY`) have no string-value significance.\n ['heredoc_beginning', '#heredoc'],\n ['heredoc_end', '#heredoc'],\n // Strings are leaves in some grammars (Go/Rust) and fragment containers in others.\n ['string', '#str'],\n ['template_string', '#str'],\n ['string_literal', '#str'],\n ['interpreted_string_literal', '#str'],\n ['raw_string_literal', '#str'],\n ['raw_string', '#str'],\n ['escape_sequence', '#str'],\n ['char_literal', '#char'],\n ['character_literal', '#char'],\n ['character', '#char'],\n ['regex_pattern', '#regex'],\n]);\n\n/**\n * Kind tags whose raw source text re-enters the fingerprint in literal-dense (data-like) regions.\n * `#heredoc` is excluded: heredoc marker names are naming choices, not data values.\n */\nconst valueCarryingLiteralKinds = new Set(['#num', '#str', '#char', '#regex']);\n\nconst commentTypes = new Set(['comment', 'line_comment', 'block_comment']);\n\n/**\n * String children that carry actual content, i.e. stringFragmentTypes minus the delimiter nodes\n * (Python's `string_start`/`string_end`), for delimiter-independent literal values.\n */\nconst stringContentFragmentTypes = new Set([\n 'string_fragment',\n 'multiline_string_fragment',\n 'string_content',\n 'raw_string_content',\n 'escape_sequence',\n 'heredoc_content',\n]);\n\n/** Children of a string node that carry only literal content; anything else is interpolation. */\nconst stringFragmentTypes = new Set([\n 'string_fragment',\n 'multiline_string_fragment',\n 'string_content',\n 'raw_string_content',\n 'escape_sequence',\n 'heredoc_content',\n // Python string delimiters are named children; they never carry interpolation.\n 'string_start',\n 'string_end',\n]);\n\n/**\n * Where a grammar names callees/members with a plain `identifier` (Java `method_invocation.name`,\n * Ruby `call.method`, Python `attribute.attribute`, plain calls elsewhere), the leaf in that field\n * must stay verbatim like `property_identifier` does: calling a different API is a semantic\n * difference, not a rename.\n */\nconst semanticNameFieldByParentType = new Map([\n ['call_expression', 'function'],\n ['method_invocation', 'name'],\n ['call', 'method'],\n ['attribute', 'attribute'],\n ['macro_invocation', 'macro'],\n // Java names accessed fields with a plain identifier in the `field` field.\n ['field_access', 'field'],\n // JS/TS `new Foo(...)` names the constructed API in the `constructor` field.\n ['new_expression', 'constructor'],\n // Python `f(timeout=...)` and Java `@Anno(key=...)` name parameters of the callee's API.\n ['keyword_argument', 'name'],\n ['element_value_pair', 'key'],\n // Rust turbofish and C++ template callees (`compute::<u32>(...)`); type arguments stay\n // anonymized via their own node types.\n ['generic_function', 'function'],\n ['template_function', 'name'],\n]);\n\nexport const defaultDuplicationOptions: Required<DuplicationOptions> = {\n minTokens: 40,\n maxGapTokens: 30,\n minSimilarityPercent: 70,\n};\n\n/**\n * N-gram size for the near-miss candidate index. 5 is NIL's published default (Nakagawa et al.,\n * ESEC/FSE 2021): short enough that edited clones still share many n-grams, long enough that\n * unrelated blocks rarely collide.\n */\nconst nearMissNgramSize = 5;\n/**\n * Blocks sharing fewer than this percentage of the smaller block's distinct n-grams skip LCS\n * verification entirely (NIL's filtration phase, default 10%): a pair below it cannot reach any\n * useful similarity, and the cheap set-overlap check prunes the quadratic candidate space.\n */\nconst nearMissFiltrationPercent = 10;\n/**\n * Token-level LCS over normalized code is dominated by punctuation and keywords, so a structural\n * 70% match alone is weak evidence of copying: two same-skeleton functions calling entirely\n * different APIs can exceed it. A near-miss pair must therefore also share at least half of the\n * larger block's content-bearing tokens (verbatim-kept names and literal values — the tokens that\n * distinguish WHAT the code does rather than how it is shaped). The bound is exclusive: a\n * value-mapping predicate pair (`x.type === 'a' || ...` with equal branch counts and zero shared\n * values) shares exactly half its content — the repeated member name — and must not pass.\n */\nconst minContentSimilarityPercent = 50;\n\n/** Minimum consecutive statements for a statement-sequence duplicate candidate. */\nconst minSequenceStatementCount = 2;\n/**\n * Caps the window length so statement-sequence enumeration stays linear in the statement count.\n * Heterogeneous clones longer than the cap are reported as capped windows (a deliberate\n * conservative undercount trading completeness for bounded discovery cost).\n */\nconst maxSequenceStatementCount = 100;\n\n/**\n * A region whose normalized tokens are at least 20% literal values is data-like (a lookup table, a\n * constant list, a value-mapping switch), not logic: literal values re-enter its fingerprint so\n * tables that merely share their shape stop counting as copy-paste. Logic-heavy code sits well\n * below the bound (5-10% literals) while object/array tables sit above it (25-50%); punctuation\n * and member names dilute tables, which is why the bound is far below half. Compared in integer\n * math (5 * literals >= total) so the TypeScript and native backends cannot disagree on the\n * boundary.\n */\nfunction isLiteralDense(literalCount: number, tokenCount: number): boolean {\n return literalCount * 5 >= tokenCount;\n}\n\ninterface Token {\n /** Normalization target: identifiers to anonymize, literal kind tags, or the raw token text. */\n kind: 'id' | 'text';\n text: string;\n /**\n * Two INDEPENDENT hashes of `text` (djb2 and FNV-1a), precomputed so fingerprinting nested\n * regions never re-hashes a token. Feeding the same per-token hash to both fingerprint\n * accumulators would collapse the key to 32 effective bits: one djb2 collision between two\n * token texts would then equate whole regions.\n */\n textHash: number;\n textHash2: number;\n /** Hash pair of a value-carrying literal's value, folded into data-like region fingerprints. */\n literalHash?: number;\n literalHash2?: number;\n /**\n * True for verbatim-kept NAMES (member/callee/type names, named grammar leaves): together with\n * value-carrying literals these are the content-bearing tokens the near-miss content gate\n * counts. Keywords, operators, and punctuation come from unnamed nodes and stay false.\n */\n isName?: boolean;\n /** 0-based source rows the token occupies, so line coverage counts only matched-token lines. */\n startRow: number;\n endRow: number;\n}\n\ninterface TokenRange {\n startTokenIndex: number;\n endTokenIndex: number;\n node: Parser.SyntaxNode;\n}\n\n/** A contiguous run of matched tokens; gapped (merged) duplicates carry several per occurrence. */\ninterface TokenSegment {\n startTokenIndex: number;\n endTokenIndex: number;\n}\n\ninterface DuplicateCandidate {\n fingerprint: string;\n tokenCount: number;\n startTokenIndex: number;\n endTokenIndex: number;\n startIndex: number;\n endIndex: number;\n startLine: number;\n endLine: number;\n}\n\ninterface CountedOccurrence {\n /** Matched token runs; more than one once gapped groups are merged. */\n segments: TokenSegment[];\n /** Sum of segment token counts (the gap tokens are not matched content). */\n tokenCount: number;\n startTokenIndex: number;\n endTokenIndex: number;\n startIndex: number;\n endIndex: number;\n startLine: number;\n endLine: number;\n}\n\n/** A duplicate region found in one file, exported for cross-file matching by fingerprint. */\nexport interface CrossFileDuplicateCandidate {\n /** Content key: equal fingerprints mean equal normalized token sequences (up to hash collision). */\n fingerprint: string;\n tokenCount: number;\n startIndex: number;\n endIndex: number;\n startLine: number;\n endLine: number;\n}\n\n/**\n * Detects copy-pasted regions within a file. Regions are compared by their normalized token\n * sequence: identifiers are anonymized consistently by first-occurrence order (`a.f(a, b)` matches\n * `x.f(x, y)` but not `x.f(y, z)`), literals are normalized by kind, and member/type names and all\n * keywords/operators are kept verbatim. Literal-dense (data-like) regions additionally require\n * equal literal values. Candidates are whole block-like subtrees plus runs of consecutive sibling\n * statements, so a copy pasted into the middle of a longer block is still found. Only maximal,\n * non-overlapping regions are counted, and adjacent groups separated by a small token gap merge\n * into one gapped (Type-3) clone group. Blocks the exact pipeline misses are additionally compared\n * by similarity (n-gram filtration then token-level LCS, NiCad/NIL-style), so near-miss (Type-3)\n * clones whose edits fragment them below `minTokens` are still reported.\n */\nexport function measureDuplication(\n root: Parser.SyntaxNode,\n codeLineNumbers: Set<number>,\n options?: DuplicationOptions\n): DuplicationMetrics {\n const minTokens = options?.minTokens ?? defaultDuplicationOptions.minTokens;\n const maxGapTokens = options?.maxGapTokens ?? defaultDuplicationOptions.maxGapTokens;\n const minSimilarityPercent = options?.minSimilarityPercent ?? defaultDuplicationOptions.minSimilarityPercent;\n const tokens: Token[] = [];\n const blockRanges: TokenRange[] = [];\n const containerStatementRanges: TokenRange[][] = [];\n collectTokens(root, tokens, blockRanges, containerStatementRanges);\n const literalCountPrefix = buildLiteralCountPrefix(tokens);\n\n const candidates = [\n ...collectBlockCandidates(tokens, literalCountPrefix, blockRanges, minTokens),\n ...collectSequenceCandidates(tokens, literalCountPrefix, containerStatementRanges, minTokens),\n ];\n const counted = selectMaximalGroups(candidates, (group) => group.length >= 2);\n const groups = mergeAdjacentGroups(toCountedGroups(counted), maxGapTokens);\n const nearMissGroups = collectNearMissGroups(\n tokens,\n literalCountPrefix,\n blockRanges,\n minTokens,\n minSimilarityPercent,\n groups\n );\n // Near-miss clustering can merge exact groups away, leaving empty entries behind.\n const reportedGroups = [...groups.filter((group) => group.length > 0), ...nearMissGroups];\n return summarizeDuplicates(reportedGroups, codeLineNumbers, tokens);\n}\n\n/**\n * Collects this file's duplicate-candidate fingerprints for cross-file clone detection: whole\n * block-like subtrees plus each statement container's full run (so wholly copied files and class\n * bodies match even when no inner block clears the threshold on its own). Nested and overlapping\n * candidates are all returned; the project-level selection keeps only maximal ones.\n */\nexport function collectCrossFileDuplicateCandidates(\n root: Parser.SyntaxNode,\n options?: DuplicationOptions\n): CrossFileDuplicateCandidate[] {\n const minTokens = options?.minTokens ?? defaultDuplicationOptions.minTokens;\n const tokens: Token[] = [];\n const blockRanges: TokenRange[] = [];\n const containerStatementRanges: TokenRange[][] = [];\n collectTokens(root, tokens, blockRanges, containerStatementRanges);\n const literalCountPrefix = buildLiteralCountPrefix(tokens);\n\n const candidates = collectBlockCandidates(tokens, literalCountPrefix, blockRanges, minTokens);\n for (const statements of containerStatementRanges) {\n const first = statements[0];\n const last = statements.at(-1);\n if (!first || !last) {\n continue;\n }\n const tokenCount = last.endTokenIndex - first.startTokenIndex;\n if (tokenCount < minTokens) {\n continue;\n }\n candidates.push(\n toCandidate(\n `s:${fingerprintKey(tokens, literalCountPrefix, first.startTokenIndex, last.endTokenIndex)}`,\n first.startTokenIndex,\n last.endTokenIndex,\n first.node,\n last.node\n )\n );\n }\n return dedupeByRegion(candidates).map(({ fingerprint, tokenCount, startIndex, endIndex, startLine, endLine }) => ({\n fingerprint,\n tokenCount,\n startIndex,\n endIndex,\n startLine,\n endLine,\n }));\n}\n\nfunction collectTokens(\n root: Parser.SyntaxNode,\n tokens: Token[],\n blockRanges: TokenRange[],\n containerStatementRanges: TokenRange[][]\n): void {\n function visit(node: Parser.SyntaxNode): TokenRange {\n const startTokenIndex = tokens.length;\n const atomicKind = node.childCount === 0 ? undefined : atomicLiteralKind(node);\n if (node.childCount === 0) {\n appendLeafToken(node, tokens);\n } else if (atomicKind !== undefined) {\n // Interpolation-free strings collapse to their kind tag so copies differing only in quote\n // style or content still match; delimiter tokens would otherwise break the equivalence.\n tokens.push(\n makeTextToken(atomicKind, literalValueText(node, atomicKind), node.startPosition.row, node.endPosition.row)\n );\n } else if (!commentTypes.has(node.type)) {\n const statementRanges: TokenRange[] = [];\n const isContainer = node.isNamed && statementContainerTypes.has(node.type);\n for (const child of node.children) {\n const childRange = visit(child);\n if (isContainer && child.isNamed && !commentTypes.has(child.type)) {\n statementRanges.push(childRange);\n }\n }\n // Single-statement containers are recorded too: within-file window enumeration needs two\n // statements and simply yields nothing for them, but cross-file matching must still see a\n // file whose only top-level statement is not a catalogued block type (a lone exported table).\n if (isContainer && statementRanges.length > 0) {\n containerStatementRanges.push(statementRanges);\n }\n }\n\n const range = { startTokenIndex, endTokenIndex: tokens.length, node };\n if (node.isNamed && duplicateBlockTypes.has(node.type)) {\n blockRanges.push(range);\n }\n return range;\n }\n\n visit(root);\n}\n\n/** The kind tag of a string-like node with no interpolation, or undefined to descend normally. */\nfunction atomicLiteralKind(node: Parser.SyntaxNode): string | undefined {\n const kind = node.isNamed ? literalKindByType.get(node.type) : undefined;\n if (kind === undefined) {\n return undefined;\n }\n return node.namedChildren.every((child) => stringFragmentTypes.has(child.type)) ? kind : undefined;\n}\n\nfunction appendLeafToken(node: Parser.SyntaxNode, tokens: Token[]): void {\n if (commentTypes.has(node.type)) {\n return;\n }\n\n const startRow = node.startPosition.row;\n const endRow = node.endPosition.row;\n if (node.isNamed && shorthandPropertyTypes.has(node.type)) {\n tokens.push(\n makeTextToken(node.text, undefined, startRow, endRow, true),\n makeTextToken(':', undefined, startRow, endRow),\n { kind: 'id', text: node.text, textHash: 0, textHash2: 0, startRow, endRow }\n );\n return;\n }\n\n if (node.isNamed && anonymizedIdentifierTypes.has(node.type) && !isSemanticNameLeaf(node)) {\n tokens.push({ kind: 'id', text: node.text, textHash: 0, textHash2: 0, startRow, endRow });\n return;\n }\n\n // Anything else keeps its text: keywords, operators, punctuation, and semantic names such as\n // `property_identifier`/`type_identifier`, which must distinguish otherwise-identical structures.\n const literalKind = node.isNamed ? literalKindByType.get(node.type) : undefined;\n if (literalKind === undefined) {\n tokens.push(makeTextToken(node.text, undefined, startRow, endRow, node.isNamed));\n } else {\n tokens.push(makeTextToken(literalKind, literalValueText(node, literalKind), startRow, endRow));\n }\n}\n\nfunction makeTextToken(\n text: string,\n literalValueText: string | undefined,\n startRow: number,\n endRow: number,\n isName = false\n): Token {\n const token: Token = { kind: 'text', text, textHash: hashText(text), textHash2: hashText2(text), startRow, endRow };\n if (literalValueText !== undefined && valueCarryingLiteralKinds.has(text)) {\n token.literalHash = hashText(literalValueText);\n token.literalHash2 = hashText2(literalValueText);\n }\n if (isName) {\n token.isName = true;\n }\n return token;\n}\n\n/**\n * The value of a literal as folded into literal-dense fingerprints. Strings hash their CONTENT,\n * not their source spelling: formatters rewrite quote style on paste (`'one'` vs `\"one\"`), so\n * delimiters must not make two copied tables differ. Content comes from the fragment children when\n * the grammar provides them (which also drops Python's `string_start`/`string_end` delimiter\n * nodes), else from the text with one matching pair of surrounding quotes stripped. Numbers keep\n * their raw text: formatters preserve numeric spelling, and canonicalizing values (`0x10` vs `16`)\n * identically in JavaScript and Rust would be far riskier than the rare mismatch it would unify.\n */\nfunction literalValueText(node: Parser.SyntaxNode, kind: string): string {\n if (kind !== '#str' && kind !== '#char') {\n return node.text;\n }\n // Fragment leaves (string_fragment, escape_sequence, heredoc_content, ...) already carry bare\n // content; a quote appearing there is content, not a delimiter.\n if (stringContentFragmentTypes.has(node.type)) {\n return node.text;\n }\n const fragments = node.namedChildren.filter((child) => stringContentFragmentTypes.has(child.type));\n if (fragments.length > 0) {\n return fragments.map((child) => child.text).join('');\n }\n return stripMatchingQuotes(node.text);\n}\n\nconst quoteCharacters = new Set(['\"', \"'\", '`']);\n\nfunction stripMatchingQuotes(text: string): string {\n const first = text[0];\n return text.length >= 2 && first !== undefined && quoteCharacters.has(first) && text.endsWith(first)\n ? text.slice(1, -1)\n : text;\n}\n\n/** literalCountPrefix[i] = value-carrying literal tokens in tokens[0..i), for O(1) density checks. */\nfunction buildLiteralCountPrefix(tokens: Token[]): Int32Array {\n const prefix = new Int32Array(tokens.length + 1);\n for (const [index, token] of tokens.entries()) {\n prefix[index + 1] = (prefix[index] ?? 0) + (token.literalHash === undefined ? 0 : 1);\n }\n return prefix;\n}\n\nfunction isSemanticNameLeaf(node: Parser.SyntaxNode): boolean {\n const parent = node.parent;\n if (!parent) {\n return false;\n }\n\n // Java method references (`Foo::bar`) name their identifiers without grammar fields; both the\n // type/object and the referenced method are semantic.\n if (parent.type === 'method_reference') {\n return true;\n }\n\n // `call` names its callee `method` in Ruby but `function` in Python; accept both fields.\n if (parent.type === 'call' && parent.childForFieldName('function')?.id === node.id) {\n return true;\n }\n\n // A Ruby constant receiving a call (`Alpha.new(...)`) names the invoked API; constants used as\n // plain values stay anonymized so renamed clones referencing constants still match.\n if (node.type === 'constant' && parent.type === 'call' && parent.childForFieldName('receiver')?.id === node.id) {\n return true;\n }\n\n // Java static receivers (`Alpha.run(...)`) name the invoked type. The tokenizer has no symbol\n // table, so PascalCase — Java's universal type-naming convention — is the discriminator;\n // camelCase instance receivers stay anonymized for rename tolerance.\n if (\n parent.type === 'method_invocation' &&\n parent.childForFieldName('object')?.id === node.id &&\n /^\\p{Lu}/u.test(node.text)\n ) {\n return true;\n }\n\n // Qualified callees (Rust `crate::alpha::make(...)`, C++ `detail::make(...)`) and generic\n // callees (`compute::<u32>(...)`, `compute<int>(...)`) wrap their identifiers arbitrarily deep;\n // every path/name segment is semantic there — but only in call position, so renamed clones that\n // merely reference scoped constants or `use` paths still match.\n if (\n (parent.type === 'scoped_identifier' || parent.type === 'qualified_identifier') &&\n (parent.childForFieldName('name')?.id === node.id || parent.childForFieldName('path')?.id === node.id)\n ) {\n let outer = parent;\n while (\n outer.parent &&\n (outer.parent.type === 'scoped_identifier' ||\n outer.parent.type === 'qualified_identifier' ||\n outer.parent.type === 'generic_function' ||\n outer.parent.type === 'template_function')\n ) {\n outer = outer.parent;\n }\n if (outer.parent?.type === 'call_expression' && outer.parent.childForFieldName('function')?.id === outer.id) {\n return true;\n }\n }\n\n // Go struct-literal keys (`Config{Timeout: ...}`) have no `key` field in the grammar: the key\n // is the keyed_element's first named child, a literal_element wrapping the identifier.\n if (\n parent.type === 'literal_element' &&\n parent.parent?.type === 'keyed_element' &&\n parent.parent.namedChild(0)?.id === parent.id\n ) {\n return true;\n }\n\n const field = semanticNameFieldByParentType.get(parent.type);\n return field !== undefined && parent.childForFieldName(field)?.id === node.id;\n}\n\nfunction collectBlockCandidates(\n tokens: Token[],\n literalCountPrefix: Int32Array,\n blockRanges: TokenRange[],\n minTokens: number\n): DuplicateCandidate[] {\n const candidates: DuplicateCandidate[] = [];\n for (const range of blockRanges) {\n const tokenCount = range.endTokenIndex - range.startTokenIndex;\n if (tokenCount < minTokens) {\n continue;\n }\n candidates.push(\n toCandidate(\n `b:${fingerprintKey(tokens, literalCountPrefix, range.startTokenIndex, range.endTokenIndex)}`,\n range.startTokenIndex,\n range.endTokenIndex,\n range.node,\n range.node\n )\n );\n }\n return candidates;\n}\n\ninterface WindowOccurrences {\n count: number;\n /** -1 once occurrences span more than one container. */\n containerIndex: number;\n minStart: number;\n maxStart: number;\n}\n\ninterface SequenceWindow {\n containerIndex: number;\n start: number;\n length: number;\n}\n\n/**\n * Enumerates runs of consecutive sibling statements. Every container statement participates; only\n * the window length is capped, so enumeration stays linear in the statement count. Windows are\n * grouped by a cheap rolling hash of per-statement fingerprints, and only locally maximal repeated\n * windows — those whose one-statement extensions stop repeating — become candidates with an exact\n * (window-consistent) fingerprint. Without the maximality filter a degenerate file of\n * near-identical statements would fingerprint every sub-window of every repeated region.\n */\nfunction collectSequenceCandidates(\n tokens: Token[],\n literalCountPrefix: Int32Array,\n containers: TokenRange[][],\n minTokens: number\n): DuplicateCandidate[] {\n const candidates: DuplicateCandidate[] = [];\n const occurrencesByWindowKey = new Map<number, WindowOccurrences>();\n const containerWindows = containers.map((statements) => enumerateContainerWindows(tokens, statements, minTokens));\n for (const [containerIndex, windows] of containerWindows.entries()) {\n for (const [start, row] of windows.windowKeysByStart.entries()) {\n for (const windowKey of row) {\n if (windowKey === undefined) {\n continue;\n }\n const occurrences = occurrencesByWindowKey.get(windowKey);\n if (occurrences) {\n occurrences.count += 1;\n if (occurrences.containerIndex !== containerIndex) {\n occurrences.containerIndex = -1;\n }\n occurrences.minStart = Math.min(occurrences.minStart, start);\n occurrences.maxStart = Math.max(occurrences.maxStart, start);\n } else {\n occurrencesByWindowKey.set(windowKey, { count: 1, containerIndex, minStart: start, maxStart: start });\n }\n }\n }\n }\n\n // A window only \"repeats\" when two of its occurrences can coexist without overlapping: sliding\n // matches inside a homogeneous run (start spread smaller than the window length) can never both\n // be counted and must neither qualify a window nor dominate its sub-windows.\n const repeats = (windowKey: number | undefined, length: number): boolean => {\n if (windowKey === undefined) {\n return false;\n }\n const occurrences = occurrencesByWindowKey.get(windowKey);\n return (\n occurrences !== undefined &&\n occurrences.count >= 2 &&\n (occurrences.containerIndex === -1 || occurrences.maxStart - occurrences.minStart >= length)\n );\n };\n\n // A window whose statements all share one normalized shape (sixteen `let x = 0;` declarations,\n // a constant table) is a homogeneous preamble, not a copy-paste: requiring two distinct\n // per-statement shapes keeps such runs out of duplicate groups and the duplication ratio.\n const hasDistinctStatements = (window: SequenceWindow): boolean => {\n const hashes = containerWindows[window.containerIndex]?.statementHashes ?? [];\n const firstHash = hashes[window.start];\n for (let index = window.start + 1; index < window.start + window.length; index += 1) {\n if (hashes[index] !== firstHash) {\n return true;\n }\n }\n return false;\n };\n\n const maximalWindows: SequenceWindow[] = [];\n for (const [containerIndex, windows] of containerWindows.entries()) {\n for (const [start, row] of windows.windowKeysByStart.entries()) {\n for (const [length, windowKey] of row.entries()) {\n if (!repeats(windowKey, length) || !hasDistinctStatements({ containerIndex, start, length })) {\n continue;\n }\n // Dominated windows are skipped: the one-statement extension also repeats, so a larger\n // candidate covering this window exists.\n const extendedRight = windows.windowKeysByStart[start]?.[length + 1];\n const extendedLeft = windows.windowKeysByStart[start - 1]?.[length + 1];\n if (repeats(extendedRight, length + 1) || repeats(extendedLeft, length + 1)) {\n continue;\n }\n maximalWindows.push({ containerIndex, start, length });\n }\n }\n }\n\n // The rolling hash anonymizes identifiers per statement, so a window can look repeated coarsely\n // while its exact (window-consistent) fingerprints differ, and a longer window's match can\n // dominate sub-windows that other copies still need (three copies where only two extend one\n // statement further). Every emitted window therefore exposes its repeating, unvisited\n // sub-windows; `visited` bounds the worklist and lengths strictly decrease, so it terminates.\n const visited = new Set(maximalWindows.map(windowId));\n let frontier = maximalWindows;\n while (frontier.length > 0) {\n const emitted: SequenceWindow[] = [];\n for (const window of frontier) {\n const statements = containers[window.containerIndex];\n const first = statements?.[window.start];\n const last = statements?.[window.start + window.length - 1];\n if (!first || !last) {\n continue;\n }\n const fingerprint = `s:${fingerprintKey(tokens, literalCountPrefix, first.startTokenIndex, last.endTokenIndex)}`;\n candidates.push(toCandidate(fingerprint, first.startTokenIndex, last.endTokenIndex, first.node, last.node));\n emitted.push(window);\n }\n frontier = [];\n for (const window of emitted) {\n for (const start of [window.start, window.start + 1]) {\n const subWindow = { containerIndex: window.containerIndex, start, length: window.length - 1 };\n const subWindowKey = containerWindows[window.containerIndex]?.windowKeysByStart[start]?.[subWindow.length];\n if (\n visited.has(windowId(subWindow)) ||\n !repeats(subWindowKey, subWindow.length) ||\n !hasDistinctStatements(subWindow)\n ) {\n continue;\n }\n visited.add(windowId(subWindow));\n frontier.push(subWindow);\n }\n }\n }\n return candidates;\n}\n\nfunction windowId(window: SequenceWindow): string {\n return `${window.containerIndex}:${window.start}:${window.length}`;\n}\n\ninterface ContainerWindows {\n /** windowKeysByStart[start][length] is the rolling-hash key of the window, or undefined if it is below the size thresholds. */\n windowKeysByStart: (number | undefined)[][];\n /** Per-statement fingerprint hashes, for the distinct-shape requirement on windows. */\n statementHashes: number[];\n}\n\nfunction enumerateContainerWindows(tokens: Token[], statements: TokenRange[], minTokens: number): ContainerWindows {\n const statementHashes = statements.map((statement) =>\n fingerprintHash(tokens, statement.startTokenIndex, statement.endTokenIndex)\n );\n const windowKeysByStart: (number | undefined)[][] = [];\n for (let start = 0; start < statements.length; start += 1) {\n const row: (number | undefined)[] = [];\n let hash = 5381;\n let tokenCount = 0;\n const maxEnd = Math.min(statements.length, start + maxSequenceStatementCount);\n for (let end = start; end < maxEnd; end += 1) {\n const statement = statements[end];\n const statementHash = statementHashes[end];\n if (!statement || statementHash === undefined) {\n break;\n }\n hash = combineHashes(hash, statementHash);\n tokenCount += statement.endTokenIndex - statement.startTokenIndex;\n const statementCount = end - start + 1;\n row[statementCount] =\n statementCount >= minSequenceStatementCount && tokenCount >= minTokens\n ? combineHashes(hash, statementCount)\n : undefined;\n }\n windowKeysByStart.push(row);\n }\n return { windowKeysByStart, statementHashes };\n}\n\n/**\n * Detects near-miss (Type-3) clone groups among block candidates the exact pipeline left\n * unreported, following the locate-filter-verify design of NIL (ESEC/FSE 2021) with NiCad's\n * per-fragment similarity semantics: an inverted index of 5-grams over normalized tokens proposes\n * candidate pairs, a cheap shared-n-gram ratio prunes them, and token-level LCS verifies that the\n * pair is at least `minSimilarityPercent` similar relative to the LARGER block (so a small block\n * embedded in a big one does not count), with a content gate requiring shared names/literal values\n * so same-skeleton code calling different APIs does not pair on punctuation alone. Verified pairs\n * are clustered transitively; each cluster becomes one clone group whose occurrences span whole\n * blocks. Only blocks that are not literal-dense (the data-table guard) participate, and wrappers\n * containing several comparable sub-blocks (a `describe(...)` call, a class body) are descended\n * through. Blocks already covered by an exactly-reported region still take part as ANCHORS — two\n * identical copies plus one edited copy is the commonest copy-paste-then-edit shape, and without\n * anchors the exact pair would swallow both comparison partners of the edited copy — but they are\n * never re-reported: an edited copy verified against an anchor is appended to the anchor's\n * fully-clustered exact group, so no reported region ever overlaps another. Reported occurrences\n * cover their whole block: unlike exact matches, a near-miss occurrence includes its edited\n * tokens, which is how NiCad-style detectors report Type-3 fragments.\n */\nfunction collectNearMissGroups(\n tokens: Token[],\n literalCountPrefix: Int32Array,\n blockRanges: TokenRange[],\n minTokens: number,\n minSimilarityPercent: number,\n reportedGroups: CountedOccurrence[][]\n): CountedOccurrence[][] {\n if (minSimilarityPercent >= 100) {\n return [];\n }\n const eligible = blockRanges\n .filter((range) => {\n const tokenCount = range.endTokenIndex - range.startTokenIndex;\n const literalCount =\n (literalCountPrefix[range.endTokenIndex] ?? 0) - (literalCountPrefix[range.startTokenIndex] ?? 0);\n return tokenCount >= minTokens && !isLiteralDense(literalCount, tokenCount);\n })\n .toSorted(\n (left, right) => left.startTokenIndex - right.startTokenIndex || right.endTokenIndex - left.endTokenIndex\n );\n const comparable = selectComparableBlocks(eligible);\n if (comparable.length < 2) {\n return [];\n }\n\n // Reported-group indices whose occurrences overlap each comparable block: such blocks anchor\n // near-miss comparisons but are never re-reported.\n const touchedGroupsByBlock = comparable.map((range) => {\n const touched: number[] = [];\n for (const [groupIndex, group] of reportedGroups.entries()) {\n if (\n group.some(\n (occurrence) =>\n occurrence.startTokenIndex < range.endTokenIndex && range.startTokenIndex < occurrence.endTokenIndex\n )\n ) {\n touched.push(groupIndex);\n }\n }\n return touched;\n });\n\n // Interned per call so a file's symbol ids (and thus its n-gram hashes) never depend on which\n // other files the process measured before it.\n const symbolIdByTokenHashes = new Map<string, number>();\n const sequences = comparable.map((range) => normalizeBlockSequence(tokens, range, symbolIdByTokenHashes));\n const ngramSets = sequences.map(({ sequence }) => collectNgramSet(sequence));\n const sharedNgramCounts = countSharedNgrams(ngramSets);\n\n const parent = comparable.map((_, index) => index);\n const find = (index: number): number => {\n let root = index;\n while (parent[root] !== root) {\n root = parent[root] ?? root;\n }\n while (parent[index] !== root) {\n const next = parent[index] ?? root;\n parent[index] = root;\n index = next;\n }\n return root;\n };\n for (const [pairKey, shared] of sharedNgramCounts) {\n // Decoded with the same modulus countSharedNgrams encodes with.\n const leftIndex = Math.floor(pairKey / ngramSets.length);\n const rightIndex = pairKey % ngramSets.length;\n const left = sequences[leftIndex];\n const right = sequences[rightIndex];\n const leftNgrams = ngramSets[leftIndex];\n const rightNgrams = ngramSets[rightIndex];\n if (!left || !right || !leftNgrams || !rightNgrams) {\n continue;\n }\n // Two already-reported blocks have nothing new to contribute to each other.\n if ((touchedGroupsByBlock[leftIndex]?.length ?? 0) > 0 && (touchedGroupsByBlock[rightIndex]?.length ?? 0) > 0) {\n continue;\n }\n if (shared * 100 < nearMissFiltrationPercent * Math.min(leftNgrams.size, rightNgrams.size)) {\n continue;\n }\n // A structural match must be backed by shared content (names and literal values), or two\n // same-skeleton blocks calling entirely different APIs would pair on punctuation alone.\n if (\n contentOverlap(left, right) * 100 <=\n minContentSimilarityPercent * Math.max(left.contentTotal, right.contentTotal)\n ) {\n continue;\n }\n // Per-fragment similarity against the larger block (NiCad semantics): both blocks must be\n // mostly covered by the common subsequence, which is stricter than NIL's min-denominator and\n // keeps a generic small block from \"matching\" inside every big one.\n if (\n lcsLength(left.sequence, right.sequence) * 100 >=\n minSimilarityPercent * Math.max(left.sequence.length, right.sequence.length)\n ) {\n const leftRoot = find(leftIndex);\n const rightRoot = find(rightIndex);\n parent[Math.max(leftRoot, rightRoot)] = Math.min(leftRoot, rightRoot);\n }\n }\n\n const membersByRoot = new Map<number, number[]>();\n for (const index of comparable.keys()) {\n const root = find(index);\n const members = membersByRoot.get(root) ?? [];\n members.push(index);\n membersByRoot.set(root, members);\n }\n const groups: CountedOccurrence[][] = [];\n for (const members of membersByRoot.values()) {\n if (members.length < 2) {\n continue;\n }\n const uncovered = members.filter((index) => (touchedGroupsByBlock[index]?.length ?? 0) === 0);\n const covered = members.filter((index) => (touchedGroupsByBlock[index]?.length ?? 0) > 0);\n if (covered.length === 0) {\n groups.push(members.flatMap((index) => (comparable[index] ? [toNearMissOccurrence(comparable[index])] : [])));\n continue;\n }\n if (uncovered.length === 0) {\n continue;\n }\n // An anchored cluster extends a reported group only when that group lies entirely inside the\n // cluster (every occurrence overlaps a member); appending the edited copies there keeps one\n // group per clone family and keeps reported regions disjoint. A group that also has\n // occurrences elsewhere is left untouched, and the uncovered copies stand alone if they can.\n const overlapsMember = (occurrence: CountedOccurrence): boolean =>\n members.some((index) => {\n const range = comparable[index];\n return (\n range !== undefined &&\n occurrence.startTokenIndex < range.endTokenIndex &&\n range.startTokenIndex < occurrence.endTokenIndex\n );\n });\n const fullyClustered = [...new Set(covered.flatMap((index) => touchedGroupsByBlock[index] ?? []))]\n .filter((groupIndex) => {\n const group = reportedGroups[groupIndex];\n return group !== undefined && group.length > 0 && group.every(overlapsMember);\n })\n .toSorted((leftIndex, rightIndex) => leftIndex - rightIndex);\n const [targetIndex, ...sourceIndexes] = fullyClustered;\n if (targetIndex !== undefined) {\n // Every fully-clustered group belongs to this verified component: rebuild them as ONE group\n // with one occurrence per member block. Occurrences landing in the same member (a copy's\n // exact prefix and suffix fragments, split by an over-large edited middle) coalesce into a\n // single multi-segment occurrence — they are one copy, not two — and each uncovered member\n // contributes its whole-block occurrence. Merged-away entries are left empty for the caller\n // to drop.\n const consumed = new Set<CountedOccurrence>();\n const merged: CountedOccurrence[] = [];\n for (const memberIndex of members) {\n const range = comparable[memberIndex];\n if (!range) {\n continue;\n }\n // Occurrences of ONE group are distinct copies (a repeated run inside the block); only\n // fragments from DIFFERENT groups (a copy's exact prefix in one group and its suffix in\n // another) belong to the same copy. Walking the member's fragments in position order and\n // starting a new copy whenever a source group repeats keeps same-group copies separate,\n // and — because the fragments are disjoint and each copy takes a consecutive slice —\n // guarantees the coalesced spans never overlap, even when groups contribute unequal\n // fragment counts to this member.\n const fragments: { occurrence: CountedOccurrence; groupIndex: number }[] = [];\n for (const groupIndex of fullyClustered) {\n for (const occurrence of reportedGroups[groupIndex] ?? []) {\n if (\n !consumed.has(occurrence) &&\n occurrence.startTokenIndex < range.endTokenIndex &&\n range.startTokenIndex < occurrence.endTokenIndex\n ) {\n consumed.add(occurrence);\n fragments.push({ occurrence, groupIndex });\n }\n }\n }\n fragments.sort(\n (left, right) =>\n left.occurrence.startTokenIndex - right.occurrence.startTokenIndex ||\n left.occurrence.endTokenIndex - right.occurrence.endTokenIndex\n );\n let copyParts: CountedOccurrence[] = [];\n const copyGroups = new Set<number>();\n for (const { occurrence, groupIndex } of fragments) {\n if (copyGroups.has(groupIndex)) {\n merged.push(coalesceOccurrences(copyParts));\n copyParts = [];\n copyGroups.clear();\n }\n copyParts.push(occurrence);\n copyGroups.add(groupIndex);\n }\n if (copyParts.length > 0) {\n merged.push(coalesceOccurrences(copyParts));\n }\n if (fragments.length === 0 && (touchedGroupsByBlock[memberIndex]?.length ?? 0) === 0) {\n merged.push(toNearMissOccurrence(range));\n }\n }\n merged.sort(\n (left, right) => left.startTokenIndex - right.startTokenIndex || left.endTokenIndex - right.endTokenIndex\n );\n reportedGroups[targetIndex] = merged;\n for (const sourceIndex of sourceIndexes) {\n reportedGroups[sourceIndex] = [];\n }\n } else if (uncovered.length >= 2) {\n groups.push(uncovered.flatMap((index) => (comparable[index] ? [toNearMissOccurrence(comparable[index])] : [])));\n }\n }\n groups.sort(compareGroups);\n return groups;\n}\n\n/**\n * Keeps the block ranges the near-miss phase compares. Block ranges nest or are disjoint, so they\n * form a forest; a candidate whose subtree branches into two or more disjoint eligible sub-blocks\n * is a WRAPPER (a `describe(...)` statement, an IIFE, a class body) and is descended through —\n * otherwise a file whose code sits inside one enclosing construct would yield a single candidate\n * and silently disable near-miss detection. A linear chain keeps its top (the most context), and\n * the kept set is an antichain, so reported near-miss occurrences stay disjoint by construction.\n */\nfunction selectComparableBlocks(eligible: TokenRange[]): TokenRange[] {\n const roots: ForestNode[] = [];\n const stack: ForestNode[] = [];\n for (const range of eligible) {\n while (stack.length > 0 && (stack.at(-1) as ForestNode).range.endTokenIndex <= range.startTokenIndex) {\n stack.pop();\n }\n const top = stack.at(-1);\n // Equal spans (two node types covering the same tokens) collapse into the first.\n if (top && top.range.startTokenIndex === range.startTokenIndex && top.range.endTokenIndex === range.endTokenIndex) {\n continue;\n }\n const node: ForestNode = { range, children: [] };\n if (top) {\n top.children.push(node);\n } else {\n roots.push(node);\n }\n stack.push(node);\n }\n\n const kept: TokenRange[] = [];\n const visit = (node: ForestNode): void => {\n if (branches(node)) {\n for (const child of node.children) {\n visit(child);\n }\n } else {\n kept.push(node.range);\n }\n };\n for (const root of roots) {\n visit(root);\n }\n return kept;\n}\n\ninterface ForestNode {\n range: TokenRange;\n children: ForestNode[];\n}\n\n/** Whether the node's subtree splits into two or more disjoint eligible sub-blocks. */\nfunction branches(node: ForestNode): boolean {\n const [firstChild] = node.children;\n return node.children.length >= 2 || (firstChild !== undefined && branches(firstChild));\n}\n\n/** One copy's fragments (an exact prefix and suffix split by a large edit) as one occurrence. */\nfunction coalesceOccurrences(occurrences: CountedOccurrence[]): CountedOccurrence {\n const first = occurrences[0];\n if (!first || occurrences.length === 1) {\n return first ?? toEmptyOccurrence();\n }\n return {\n segments: occurrences\n .flatMap((occurrence) => occurrence.segments)\n .toSorted((left, right) => left.startTokenIndex - right.startTokenIndex),\n tokenCount: occurrences.reduce((sum, occurrence) => sum + occurrence.tokenCount, 0),\n startTokenIndex: Math.min(...occurrences.map((occurrence) => occurrence.startTokenIndex)),\n endTokenIndex: Math.max(...occurrences.map((occurrence) => occurrence.endTokenIndex)),\n startIndex: Math.min(...occurrences.map((occurrence) => occurrence.startIndex)),\n endIndex: Math.max(...occurrences.map((occurrence) => occurrence.endIndex)),\n startLine: Math.min(...occurrences.map((occurrence) => occurrence.startLine)),\n endLine: Math.max(...occurrences.map((occurrence) => occurrence.endLine)),\n };\n}\n\n/** Unreachable fallback keeping coalesceOccurrences total without a non-null assertion. */\nfunction toEmptyOccurrence(): CountedOccurrence {\n return {\n segments: [],\n tokenCount: 0,\n startTokenIndex: 0,\n endTokenIndex: 0,\n startIndex: 0,\n endIndex: 0,\n startLine: 0,\n endLine: 0,\n };\n}\n\n/** A near-miss occurrence spans its whole block as one segment, edited tokens included. */\nfunction toNearMissOccurrence(range: TokenRange): CountedOccurrence {\n return {\n segments: [{ startTokenIndex: range.startTokenIndex, endTokenIndex: range.endTokenIndex }],\n tokenCount: range.endTokenIndex - range.startTokenIndex,\n startTokenIndex: range.startTokenIndex,\n endTokenIndex: range.endTokenIndex,\n startIndex: range.node.startIndex,\n endIndex: range.node.endIndex,\n startLine: range.node.startPosition.row + 1,\n endLine: range.node.endPosition.row + 1,\n };\n}\n\ninterface NormalizedBlock {\n sequence: Int32Array;\n /** Occurrences per content-bearing symbol (names and literal values), for the content gate. */\n contentCountBySymbol: Map<number, number>;\n contentTotal: number;\n}\n\n/**\n * A block's tokens as comparable integers: anonymized identifiers become negative first-occurrence\n * indexes (per block, mirroring the exact fingerprint's rename tolerance) and every other token\n * becomes a non-negative id interned over BOTH 32-bit text hashes — with literal VALUES folded in,\n * unlike the exact fingerprint's kind tags — so one hash collision cannot equate two different\n * tokens and a fuzzy 70% match cannot mistake same-shape tables or dispatch predicates with\n * entirely different values for copies.\n */\nfunction normalizeBlockSequence(\n tokens: Token[],\n range: TokenRange,\n symbolIdByTokenHashes: Map<string, number>\n): NormalizedBlock {\n const sequence = new Int32Array(range.endTokenIndex - range.startTokenIndex);\n const indexByIdentifier = new Map<string, number>();\n const contentCountBySymbol = new Map<number, number>();\n let contentTotal = 0;\n for (let index = range.startTokenIndex; index < range.endTokenIndex; index += 1) {\n const token = tokens[index];\n if (!token) {\n continue;\n }\n let value: number;\n if (token.kind === 'id') {\n let identifierIndex = indexByIdentifier.get(token.text);\n if (identifierIndex === undefined) {\n identifierIndex = indexByIdentifier.size;\n indexByIdentifier.set(token.text, identifierIndex);\n }\n value = -(identifierIndex + 1);\n } else {\n const key = `${token.textHash}:${token.textHash2}:${token.literalHash ?? 0}:${token.literalHash2 ?? 0}`;\n let id = symbolIdByTokenHashes.get(key);\n if (id === undefined) {\n id = symbolIdByTokenHashes.size;\n symbolIdByTokenHashes.set(key, id);\n }\n value = id;\n if (token.isName === true || token.literalHash !== undefined) {\n contentCountBySymbol.set(value, (contentCountBySymbol.get(value) ?? 0) + 1);\n contentTotal += 1;\n }\n }\n sequence[index - range.startTokenIndex] = value;\n }\n return { sequence, contentCountBySymbol, contentTotal };\n}\n\n/** Multiset overlap of two blocks' content-bearing symbols, for the content gate. */\nfunction contentOverlap(left: NormalizedBlock, right: NormalizedBlock): number {\n const [smaller, larger] =\n left.contentCountBySymbol.size <= right.contentCountBySymbol.size ? [left, right] : [right, left];\n let overlap = 0;\n for (const [symbol, count] of smaller.contentCountBySymbol) {\n overlap += Math.min(count, larger.contentCountBySymbol.get(symbol) ?? 0);\n }\n return overlap;\n}\n\n/** The distinct 5-gram hashes of a normalized block sequence, for the filtration set overlap. */\nfunction collectNgramSet(sequence: Int32Array): Set<number> {\n const ngrams = new Set<number>();\n for (let start = 0; start + nearMissNgramSize <= sequence.length; start += 1) {\n let hash = 5381;\n for (let offset = 0; offset < nearMissNgramSize; offset += 1) {\n // oxlint-disable-next-line unicorn/prefer-math-trunc -- `| 0` wraps the sum to int32 to match the native backend's wrapping i32 arithmetic.\n hash = (Math.imul(hash, 31) + (sequence[start + offset] ?? 0)) | 0;\n }\n ngrams.add(hash);\n }\n return ngrams;\n}\n\n/** Shared distinct-n-gram counts per block pair, keyed `leftIndex * blockCount + rightIndex`. */\nfunction countSharedNgrams(ngramSets: Set<number>[]): Map<number, number> {\n const blocksByNgram = new Map<number, number[]>();\n for (const [blockIndex, ngrams] of ngramSets.entries()) {\n // Get-or-create: this loop runs once per distinct n-gram of every block, so the redundant\n // `set` on already-present keys is worth avoiding here.\n for (const ngram of ngrams) {\n let blocks = blocksByNgram.get(ngram);\n if (!blocks) {\n blocks = [];\n blocksByNgram.set(ngram, blocks);\n }\n blocks.push(blockIndex);\n }\n }\n const sharedCounts = new Map<number, number>();\n for (const blocks of blocksByNgram.values()) {\n // Index-based loops: slicing here would allocate per pair in what can be a hot loop.\n for (let leftPosition = 0; leftPosition < blocks.length; leftPosition += 1) {\n const leftIndex = blocks[leftPosition] ?? 0;\n for (let rightPosition = leftPosition + 1; rightPosition < blocks.length; rightPosition += 1) {\n const pairKey = leftIndex * ngramSets.length + (blocks[rightPosition] ?? 0);\n sharedCounts.set(pairKey, (sharedCounts.get(pairKey) ?? 0) + 1);\n }\n }\n }\n return sharedCounts;\n}\n\n/**\n * Longest-common-subsequence LENGTH of two symbol sequences via the Allison–Dix bit-parallel\n * recurrence (O(|a|/32 · |b|) words): per symbol of `b`, `x = match | v` and\n * `v = x & ~(x - ((v << 1) | 1))` over multi-word bit vectors; the set bits of `v` count the LCS.\n * Only the length is needed (similarity is a ratio), and LCS length is algorithm-independent, so\n * the native backend may use a different word size and still agree bit-for-bit.\n */\nfunction lcsLength(a: Int32Array, b: Int32Array): number {\n const wordCount = (a.length + 31) >>> 5;\n const positionMasks = new Map<number, Uint32Array>();\n for (const [index, symbol] of a.entries()) {\n let mask = positionMasks.get(symbol);\n if (!mask) {\n mask = new Uint32Array(wordCount);\n positionMasks.set(symbol, mask);\n }\n const word = index >>> 5;\n // The Uint32Array store wraps the signed int32 bit pattern to unsigned. The `?? 0` guards in\n // this function are required by noUncheckedIndexedAccess (typed-array reads type as\n // `number | undefined`), not redundancy: every index is in bounds.\n mask[word] = (mask[word] ?? 0) | (1 << (index & 31));\n }\n\n const v = new Uint32Array(wordCount);\n for (const symbol of b) {\n const matchMask = positionMasks.get(symbol);\n // `(v << 1) | 1` shifts a carry bit across words; subtraction borrows across words.\n let shiftCarry = 1;\n let borrow = 0;\n for (let word = 0; word < wordCount; word += 1) {\n const previous = v[word] ?? 0;\n // oxlint-disable-next-line unicorn/prefer-math-trunc -- `>>> 0` reinterprets the signed int32 bit pattern as unsigned so the borrow subtraction below compares magnitudes; Math.trunc would keep it negative.\n const x = ((matchMask?.[word] ?? 0) | previous) >>> 0;\n // oxlint-disable-next-line unicorn/prefer-math-trunc -- same unsigned reinterpretation as `x`.\n const shifted = ((previous << 1) | shiftCarry) >>> 0;\n shiftCarry = previous >>> 31;\n const difference = x - shifted - borrow;\n borrow = difference < 0 ? 1 : 0;\n // The Uint32Array store wraps the signed int32 bit pattern to unsigned.\n v[word] = x & ~difference;\n }\n }\n\n let length = 0;\n for (const word of v) {\n length += popCount(word);\n }\n return length;\n}\n\nfunction popCount(value: number): number {\n let count = value - ((value >>> 1) & 0x55_55_55_55);\n count = (count & 0x33_33_33_33) + ((count >>> 2) & 0x33_33_33_33);\n return (Math.imul((count + (count >>> 4)) & 0x0F_0F_0F_0F, 0x01_01_01_01) >>> 24) & 0xFF;\n}\n\nfunction toCandidate(\n fingerprint: string,\n startTokenIndex: number,\n endTokenIndex: number,\n firstNode: Parser.SyntaxNode,\n lastNode: Parser.SyntaxNode\n): DuplicateCandidate {\n return {\n fingerprint,\n tokenCount: endTokenIndex - startTokenIndex,\n startTokenIndex,\n endTokenIndex,\n startIndex: firstNode.startIndex,\n endIndex: lastNode.endIndex,\n startLine: firstNode.startPosition.row + 1,\n endLine: lastNode.endPosition.row + 1,\n };\n}\n\n/** Caches of hashText/hashText2 over '$0', '$1', ... so anonymized identifiers hash without allocating. */\nconst anonymizedIndexHashes: number[] = [];\nconst anonymizedIndexHashes2: number[] = [];\n\nfunction anonymizedIndexHash(index: number): number {\n let hash = anonymizedIndexHashes[index];\n if (hash === undefined) {\n hash = hashText(`$${index}`);\n anonymizedIndexHashes[index] = hash;\n }\n return hash;\n}\n\nfunction anonymizedIndexHash2(index: number): number {\n let hash = anonymizedIndexHashes2[index];\n if (hash === undefined) {\n hash = hashText2(`$${index}`);\n anonymizedIndexHashes2[index] = hash;\n }\n return hash;\n}\n\n/**\n * Content key of a token range: two independent 32-bit hashes over the normalized token sequence\n * (identifiers anonymized consistently by first-occurrence order) plus the token count. Regions\n * with equal keys are treated as equal content; a collision would need both 32-bit hashes and the\n * length to coincide, which is negligible for a metrics report. Hashing per-token instead of\n * serializing the whole range to a string keeps fingerprinting allocation-free for nested regions.\n */\nfunction fingerprintKey(\n tokens: Token[],\n literalCountPrefix: Int32Array,\n startTokenIndex: number,\n endTokenIndex: number\n): string {\n const literalCount = (literalCountPrefix[endTokenIndex] ?? 0) - (literalCountPrefix[startTokenIndex] ?? 0);\n const literalDense = isLiteralDense(literalCount, endTokenIndex - startTokenIndex);\n const [primary, secondary] = fingerprintHashPair(tokens, startTokenIndex, endTokenIndex, literalDense);\n return `${primary}:${secondary}:${endTokenIndex - startTokenIndex}`;\n}\n\n/**\n * A single 32-bit summary of a range for the coarse rolling-hash phase. Deliberately\n * density-agnostic: density is a property of the final candidate REGION, and folding literal\n * values into per-statement hashes would make a dense statement inside a logic-heavy window\n * (`const weights = [1, 2, 3];`) block the window from ever being enumerated. The coarse phase\n * over-approximates on shape alone; the exact region fingerprint still applies the density rule.\n */\nfunction fingerprintHash(tokens: Token[], startTokenIndex: number, endTokenIndex: number): number {\n const [primary, secondary] = fingerprintHashPair(tokens, startTokenIndex, endTokenIndex, false);\n // XOR already coerces to int32, matching the native backend's i32 arithmetic.\n return primary ^ Math.imul(secondary, 31);\n}\n\nfunction fingerprintHashPair(\n tokens: Token[],\n startTokenIndex: number,\n endTokenIndex: number,\n foldLiteralValues: boolean\n): [number, number] {\n const indexByIdentifier = new Map<string, number>();\n let primary = 5381;\n let secondary = 52_711;\n for (let index = startTokenIndex; index < endTokenIndex; index += 1) {\n const token = tokens[index];\n if (!token) {\n continue;\n }\n // Each accumulator consumes its own independent per-token hash: sharing one would collapse\n // the key to 32 effective bits (a single djb2 collision would equate whole regions).\n let part: number;\n let part2: number;\n if (token.kind === 'id') {\n let identifierIndex = indexByIdentifier.get(token.text);\n if (identifierIndex === undefined) {\n identifierIndex = indexByIdentifier.size;\n indexByIdentifier.set(token.text, identifierIndex);\n }\n part = anonymizedIndexHash(identifierIndex);\n part2 = anonymizedIndexHash2(identifierIndex);\n } else {\n part = token.textHash;\n part2 = token.textHash2;\n }\n // oxlint-disable-next-line unicorn/prefer-math-trunc -- `| 0` wraps the sum to int32 (Math.trunc does not), which must match the native backend's wrapping i32 arithmetic.\n primary = (Math.imul(primary, 31) + part) | 0;\n secondary = Math.imul(secondary, 37) ^ part2;\n if (foldLiteralValues && token.literalHash !== undefined && token.literalHash2 !== undefined) {\n // oxlint-disable-next-line unicorn/prefer-math-trunc -- `| 0` wraps the sum to int32 (Math.trunc does not), which must match the native backend's wrapping i32 arithmetic.\n primary = (Math.imul(primary, 31) + token.literalHash) | 0;\n secondary = Math.imul(secondary, 37) ^ token.literalHash2;\n }\n }\n return [primary, secondary];\n}\n\n/** djb2-style hash; XOR keeps the value in signed 32-bit range, which is fine for a grouping key. */\nfunction hashText(text: string): number {\n let hash = 5381;\n for (let index = 0; index < text.length; index += 1) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- djb2 hashes UTF-16 code units; codePointAt would hash surrogate pairs twice (full code point, then the lone low surrogate).\n hash = Math.imul(hash, 33) ^ text.charCodeAt(index);\n }\n return hash;\n}\n\n/** FNV-1a over UTF-16 code units: independent of hashText so the two accumulators never share input. */\nfunction hashText2(text: string): number {\n let hash = -2_128_831_035; // 2166136261 as int32 (the FNV-1a offset basis)\n for (let index = 0; index < text.length; index += 1) {\n // oxlint-disable-next-line unicorn/prefer-code-point -- hashes UTF-16 code units like hashText.\n hash = Math.imul(hash ^ text.charCodeAt(index), 16_777_619);\n }\n return hash;\n}\n\nfunction combineHashes(hash: number, value: number): number {\n return Math.imul(hash, 31) + value;\n}\n\nfunction toCountedGroups(counted: Map<string, DuplicateCandidate[]>): CountedOccurrence[][] {\n const groups: CountedOccurrence[][] = [];\n for (const group of counted.values()) {\n const occurrences = group.map((candidate) => ({\n segments: [{ startTokenIndex: candidate.startTokenIndex, endTokenIndex: candidate.endTokenIndex }],\n tokenCount: candidate.tokenCount,\n startTokenIndex: candidate.startTokenIndex,\n endTokenIndex: candidate.endTokenIndex,\n startIndex: candidate.startIndex,\n endIndex: candidate.endIndex,\n startLine: candidate.startLine,\n endLine: candidate.endLine,\n }));\n occurrences.sort(\n (left, right) => left.startTokenIndex - right.startTokenIndex || left.endTokenIndex - right.endTokenIndex\n );\n groups.push(occurrences);\n }\n return groups;\n}\n\n/**\n * Merges duplicate groups separated by a small token gap into one gapped (Type-3) clone group: a\n * copy edited in one spot splits into two exact groups whose occurrences sit side by side in the\n * same order. Two groups merge when they have the same number of occurrences and, pairing\n * occurrences in source order, every pair is gap-adjacent without crossing into the next pair.\n * Merging repeats to a fixpoint so a clone edited in several spots still reassembles. Gap tokens\n * are not matched content: line coverage and sizes count only the matched segments.\n */\nfunction mergeAdjacentGroups(groups: CountedOccurrence[][], maxGapTokens: number): CountedOccurrence[][] {\n if (maxGapTokens <= 0 || groups.length < 2) {\n return groups;\n }\n // Deterministic processing order (mirrored by the native backend): by first occurrence position.\n groups.sort(compareGroups);\n for (let restart = true; restart;) {\n restart = false;\n for (let leftIndex = 0; leftIndex < groups.length && !restart; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < groups.length; rightIndex += 1) {\n const left = groups[leftIndex];\n const right = groups[rightIndex];\n if (!left || !right) {\n continue;\n }\n const merged = mergeGroups(left, right, maxGapTokens) ?? mergeGroups(right, left, maxGapTokens);\n if (merged) {\n groups[leftIndex] = merged;\n groups.splice(rightIndex, 1);\n groups.sort(compareGroups);\n restart = true;\n break;\n }\n }\n }\n }\n return groups;\n}\n\nfunction compareGroups(left: CountedOccurrence[], right: CountedOccurrence[]): number {\n const leftFirst = left[0];\n const rightFirst = right[0];\n return (\n (leftFirst?.startTokenIndex ?? 0) - (rightFirst?.startTokenIndex ?? 0) ||\n (leftFirst?.endTokenIndex ?? 0) - (rightFirst?.endTokenIndex ?? 0)\n );\n}\n\n/** The merged group when every `second` occurrence gap-follows its `first` counterpart, else undefined. */\nfunction mergeGroups(\n first: CountedOccurrence[],\n second: CountedOccurrence[],\n maxGapTokens: number\n): CountedOccurrence[] | undefined {\n if (first.length !== second.length) {\n return undefined;\n }\n for (const [index, leading] of first.entries()) {\n const trailing = second[index];\n if (!trailing) {\n return undefined;\n }\n const gap = trailing.startTokenIndex - leading.endTokenIndex;\n if (gap < 0 || gap > maxGapTokens) {\n return undefined;\n }\n // The merged span must stay clear of the next pair, or spans would overlap.\n const next = first[index + 1];\n if (next && trailing.endTokenIndex > next.startTokenIndex) {\n return undefined;\n }\n }\n return first.map((leading, index) => {\n const trailing = second[index];\n if (!trailing) {\n return leading;\n }\n return {\n segments: [...leading.segments, ...trailing.segments],\n tokenCount: leading.tokenCount + trailing.tokenCount,\n startTokenIndex: leading.startTokenIndex,\n endTokenIndex: trailing.endTokenIndex,\n startIndex: leading.startIndex,\n endIndex: trailing.endIndex,\n startLine: leading.startLine,\n endLine: trailing.endLine,\n };\n });\n}\n\nfunction summarizeDuplicates(\n groups: CountedOccurrence[][],\n codeLineNumbers: Set<number>,\n tokens: Token[]\n): DuplicationMetrics {\n let duplicateBlockCount = 0;\n let maxDuplicateBlockSize = 0;\n const duplicateBlockGroups: { startLine: number; endLine: number }[][] = [];\n const duplicatedLines = new Set<number>();\n for (const group of groups) {\n // Each redundant occurrence contributes one count per matched fragment, so merging a gapped\n // clone's fragments into one group does not halve the count a `duplicateBlock` threshold sees:\n // an edited two-fragment pair still counts 2, exactly as its unmerged fragments did.\n // Occurrence shapes can differ within one group (a gap-merged exact pair plus an appended\n // whole-block near-miss copy), so every occurrence's fragments are summed and one\n // representative — the largest — is deducted, keeping the count independent of source order.\n const segmentCounts = group.map((occurrence) => occurrence.segments.length);\n duplicateBlockCount += segmentCounts.reduce((sum, count) => sum + count, 0) - Math.max(...segmentCounts, 0);\n for (const occurrence of group) {\n maxDuplicateBlockSize = Math.max(maxDuplicateBlockSize, occurrence.tokenCount);\n // Only CODE lines carrying segment tokens count: comments and blank gaps inside an\n // occurrence's bounding range — the unmatched gap of a merged clone, and blank rows inside a\n // multi-row token (heredocs, template literals) — are not duplicated content and would push\n // the ratio past 1. A near-miss occurrence's single segment deliberately spans its WHOLE\n // block, edited tokens included: an LCS has no canonical per-line attribution, and\n // NiCad-style detectors treat the whole near-miss fragment as the clone.\n for (const segment of occurrence.segments) {\n for (let index = segment.startTokenIndex; index < segment.endTokenIndex; index += 1) {\n const token = tokens[index];\n for (let row = token?.startRow ?? 0; row <= (token?.endRow ?? -1); row += 1) {\n if (codeLineNumbers.has(row + 1)) {\n duplicatedLines.add(row + 1);\n }\n }\n }\n }\n }\n duplicateBlockGroups.push(\n group\n .map(({ startLine, endLine }) => ({ startLine, endLine }))\n .toSorted((left, right) => left.startLine - right.startLine)\n );\n }\n duplicateBlockGroups.sort((left, right) => (left[0]?.startLine ?? 0) - (right[0]?.startLine ?? 0));\n\n return {\n duplicateBlockCount,\n duplicateBlockGroupCount: groups.length,\n duplicateBlockGroups,\n duplicateLineCount: duplicatedLines.size,\n duplicationRatio: codeLineNumbers.size === 0 ? 0 : duplicatedLines.size / codeLineNumbers.size,\n maxDuplicateBlockSize,\n };\n}\n"],"mappings":"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"}
|
package/dist/metrics.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";const e=require("./_virtual/_rolldown/runtime.cjs"),t=require("./languages.cjs"),n=require("./duplication.cjs"),r=require("./ncss.cjs"),i=require("./nativeMetrics.cjs");let a=require("tree-sitter");a=e.__toESM(a,1);const o=new Set([`&&`,`||`,`and`,`or`]),s=new Set(`+,-,*,/,%,**,=,+=,-=,*=,/=,%=,==,!=,===,!==,<,<=,>,>=,!,~,&,|,^,++,--,<<,>>,>>>,=>,**=,<<=,>>=,>>>=,&=,|=,^=,&&=,||=,??=,??,?.,?,//,//=,@,@=,:=,<-,<=>,=~,..,...,..=,&&,||,!~,&^,&^=,&.,.,->,::,->*,.*,sizeof,alignof,defined?,as,bitand,bitor,xor,compl,and_eq,or_eq,xor_eq,not_eq,and,or,not,in,is,instanceof,typeof,new,delete,return,throw,raise,yield,await,co_await,co_yield,co_return,break,continue`.split(`,`)),c=new Set(`identifier.property_identifier.field_identifier.type_identifier.constant.instance_variable.class_variable.global_variable.simple_symbol.self.this.super.primitive_type.boolean_type.void_type.auto.number.integer.float.integer_literal.float_literal.int_literal.rune_literal.imaginary_literal.number_literal.decimal_integer_literal.hex_integer_literal.octal_integer_literal.binary_integer_literal.decimal_floating_point_literal.hex_floating_point_literal.string.string_literal.raw_string_literal.string_fragment.multiline_string_fragment.string_content.raw_string_content.template_string.character_literal.char_literal.character.true.false.null.null_literal.undefined.nil.none`.split(`.`)),l=new Set([`interpreted_string_literal`,`regex`,`user_defined_literal`,`integral_type`,`floating_point_type`,`sized_type_specifier`,`placeholder_type_specifier`]);var u=class{registry=t.createLanguageRegistry();registerLanguage(e){fe(e),r.invalidateNcssSetsCache(e),this.registry.set(e.name,e);for(let t of e.aliases??[])this.registry.set(t,e)}getSupportedLanguages(){return[...new Set([...this.registry.values()].map(e=>e.name))]}measure(e,t){let a=this.registry.get(t.language);if(!a)throw Error(`Unsupported language: ${t.language}`);let o=f(t)?i.measureWithNativeBackend(e,a,t.includeSyntaxTree??!1):void 0;if(o)return g(o,t.includeSyntaxTree??!1);let s=d(e,a),c=_(s,C(s,new Set(a.functionNodeTypes)).filter(e=>!se(e)&&oe(e)),a),l=c.functions,u=me(s,a,0,!1),{lines:p,codeLineNumbers:m}=jt(e,s),h=Pt(s,e);return{language:a.name,bytes:Buffer.byteLength(e),lines:p,functions:l,classCount:Pe(s,a),functionCount:l.length,cyclomaticComplexity:u.cyclomaticComplexity,maxCyclomaticComplexity:vn(l,`cyclomaticComplexity`),cognitiveComplexity:u.cognitiveComplexity,maxCognitiveComplexity:vn(l,`cognitiveComplexity`),nestingDepth:u.nestingDepth,ncssCount:r.countNcss(s,a),callGraph:c.callGraph,coupling:c.coupling,module:c.module,cohesion:c.cohesion,syntaxFeatures:c.syntaxFeatures,typeComplexity:c.typeComplexity,duplication:n.measureDuplication(s,m,t.duplication),halstead:h,maintainabilityIndex:_n(h.volume,u.cyclomaticComplexity,p.code),syntaxTree:t.includeSyntaxTree?s.toString():void 0}}collectDuplicationCandidates(e,t){let r=this.registry.get(t.language);if(!r)throw Error(`Unsupported language: ${t.language}`);return n.collectCrossFileDuplicateCandidates(d(e,r),t.duplication)}};function d(e,t){let n=new a.default;return n.setLanguage(t.parserLanguage),n.parse(e,void 0,{bufferSize:e.length+1}).rootNode}function f(e){let t=e.duplication;return(t?.minTokens??n.defaultDuplicationOptions.minTokens)===n.defaultDuplicationOptions.minTokens&&(t?.maxGapTokens??n.defaultDuplicationOptions.maxGapTokens)===n.defaultDuplicationOptions.maxGapTokens}const p=new u;function m(e,t){return p.measure(e,t)}function h(e,t){return p.collectDuplicationCandidates(e,t)}function g(e,t){let n=P(e.halsteadCounts);return{language:e.language,bytes:e.bytes,lines:e.lines,functions:e.functions.map(e=>({name:e.name,nodeType:e.nodeType,startLine:e.startLine,startColumn:e.startColumn,endLine:e.endLine,returnsJsx:e.returnsJsx,cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,nestingDepth:e.nestingDepth,ncss:e.ncss,callCount:e.callCount,uniqueCalleeCount:e.uniqueCalleeCount,fanIn:e.fanIn,fanOut:e.fanOut,parameterCount:e.parameterCount,recursive:e.recursive})),classCount:e.classCount,functionCount:e.functionCount,cyclomaticComplexity:e.cyclomaticComplexity,maxCyclomaticComplexity:e.maxCyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,maxCognitiveComplexity:e.maxCognitiveComplexity,nestingDepth:e.nestingDepth,ncssCount:e.ncssCount,callGraph:e.callGraph,coupling:e.coupling,module:e.module,cohesion:e.cohesion,syntaxFeatures:e.syntaxFeatures,typeComplexity:e.typeComplexity,duplication:e.duplication,halstead:n,maintainabilityIndex:_n(n.volume,e.cyclomaticComplexity,e.lines.code),syntaxTree:t?e.syntaxTree:void 0}}function _(e,t,n){let r=je(e,n),i=t.map((e,t)=>ee(e,n,t,r)),a=ne(i);return{functions:i.map(e=>({name:e.name,nodeType:e.nodeType,startLine:e.startLine,startColumn:e.startColumn,endLine:e.endLine,returnsJsx:e.returnsJsx,cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,nestingDepth:e.nestingDepth,ncss:e.ncss,callCount:e.callCount,uniqueCalleeCount:e.callees.size,fanIn:a.fanInByIndex.get(e.index)??0,fanOut:a.fanOutByIndex.get(e.index)??0,parameterCount:e.parameterCount,recursive:a.recursiveIndexes.has(e.index)})),callGraph:a.metrics,coupling:ft(e,n),module:Ue(e,n),cohesion:kt(i),syntaxFeatures:pt(e,n.name),typeComplexity:At(e)}}function ee(e,t,n,i){let a=me(e,t,0,!0),o=Ee(e,t,i);return{index:n,name:Ft(e),nodeType:e.type,hasImplementation:ae(e),startLine:e.startPosition.row+1,startColumn:e.startPosition.column,endLine:e.endPosition.row+1,returnsJsx:Ie(e,t),cyclomaticComplexity:a.cyclomaticComplexity,cognitiveComplexity:a.cognitiveComplexity,nestingDepth:a.nestingDepth,ncss:r.countFunctionNcss(e,t),callCount:o.callCount,parameterCount:te(e),callees:o.callees,identifiers:Ne(e)}}function te(e){if(e.childForFieldName(`parameter`))return 1;let t=y(e);if(!t)return 0;if(t.type===`identifier`)return 1;let n=new Set(J(t,`locals`).map(e=>e.id));return $(t.namedChildren.filter(e=>e.type!==`comment`&&e.type!==`self_parameter`&&e.type!==`receiver_parameter`&&e.type!==`positional_separator`&&e.type!==`keyword_separator`&&!n.has(e.id)&&!v(e)).map(e=>e.type===`parameter_declaration`?Math.max(1,J(e,`name`).length):1))+t.children.filter(e=>!e.isNamed&&e.text===`...`).length}function v(e){return e.type===`parameter_declaration`&&e.childForFieldName(`declarator`)===null&&e.childForFieldName(`type`)?.text===`void`}function y(e){let t=e.childForFieldName(`parameters`);if(t)return t;if(e.type===`compact_constructor_declaration`)return e.parent?.parent?.childForFieldName(`parameters`)??void 0;let n=e.childForFieldName(`declarator`);for(;n;){let e=n.childForFieldName(`parameters`);if(e)return e;n=I(n)}return e.namedChildren.find(e=>e.type===`formal_parameters`||e.type===`parameter_list`)}function ne(e){let t=re(e),n=new Set(t.keys()),r=new Map,i=new Map,a=new Map,o=0,s=0,c=new Set;for(let l of e){o+=l.callCount;for(let e of l.callees)c.add(e);let e=new Set([...l.callees].filter(e=>n.has(e))),u=new Set;for(let n of e){let e=t.get(n);e!==void 0&&u.add(e)}a.set(l.index,u),i.set(l.index,e.size),s+=e.size;for(let e of u)r.set(e,(r.get(e)??0)+1)}let l=fn(a);return{fanInByIndex:r,fanOutByIndex:i,recursiveIndexes:l,metrics:{callCount:o,uniqueCalleeCount:c.size,internalCallCount:s,internalEdgeCount:$([...a.values()].map(e=>e.size)),recursiveFunctionCount:l.size,maxFanIn:yn(r),maxFanOut:yn(i),maxCallDepth:mn(a)}}}function re(e){let t=new Map;for(let n of e)!n.name||!n.hasImplementation||t.set(n.name,t.has(n.name)?void 0:n.index);return new Map([...t.entries()].filter(e=>e[1]!==void 0))}const ie=new Set([`function_definition`,`constructor_declaration`,`compact_constructor_declaration`,`function_signature_item`]);function ae(e){return e.type!==`method_declaration`||e.childForFieldName(`body`)!==null}function oe(e){return!ie.has(e.type)||e.childForFieldName(`body`)!==null||e.namedChildren.some(e=>e.type===`try_statement`)}function se(e){return(e.type===`block`||e.type===`do_block`)&&e.parent?.type===`lambda`}function b(e,t){return t.has(e.type)&&!se(e)}const ce=new Set([`switch_statement`,`switch_expression`,`expression_switch_statement`,`type_switch_statement`,`select_statement`,`match_expression`,`match_statement`,`case`,`case_match`]),le=new Set([`case_clause`,`switch_case`,`switch_block_statement_group`,`switch_rule`,`case_statement`,`expression_case`,`type_case`,`communication_case`,`match_arm`,`when`,`in_clause`]),ue=new Set([`if_statement`,`if_expression`,`if`,`unless`]),de=new Set([`throw_statement`]),x=new WeakMap;function fe(e){x.delete(e)}function pe(e){let t=x.get(e);return t||(t={functionNodes:new Set(e.functionNodeTypes),decisionNodes:new Set(e.decisionNodeTypes),nestingNodes:new Set(e.nestingNodeTypes)},x.set(e,t)),t}function me(e,t,n,r){let i=1,a=0,o=n,{functionNodes:s,decisionNodes:c,nestingNodes:l}=pe(t);function u(e,t,n,d,f,p){let m=e.type===`class_body`&&d;m&&(f=!0,n+=1),b(e,s)&&(d&&(f=!0,p||(n+=1)),d=!0);let h=t+n,g=!(r&&f),_=e.isNamed&&c.has(e.type)&&!Ce(e),ee=e.isNamed&&le.has(e.type),te=e.isNamed&&(l.has(e.type)||e.type===`else`&&(e.parent?.type===`case`||e.parent?.type===`case_match`)),v=_&&Se(e);_&&g&&(i+=1),_&&!ee&&!de.has(e.type)&&(a+=v?1:1+h),e.isNamed&&ce.has(e.type)&&(a+=1+h),a+=he(e),ge(e)&&(a+=1),Te(e)&&(g&&(i+=1),ve(e)&&(a+=1)),xe(e)&&(g&&(i+=1),a+=1);let y=te&&!v?t+1:t;g&&(o=Math.max(o,y));for(let t of e.children)u(t,y,n,d,f,m)}for(let t of e.children)u(t,n,0,r,!1,!1);return{cyclomaticComplexity:i,cognitiveComplexity:a,nestingDepth:o}}function he(e){if(!e.isNamed)return 0;if(e.type===`else`)return e.parent?.type===`case`||e.parent?.type===`case_match`?0:1;if(e.type===`else_clause`)return+!e.namedChildren.some(e=>ue.has(e.type));if(e.type!==`if_statement`&&e.type!==`if_expression`)return 0;let t=0;for(let n=0;n<e.childCount;n+=1){let r=e.child(n);r&&e.fieldNameForChild(n)===`alternative`&&r.type!==`else_clause`&&r.type!==`elif_clause`&&!ue.has(r.type)&&(t+=1)}return t}function ge(e){return e.isNamed?e.type===`goto_statement`?!0:e.type===`break_expression`||e.type===`continue_expression`?e.namedChildren.some(e=>e.type===`label`||e.type===`loop_label`):(e.type===`break_statement`||e.type===`continue_statement`)&&e.namedChildren.some(e=>!r.commentNodeTypes.has(e.type)):!1}const _e=new Set([`parenthesized_expression`,`parenthesized_statements`]);function ve(e){let t=e.parent;if(!t)return!0;let n=t.parent;for(;n&&_e.has(n.type);)n=n.parent;return!n||n.type!==t.type||ye(be(n))!==ye(e.text)}function ye(e){return e===`and`?`&&`:e===`or`?`||`:e}function be(e){let t=e.childForFieldName(`operator`);return t?t.text:e.children.find(e=>!e.isNamed&&o.has(e.text))?.text}function xe(e){return e.isNamed?e.type===`guard`||e.type===`if_guard`||e.type===`unless_guard`||e.type===`if_clause`||e.type===`match_pattern`&&e.children.some(e=>!e.isNamed&&e.type===`if`):!1}function Se(e){if(e.type===`elsif`||e.type===`elif_clause`)return!0;if(e.type!==`if_statement`&&e.type!==`if_expression`&&e.type!==`if`)return!1;let t=e.parent;return t?t.type===`else_clause`||t.childForFieldName(`alternative`)?.id===e.id:!1}function Ce(e){if(e.type===`case_statement`)return e.childForFieldName(`value`)===null;if(e.type===`switch_block_statement_group`||e.type===`switch_rule`){let t=e.namedChildren.find(e=>e.type===`switch_label`);return t!==void 0&&t.namedChildCount===0}if(e.type===`case_clause`||e.type===`match_arm`){let t=e.namedChildren.find(e=>e.type===`case_pattern`||e.type===`match_pattern`);if(!t)return!1;if(t.child(0)?.type===`_`&&(t.childCount===1||t.child(1)?.type===`if`))return!0;let n=t.namedChildCount===1?t.namedChild(0):void 0;return e.type===`case_clause`&&n?.type===`dotted_name`&&n.namedChildCount===1&&n.namedChild(0)?.type===`identifier`}return e.type===`in_clause`&&e.namedChild(0)?.type===`identifier`}const we=new Set([`binary_expression`,`binary`,`boolean_operator`]);function Te(e){if(e.isNamed||!o.has(e.text))return!1;let t=e.parent;return t!==null&&we.has(t.type)}function Ee(e,t,n=new Set){let r=new Set,i=pe(t).functionNodes,a=0;function o(e,s){if(!(!s&&b(e,i))){if(!(t.name===`cpp`&&Oe(e)))if(L(e)){a+=1;let i=t.name===`cpp`&&(e.type===`new_expression`||e.type===`call_expression`&&n.has(S(e.childForFieldName(`function`))??``))?void 0:Gt(e);if(i&&r.add(i),t.name===`ruby`&&e.type===`call`&&e.parent?.type===`operator_assignment`&&e.parent.childForFieldName(`left`)?.id===e.id){a+=1;let t=e.childForFieldName(`method`);t&&r.add(`${t.text}=`)}}else(Me(e,t)||ke(e,n))&&(a+=1);for(let t of e.namedChildren)o(t,!1)}}return o(e,!0),{callCount:a,callees:r}}const De=new Set([`static_cast`,`dynamic_cast`,`const_cast`,`reinterpret_cast`]);function Oe(e){if(e.type!==`call_expression`)return!1;let t=e.childForFieldName(`function`);if(t?.type===`primitive_type`)return!0;let n=t?.type===`template_function`?t.childForFieldName(`name`)?.text:t?.text;return n!==void 0&&De.has(n)}function ke(e,t){if(t.size===0)return!1;if(e.type===`compound_literal_expression`)return t.has(S(e.childForFieldName(`type`))??``);if(e.type===`init_declarator`){let n=e.childForFieldName(`value`);return n?.type!==`argument_list`&&n?.type!==`initializer_list`?!1:t.has(S(e.parent?.childForFieldName(`type`))??``)}if((e.type===`identifier`||e.type===`array_declarator`)&&e.parent?.type===`declaration`&&J(e.parent,`declarator`).some(t=>t.id===e.id)&&!k(e.parent,`extern`)){let n=e;for(;n?.type===`array_declarator`;)n=n.childForFieldName(`declarator`);return n?.type===`identifier`&&t.has(S(e.parent.childForFieldName(`type`))??``)}if(e.type===`field_initializer`){let n=e.namedChild(0),r=n?.type===`field_identifier`?n.text:S(n);return t.has(r??``)}return!1}function S(e){let t=e;for(;t;){if(t.type===`type_identifier`||t.type===`identifier`)return t.text;if(t.type===`qualified_identifier`||t.type===`scoped_identifier`||t.type===`template_type`||t.type===`template_function`){t=t.childForFieldName(`name`);continue}return}}const Ae=new Set([`class_specifier`,`struct_specifier`,`union_specifier`]);function je(e,t){let n=new Set;if(t.name!==`cpp`)return n;for(let t of C(e,Ae)){let e=t.childForFieldName(`name`)?.text;e&&t.childForFieldName(`body`)&&n.add(e)}return n}function Me(e,t){return t.name===`ruby`?e.type===`yield`||e.type===`super`&&e.parent?.type!==`call`:!1}function Ne(e){let t=new Set;function n(e){(e.type===`identifier`||e.type===`property_identifier`||e.type===`field_identifier`||e.type===`constant`||e.type===`instance_variable`||e.type===`class_variable`||e.type===`global_variable`)&&t.add(e.text);for(let t of e.namedChildren)n(t)}return n(e),t}function Pe(e,t){return C(e,new Set(t.classNodeTypes)).filter(Fe).length}function Fe(e){return e.type===`object_creation_expression`||e.type===`enum_constant`?e.namedChildren.some(e=>e.type===`class_body`):!e.type.endsWith(`_specifier`)||e.childForFieldName(`body`)!==null}function C(e,t){let n=[];function r(e){t.has(e.type)&&n.push(e);for(let t of e.namedChildren)r(t)}return r(e),n}function Ie(e,t){let n=new Set(t.functionNodeTypes);function r(t,i){if(!i&&n.has(t.type))return!1;if(t.type===`return_statement`||e.type===`arrow_function`&&t.id===w(e)?.id&&t.type!==`statement_block`&&!n.has(t.type))return T(t,n)||E(t,n);for(let e of t.namedChildren)if(r(e,!1))return!0;return!1}return r(e,!0)}function w(e){return e.childForFieldName(`body`)??e.namedChild(e.namedChildCount-1)??void 0}function T(e,t){return Le(e,t,e=>e.type.startsWith(`jsx_`)||Re(e,t))}function E(e,t){return Le(e,t,Yt)}function Le(e,t,n){function r(e,i){if(!i&&t.has(e.type))return!1;if(n(e))return!0;for(let t of e.namedChildren)if(r(t,!1))return!0;return!1}return r(e,!0)}function Re(e,t){return!L(e)||!ze(e.childForFieldName(`function`)??e.namedChild(0))?!1:e.namedChildren.some(e=>Be(e,t))}function ze(e){if(!e)return!1;let t=R(e);return t===`map`||t===`flatMap`}function Be(e,t){return t.has(e.type)?Ve(e,t):e.namedChildren.some(e=>Be(e,t))}function Ve(e,t){let n=e.type===`arrow_function`?w(e):void 0;return n&&n.type!==`statement_block`&&!t.has(n.type)?T(n,t)||E(n,t):He(e,t,e=>T(e,t)||E(e,t))}function He(e,t,n){function r(e,i){if(!i&&t.has(e.type))return!1;if(e.type===`return_statement`&&n(e))return!0;for(let t of e.namedChildren)if(r(t,!1))return!0;return!1}return r(e,!0)}function Ue(e,t){let n=new Set;function r(e){if(Zt(e,t))for(let r of $t(e,t,{expandPythonSubmodules:!0}))n.add(r);for(let t of e.namedChildren)r(t)}return r(e),{declarations:We(e,t),importSources:[...n]}}function We(e,t){let n=st(e),r=t.name===`java`?Ge(e):``;return e.namedChildren.flatMap(e=>D(e,!1,r,t.name===`cpp`)).map(e=>n.has(e.name)?{...e,exported:!0}:e)}function Ge(e){let t=e.namedChildren.find(e=>e.type===`package_declaration`)?.namedChildren.find(e=>e.type===`scoped_identifier`||e.type===`identifier`);return t?`${t.text}::`:``}const Ke=new Set([`module`,`class`,`singleton_class`]);function D(e,t,n=``,r=!1){if(it(e))return e.namedChildren.flatMap(e=>D(e,!0,n,r));if(e.type===`namespace_definition`){let i=e.childForFieldName(`name`)?.text;return i?(e.childForFieldName(`body`)?.namedChildren??[]).flatMap(e=>D(e,t,`${n}${i}::`,r)):[]}return at(e)?e.namedChildren.flatMap(e=>D(e,t,n,r)):e.type===`declaration`?O(et(e,t,r),n):e.type===`type_definition`?O(Ye(e,t),n):Ke.has(e.type)?qe(e,t,n):e.type===`assignment`||e.type===`operator_assignment`?O(Je(e,t),n,!0):O(A(e,t),n)}function O(e,t,n=!1){return t?e.map(e=>n&&e.name.includes(`::`)?e:{...e,name:`${t}${e.name}`}):e}function qe(e,t,n=``){let r=O(A(e,t),n,!0),i=r[0]?`${r[0].name}::`:n,a=e.childForFieldName(`body`);for(let e of a?.namedChildren??[])Ke.has(e.type)?r.push(...qe(e,t,i)):(e.type===`assignment`||e.type===`operator_assignment`)&&r.push(...O(Je(e,t),i,!0));return r}function Je(e,t){if(e.type===`operator_assignment`&&!e.children.some(e=>!e.isNamed&&e.text===`||=`))return[];let n=e.childForFieldName(`left`);return n?(n.type===`left_assignment_list`?n.namedChildren:[n]).filter(e=>e.type===`constant`||e.type===`scope_resolution`&&e.childForFieldName(`name`)?.type===`constant`).map(e=>({exported:t,name:e.text,startLine:e.startPosition.row+1})):[]}function Ye(e,t){let n=e.childForFieldName(`type`),r=n?A(n,t):[],i=n?.type.endsWith(`_specifier`)&&!n.childForFieldName(`body`)?n.childForFieldName(`name`)?.text:void 0,a=new Set(r.map(e=>e.name));for(let n of J(e,`declarator`)){let e=n.type===`type_identifier`?n.text:F(n);e&&e!==i&&!a.has(e)&&(a.add(e),r.push({exported:t,name:e,startLine:n.startPosition.row+1}))}return r}const Xe=new Set([`init_declarator`,`pointer_declarator`,`array_declarator`,`reference_declarator`,`identifier`,`field_identifier`]);function Ze(e){if(e.type===`pointer_declarator`||e.type===`reference_declarator`){let t=e;for(;t&&(t.type===`pointer_declarator`||t.type===`reference_declarator`||t.type===`array_declarator`);)t=I(t);return t?.type!==`function_declarator`||t.childForFieldName(`declarator`)?.type===`parenthesized_declarator`}return Xe.has(e.type)?!0:e.type===`function_declarator`&&e.childForFieldName(`declarator`)?.type===`parenthesized_declarator`}function k(e,t){return e.children.some(e=>e.type===`storage_class_specifier`&&e.text===t)}function Qe(e){let t=e.childForFieldName(`type`);return t?.type!==`type_identifier`||t.text!==`import`&&t.text!==`export`&&t.text!==`module`?!1:!$e(e,t.text)}function $e(e,t){let n=e;for(;n.parent;)n=n.parent;return C(n,new Set([`type_definition`,`alias_declaration`])).some(e=>(e.childForFieldName(`declarator`)??e.childForFieldName(`name`))?.text===t)}function et(e,t,n=!1){if(n&&Qe(e)||k(e,`static`))return[];let r=e.childForFieldName(`type`),i=r?A(r,t):[],a=new Set(i.map(e=>e.name)),o=k(e,`extern`);for(let r of e.namedChildren.filter(Ze)){if(o&&r.type!==`init_declarator`||n&&!o&&!k(e,`inline`)&&!tt(r)&&!M(e,r))continue;let s=F(r);s&&!a.has(s)&&(a.add(s),i.push({exported:t,name:s,startLine:r.startPosition.row+1}))}return i}function tt(e){let t=e.type===`init_declarator`?e.childForFieldName(`declarator`)??e:e;for(;t;){if(t.type===`reference_declarator`)return!0;t=I(t)}return!1}function A(e,t){if(!ot(e)||e.type.endsWith(`_specifier`)&&!e.childForFieldName(`body`)||k(e,`static`))return[];if(e.type===`enum_specifier`)return nt(e,t);let n=rt(e);return n?[{exported:t,name:n,startLine:e.startPosition.row+1}]:[]}function nt(e,t){let n=[],r=e.childForFieldName(`name`)?.text;r&&n.push({exported:t,name:r,startLine:e.startPosition.row+1});let i=e.children.some(e=>!e.isNamed&&(e.text===`class`||e.text===`struct`));for(let a of e.childForFieldName(`body`)?.namedChildren??[]){if(a.type!==`enumerator`)continue;let e=a.childForFieldName(`name`)?.text;e&&n.push({exported:t,name:i&&r?`${r}::${e}`:e,startLine:a.startPosition.row+1})}return n}function rt(e){if(e.type===`method_declaration`&&e.childForFieldName(`receiver`))return ut(e);let t=e.childForFieldName(`name`);return t?.type===`template_type`&&(t=t.childForFieldName(`name`)),t?.type===`scope_resolution`?t.text:t?j(t)?t.text:void 0:F(e.childForFieldName(`declarator`),!0)||e.namedChildren.find(j)?.text}function it(e){return e.type===`export_statement`||e.type===`export_declaration`}function at(e){return e.type===`lexical_declaration`||e.type===`variable_declaration`||e.type===`decorated_definition`||e.type===`type_declaration`||e.type===`const_declaration`||e.type===`var_declaration`||e.type===`var_spec_list`||e.type===`linkage_specification`||e.type===`template_declaration`||e.type===`declaration_list`||e.type===`preproc_ifdef`||e.type===`preproc_if`||e.type===`preproc_else`||e.type===`preproc_elif`}function ot(e){return e.type===`function_declaration`||e.type===`function_definition`||e.type===`function_item`||e.type===`method_declaration`||e.type===`class_declaration`||e.type===`class_definition`||e.type===`interface_declaration`||e.type===`type_alias_declaration`||e.type===`type_declaration`||e.type===`type_spec`||e.type===`const_spec`||e.type===`var_spec`||e.type===`variable_declarator`||e.type===`struct_item`||e.type===`enum_item`||e.type===`union_item`||e.type===`trait_item`||e.type===`type_item`||e.type===`const_item`||e.type===`static_item`||e.type===`mod_item`||e.type===`enum_declaration`||e.type===`record_declaration`||e.type===`annotation_type_declaration`||e.type===`method`||e.type===`singleton_method`||e.type===`class`||e.type===`module`||e.type===`alias_declaration`||e.type===`struct_specifier`||e.type===`class_specifier`||e.type===`enum_specifier`||e.type===`union_specifier`}function j(e){return e.type===`identifier`||e.type===`type_identifier`||e.type===`property_identifier`||e.type===`field_identifier`||e.type===`constant`}function st(e){let t=new Set;function n(e,r){if(!r&&ct(e)){let n=lt(e);n&&t.add(n)}let i=r||it(e)&&e.childForFieldName(`source`)!==null;for(let t of e.namedChildren)n(t,i)}return n(e,!1),t}function ct(e){return e.type===`export_specifier`||e.type===`namespace_export`}function lt(e){let t=e.childForFieldName(`name`)??e.childForFieldName(`alias`)??e.namedChildren.find(j);return t&&j(t)?t.text:void 0}function ut(e){let t=e.childForFieldName(`name`),n=e.childForFieldName(`receiver`)?.namedChildren[0]?.childForFieldName(`type`);return!t||!j(t)||!n?t&&j(t)?t.text:void 0:`${dt(n.text)}.${t.text}`}function dt(e){return e.replaceAll(/\s+/gu,``).replace(/^\*+/u,``)}function ft(e,t){let n=new Set,r=0,i=0;function a(e){if((t.name!==`go`||e.type!==`import_declaration`&&e.type!==`import_spec_list`)&&(Xt(e)||Qt(e,t)||z(e,t)||B(e)||V(e,t))&&(r+=1),Zt(e,t))for(let r of $t(e,t,{expandPythonSubmodules:!1}))n.add(r);dn(e)&&(i+=1);for(let t of e.namedChildren)a(t)}a(e);let o=[...n].filter(e=>on(e,t.name)).length;return{importCount:r,importSourceCount:n.size,relativeImportCount:o,externalImportCount:n.size-o,exportCount:i}}function pt(e,t){let n={assignmentCount:0,awaitExpressionCount:0,loopStatementCount:0,mutableBindingCount:0,returnStatementCount:0,throwStatementCount:0,tryStatementCount:0};function r(e){mt(e)&&(n.assignmentCount+=1),ht(e)&&(n.awaitExpressionCount+=1),gt(e)&&(n.loopStatementCount+=1),n.mutableBindingCount+=_t(e,t),wt(e)&&(n.returnStatementCount+=1),Tt(e)&&(n.throwStatementCount+=1),Dt(e)&&(n.tryStatementCount+=1);for(let t of e.namedChildren)r(t)}return r(e),n}function mt(e){return e.type===`assignment_expression`||e.type===`augmented_assignment_expression`||e.type===`assignment_statement`||e.type===`assignment`||e.type===`augmented_assignment`||e.type===`operator_assignment`||e.type===`short_var_declaration`||e.type===`compound_assignment_expr`||e.type===`named_expression`||e.type===`update_expression`||e.type===`inc_statement`||e.type===`dec_statement`}function ht(e){return e.type===`await_expression`||e.type===`await`||e.type===`co_await_expression`}function gt(e){return e.type===`for_statement`||e.type===`for_in_statement`||e.type===`enhanced_for_statement`||e.type===`for_range_loop`||e.type===`while_statement`||e.type===`do_statement`||e.type===`for_expression`||e.type===`while_expression`||e.type===`loop_expression`||e.type===`while`||e.type===`until`||e.type===`for`||e.type===`while_modifier`||e.type===`until_modifier`}function _t(e,t){let n=t===`c`||t===`cpp`;if(e.type===`local_variable_declaration`||e.type===`field_declaration`&&t===`java`){let t=e.namedChildren.filter(e=>e.type===`variable_declarator`);return xt(e)?t.length:0}if(n&&(e.type===`declaration`||e.type===`field_declaration`))return vt(e,t===`cpp`);if(n&&e.type===`function_definition`){let n=e.childForFieldName(`declarator`);return n?.type===`field_identifier`||n?.type===`identifier`?vt(e,t===`cpp`):0}if(e.type===`enhanced_for_statement`)return+!!xt(e);if(t===`java`&&(e.type===`instanceof_expression`||e.type===`type_pattern`||e.type===`record_pattern_component`)){let t=e.type===`instanceof_expression`?e.childForFieldName(`name`)!==null:e.namedChildren.some(e=>e.type===`identifier`),n=e.children.some(e=>!e.isNamed&&e.text===`final`);return t&&!n?1:0}if(n&&e.type===`for_range_loop`){let t=e.childForFieldName(`declarator`);return t&&M(e,t)?yt(t):0}return+!!bt(e)}function vt(e,t){return t&&Qe(e)?0:$(e.namedChildren.filter(t=>Ze(t)&&M(e,t)).map(yt))}function yt(e){let t=e.type===`init_declarator`?e.childForFieldName(`declarator`)??e:e;return t.type===`structured_binding_declarator`?Math.max(1,t.namedChildren.filter(e=>e.type===`identifier`).length):1}function bt(e){return e.type===`lexical_declaration`&&e.firstChild?.text===`let`||e.type===`variable_declaration`&&e.firstChild?.text===`var`||e.type===`var_declaration`||e.type===`let_declaration`&&Ct(e)}function xt(e){return!e.namedChildren.find(e=>e.type===`modifiers`)?.children.some(e=>e.text===`final`)}function M(e,t){let n=t.type===`init_declarator`?t.childForFieldName(`declarator`)??t:t,r=!1;for(;n.type===`reference_declarator`||n.type===`pointer_declarator`||n.type===`array_declarator`||n.type===`parenthesized_declarator`||n.type===`function_declarator`;){if(n.type===`reference_declarator`)return!1;let e=I(n);if(!e)break;if(n.type===`pointer_declarator`&&(r=!0,N(n)&&!St(e)))return!1;n=e}return r||!N(e)}function St(e){let t=e;for(;t;){if(t.type===`pointer_declarator`)return!0;t=I(t)}return!1}function N(e){return e.namedChildren.some(e=>e.type===`type_qualifier`&&(e.text===`const`||e.text===`constexpr`))}function Ct(e){if(e.children.some(e=>e.type===`mutable_specifier`))return!0;let t=e.childForFieldName(`pattern`);return t?t.descendantsOfType(`mutable_specifier`).some(e=>e.parent?.type!==`reference_pattern`):!1}function wt(e){return e.type===`return_statement`||e.type===`return_expression`||e.type===`return`||e.type===`co_return_statement`}function Tt(e){return e.type===`throw_statement`||e.type===`raise_statement`||Et(e)}function Et(e){if(e.type!==`call`||e.childForFieldName(`receiver`))return!1;let t=e.childForFieldName(`method`);return t?.type===`identifier`&&(t.text===`raise`||t.text===`fail`)}function Dt(e){return e.type===`try_statement`||e.type===`try_with_resources_statement`||e.type===`rescue_modifier`||Ot(e)}function Ot(e){return(e.type===`begin`||e.type===`body_statement`)&&e.namedChildren.some(e=>e.type===`rescue`||e.type===`ensure`)}function kt(e){let t=new Map;for(let n of e)for(let e of n.identifiers)t.set(e,(t.get(e)??0)+1);let n=0;for(let e of t.values())e>=2&&(n+=1);let r=e.length,i=r*(r-1)/2,a=Math.max(1,Math.ceil(i/25e4)),o=0,s=0,c=0,l=0,u=r-1;for(let t=0;t<i;t+=a){for(;t>=l+u;)l+=u,c+=1,u=r-1-c;let n=c+1+(t-l),i=e[c],a=e[n];if(!i||!a)continue;let d=gn(i.identifiers,a.identifiers),f=i.identifiers.size+a.identifiers.size-d;o+=f===0?0:d/f,s+=1}return{averageFunctionIdentifierOverlap:s===0?1:o/s,sharedIdentifierCount:n,uniqueIdentifierCount:t.size}}function At(e){let t={typeAnnotationCount:0,typeAliasCount:0,interfaceCount:0,genericParameterCount:0,unionTypeCount:0,intersectionTypeCount:0,conditionalTypeCount:0,typeAssertionCount:0,nonNullAssertionCount:0,satisfiesExpressionCount:0};function n(e){switch(e.type){case`type_annotation`:t.typeAnnotationCount+=1;break;case`type_alias_declaration`:t.typeAliasCount+=1;break;case`interface_declaration`:t.interfaceCount+=1;break;case`type_parameters`:case`type_parameter`:t.genericParameterCount+=+(e.type===`type_parameter`);break;case`union_type`:t.unionTypeCount+=1;break;case`intersection_type`:t.intersectionTypeCount+=1;break;case`conditional_type`:t.conditionalTypeCount+=1;break;case`as_expression`:case`type_assertion`:t.typeAssertionCount+=1;break;case`non_null_expression`:t.nonNullAssertionCount+=1;break;case`satisfies_expression`:t.satisfiesExpressionCount+=1}for(let t of e.namedChildren)n(t)}return n(e),t}function jt(e,t){let n=e.length===0?[]:e.split(/\r\n|\n|\r/),r=new Map;for(let e of Mt(t)){let t=r.get(e.line)??[];t.push(e),r.set(e.line,t)}let i=0,a=0,o=new Set;for(let[e,t]of n.entries()){if(t.trim()===``){i+=1;continue}Nt(t,r.get(e)??[])?a+=1:o.add(e+1)}return{lines:{total:n.length,code:o.size,comment:a,blank:i},codeLineNumbers:o}}function Mt(e){let t=[];function n(e){if(e.type===`comment`||e.type===`line_comment`||e.type===`block_comment`)for(let n=e.startPosition.row;n<=e.endPosition.row;n+=1)t.push({line:n,startColumn:n===e.startPosition.row?e.startPosition.column:0,endColumn:n===e.endPosition.row?e.endPosition.column:1/0});for(let t of e.namedChildren)n(t)}return n(e),t}function Nt(e,t){if(t.length===0)return!1;for(let n=0;n<e.length;n+=1)if(!/\s/u.test(e[n]??` `)&&!t.some(e=>e.startColumn<=n&&n<e.endColumn))return!1;return!0}function Pt(e,t){let n=new Map,r=new Map;function i(e){if(e.type!==`comment`&&e.type!==`line_comment`&&e.type!==`block_comment`){if(l.has(e.type)){Q(r,t.slice(e.startIndex,e.endIndex));return}if(e.childCount===0){let i=t.slice(e.startIndex,e.endIndex);c.has(e.type)?Q(r,i):(s.has(i)||s.has(e.type))&&Jt(e,i)&&Q(n,i||e.type);return}for(let t of e.children)i(t)}}return i(e),P({distinctOperators:n.size,distinctOperands:r.size,totalOperators:$(n.values()),totalOperands:$(r.values())})}function P(e){let{distinctOperators:t,distinctOperands:n,totalOperators:r,totalOperands:i}=e,a=t+n,o=r+i,s=a===0?0:o*Math.log2(a),c=n===0?0:t/2*(i/n),l=c*s;return{distinctOperators:t,distinctOperands:n,totalOperators:r,totalOperands:i,vocabulary:a,length:o,volume:s,difficulty:c,effort:l,time:l/18,bugs:s/3e3}}function Ft(e){let t=Vt(e);if(t)return t;let n=e.childForFieldName(`name`);if(n)return n.text;let r=It(e);if(r)return r;let i=e.parent;if(i){if(e.type===`closure_expression`&&i.type===`let_declaration`){let e=i.childForFieldName(`pattern`);return e?.type===`identifier`?e.text:void 0}return e.type===`lambda_expression`&&i.type===`init_declarator`?F(i.childForFieldName(`declarator`)):e.type===`func_literal`&&i.type===`expression_list`?zt(e,i):e.type===`lambda`&&i.type===`assignment`?Lt(i):(e.type===`block`||e.type===`do_block`)&&Rt(i)?i.parent?.type===`assignment`?Lt(i.parent):void 0:i.childForFieldName(`name`)?.text}}function It(e){return F(e.childForFieldName(`declarator`))}function F(e,t=!1){let n=e,r=``;for(;n;)switch(n.type){case`identifier`:case`field_identifier`:case`type_identifier`:case`destructor_name`:case`operator_name`:return r?`${r}::${n.text}`:n.text;case`operator_cast`:{let e=`operator ${n.childForFieldName(`type`)?.text??``}`.trimEnd();return r?`${r}::${e}`:e}case`template_function`:n=n.childForFieldName(`name`);break;case`qualified_identifier`:if(t){let e=n.childForFieldName(`scope`)?.text.replaceAll(/\s+/gu,``);e&&(r=r?`${r}::${e}`:e)}n=n.childForFieldName(`name`);break;default:n=I(n)}}function I(e){let t=e.childForFieldName(`declarator`);if(t)return t;if(e.type===`reference_declarator`||e.type===`parenthesized_declarator`)return e.namedChild(0)??void 0}function Lt(e){let t=e.childForFieldName(`left`);return t?.type===`identifier`||t?.type===`constant`?t.text:void 0}function Rt(e){if(e.type!==`call`||e.childForFieldName(`receiver`))return!1;let t=e.childForFieldName(`method`);return t?.type===`identifier`&&(t.text===`lambda`||t.text===`proc`)}function zt(e,t){let n=t.parent,r=t.namedChildren.filter(e=>e.type!==`comment`).findIndex(t=>t.id===e.id);if(!(!n||r===-1)){if(n.type===`short_var_declaration`){let e=n.childForFieldName(`left`)?.namedChildren.filter(e=>e.type!==`comment`);return Bt(e?.[r])}if(n.type===`var_spec`){let e=J(n,`name`)[r];return Bt(e)}}}function Bt(e){return e?.type===`identifier`&&e.text!==`_`?e.text:void 0}function Vt(e){let t=e;for(;t;){let e=t.parent,n=e?.parent;if(e?.type!==`arguments`||n?.type!==`call_expression`||!Ht(n))return;let r=n.parent;if(r?.type===`variable_declarator`)return r.childForFieldName(`name`)?.text;t=n}}function Ht(e){let t=e.childForFieldName(`function`)??e.namedChild(0);return t?.text===`memo`||t?.text===`React.memo`||t?.text===`forwardRef`||t?.text===`React.forwardRef`}function L(e){return e.type===`call_expression`||e.type===`call`||e.type===`method_invocation`||e.type===`macro_invocation`||e.type===`new_expression`||e.type===`object_creation_expression`||e.type===`explicit_constructor_invocation`}function Ut(e){for(let t of e.children)if(t.type===`ERROR`){let e=t.children.find(e=>e.type===`operator_name`);if(e)return e.text}let t=e.childForFieldName(`function`);if(t?.type===`field_expression`){let e=t.children.findIndex(e=>e.type===`ERROR`&&e.text===`operator`),n=e===-1?void 0:t.children[e+1];if(n?.type===`field_identifier`||n?.type===`primitive_type`)return`operator ${n.text}`}}const Wt=new Set([`arrow_function`,`function_expression`,`function`,`lambda`,`lambda_expression`,`closure_expression`,`func_literal`,`anonymous_function`]);function Gt(e){if(e.type===`call`){let t=e.childForFieldName(`method`),n=e.childForFieldName(`receiver`);if(t?.text===`call`&&n?.type===`identifier`)return n.text;if(t&&e.parent?.type===`assignment`&&e.parent.childForFieldName(`left`)?.id===e.id)return`${t.text}=`;if(t?.type===`operator`)return t.text}if(e.type===`call_expression`){let t=Ut(e);if(t)return t}let t=e.childForFieldName(`function`)??e.childForFieldName(`name`)??e.childForFieldName(`method`)??e.childForFieldName(`constructor`)??e.childForFieldName(`type`)??e.namedChild(0);if(!t)return;let n=Kt(t);if(!Wt.has(n.type))return R(n)}function Kt(e){let t=e;for(;t.type===`parenthesized_expression`&&t.namedChildCount===1;){let e=t.namedChild(0);if(!e)break;t=e}return t}const qt=new Set([`ternary_expression`,`conditional_expression`,`conditional`,`try_expression`,`conditional_type`]);function Jt(e,t){if(t===`@`){let t=e.parent?.type;return t===`binary_operator`||t===`augmented_assignment`}if(t!==`?`)return!0;let n=e.parent?.type;return n!==void 0&&qt.has(n)}function R(e){if(e.type===`generic_function`||e.type===`template_function`||e.type===`template_method`){let t=e.childForFieldName(`function`)??e.childForFieldName(`name`);if(t)return R(t)}if(e.type===`destructor_name`)return e.text;if(e.type===`generic_type`){let t=e.namedChildren.find(e=>e.type===`type_identifier`||e.type===`scoped_type_identifier`);if(t)return R(t)}if(e.type===`identifier`||e.type===`property_identifier`||e.type===`field_identifier`||e.type===`type_identifier`||e.type===`attribute`)return e.text;for(let t=e.namedChildCount-1;t>=0;--t){let n=e.namedChild(t);if(!n)continue;let r=R(n);if(r)return r}}function Yt(e){if(!L(e))return!1;let t=e.childForFieldName(`function`)??e.namedChild(0);return t?.text===`React.createElement`||t?.text===`createElement`}function Xt(e){return e.type===`import_statement`||e.type===`import_declaration`||e.type===`import_from_statement`||e.type===`import_spec`||e.type===`import_spec_list`||e.type===`use_declaration`||e.type===`extern_crate_declaration`||e.type===`requires_module_directive`||e.type===`preproc_include`}function Zt(e,t){return Xt(e)||Qt(e,t)||z(e,t)||B(e)||V(e,t)||dn(e)&&e.childForFieldName(`source`)!==null}function z(e,t){if(t.name!==`cpp`)return!1;if(e.type===`declaration`){let t=e.childForFieldName(`type`);return t?.type===`type_identifier`?t.text===`import`?!$e(e,`import`):t.text===`export`&&/^export\s+import\b/u.test(e.text):!1}return e.type===`labeled_statement`||e.type===`expression_statement`?e.parent?.type===`translation_unit`&&/^import\s+[:"<]/u.test(e.text):!1}function Qt(e,t){return t.name===`rust`&&e.type===`mod_item`&&!e.childForFieldName(`body`)}function B(e){return L(e)?(e.childForFieldName(`function`)??e.namedChild(0))?.text===`import`:!1}function $t(e,t,n){if(t.name===`python`){let t=cn(e,n);if(t.length>0)return t}if(t.name===`rust`)return sn(e);if(t.name===`java`&&e.type===`requires_module_directive`){let t=e.childForFieldName(`module`);return t?[Y(t.text)]:[]}if(t.name===`java`&&e.type===`import_declaration`){let t=e.namedChild(0);if(!t)return[];let n=e.children.some(e=>e.type===`static`),r=e.namedChildren.some(e=>e.type===`asterisk`),i=Y(t.text);return[r&&!n?`${i}.*`:i]}if(z(e,t)){let t=/^(?:export\s+)?import\s+([\w.:]+|"[^"]+"|<[^>]+>)/u.exec(e.text)?.[1];return t?t.startsWith(`"`)?[`./${Z(t)}`]:[t]:[]}if(V(e,t))return tn(e);if(e.type===`preproc_include`){let t=e.childForFieldName(`path`);if(!t)return[];let n=Z(t.text);return[t.type===`string_literal`&&!n.startsWith(`.`)&&!n.startsWith(`/`)?`./${n}`:n]}if(B(e))return an(e);let r=e.childForFieldName(`source`)??un(e);return r?[Z(r.text)]:[]}const en=new Set([`require`,`require_relative`,`load`]);function V(e,t){if(t.name!==`ruby`||e.type!==`call`)return!1;let n=e.childForFieldName(`method`);if(n?.type!==`identifier`)return!1;if(n.text===`autoload`){let t=e.childForFieldName(`receiver`);return t===null||t.type===`constant`||t.type===`scope_resolution`}return e.childForFieldName(`receiver`)===null&&en.has(n.text)}function tn(e){let t=e.childForFieldName(`arguments`),n=e.childForFieldName(`method`)?.text===`autoload`,r=t?.namedChild(+!!n);if(!r||r.type!==`string`||r.namedChildren.some(e=>e.type===`interpolation`))return[];let i=r.namedChildren.filter(e=>e.type===`string_content`||e.type===`escape_sequence`),a=i.length>0?i.map(e=>e.type===`escape_sequence`?rn(e.text):e.text).join(``):Z(r.text);return e.childForFieldName(`method`)?.text===`require_relative`?[a.startsWith(`.`)?a:`./${a}`]:[a.replace(/^(?:\.\.?\/)+/u,``)]}const nn=new Map([[`n`,`
|
|
1
|
+
"use strict";const e=require("./_virtual/_rolldown/runtime.cjs"),t=require("./languages.cjs"),n=require("./duplication.cjs"),r=require("./ncss.cjs"),i=require("./nativeMetrics.cjs");let a=require("tree-sitter");a=e.__toESM(a,1);const o=new Set([`&&`,`||`,`and`,`or`]),s=new Set(`+,-,*,/,%,**,=,+=,-=,*=,/=,%=,==,!=,===,!==,<,<=,>,>=,!,~,&,|,^,++,--,<<,>>,>>>,=>,**=,<<=,>>=,>>>=,&=,|=,^=,&&=,||=,??=,??,?.,?,//,//=,@,@=,:=,<-,<=>,=~,..,...,..=,&&,||,!~,&^,&^=,&.,.,->,::,->*,.*,sizeof,alignof,defined?,as,bitand,bitor,xor,compl,and_eq,or_eq,xor_eq,not_eq,and,or,not,in,is,instanceof,typeof,new,delete,return,throw,raise,yield,await,co_await,co_yield,co_return,break,continue`.split(`,`)),c=new Set(`identifier.property_identifier.field_identifier.type_identifier.constant.instance_variable.class_variable.global_variable.simple_symbol.self.this.super.primitive_type.boolean_type.void_type.auto.number.integer.float.integer_literal.float_literal.int_literal.rune_literal.imaginary_literal.number_literal.decimal_integer_literal.hex_integer_literal.octal_integer_literal.binary_integer_literal.decimal_floating_point_literal.hex_floating_point_literal.string.string_literal.raw_string_literal.string_fragment.multiline_string_fragment.string_content.raw_string_content.template_string.character_literal.char_literal.character.true.false.null.null_literal.undefined.nil.none`.split(`.`)),l=new Set([`interpreted_string_literal`,`regex`,`user_defined_literal`,`integral_type`,`floating_point_type`,`sized_type_specifier`,`placeholder_type_specifier`]);var u=class{registry=t.createLanguageRegistry();registerLanguage(e){fe(e),r.invalidateNcssSetsCache(e),this.registry.set(e.name,e);for(let t of e.aliases??[])this.registry.set(t,e)}getSupportedLanguages(){return[...new Set([...this.registry.values()].map(e=>e.name))]}measure(e,t){let a=this.registry.get(t.language);if(!a)throw Error(`Unsupported language: ${t.language}`);let o=f(t)?i.measureWithNativeBackend(e,a,t.includeSyntaxTree??!1):void 0;if(o)return g(o,t.includeSyntaxTree??!1);let s=d(e,a),c=_(s,C(s,new Set(a.functionNodeTypes)).filter(e=>!se(e)&&oe(e)),a),l=c.functions,u=me(s,a,0,!1),{lines:p,codeLineNumbers:m}=jt(e,s),h=Pt(s,e);return{language:a.name,bytes:Buffer.byteLength(e),lines:p,functions:l,classCount:Pe(s,a),functionCount:l.length,cyclomaticComplexity:u.cyclomaticComplexity,maxCyclomaticComplexity:vn(l,`cyclomaticComplexity`),cognitiveComplexity:u.cognitiveComplexity,maxCognitiveComplexity:vn(l,`cognitiveComplexity`),nestingDepth:u.nestingDepth,ncssCount:r.countNcss(s,a),callGraph:c.callGraph,coupling:c.coupling,module:c.module,cohesion:c.cohesion,syntaxFeatures:c.syntaxFeatures,typeComplexity:c.typeComplexity,duplication:n.measureDuplication(s,m,t.duplication),halstead:h,maintainabilityIndex:_n(h.volume,u.cyclomaticComplexity,p.code),syntaxTree:t.includeSyntaxTree?s.toString():void 0}}collectDuplicationCandidates(e,t){let r=this.registry.get(t.language);if(!r)throw Error(`Unsupported language: ${t.language}`);return n.collectCrossFileDuplicateCandidates(d(e,r),t.duplication)}};function d(e,t){let n=new a.default;return n.setLanguage(t.parserLanguage),n.parse(e,void 0,{bufferSize:e.length+1}).rootNode}function f(e){let t=e.duplication;return(t?.minTokens??n.defaultDuplicationOptions.minTokens)===n.defaultDuplicationOptions.minTokens&&(t?.maxGapTokens??n.defaultDuplicationOptions.maxGapTokens)===n.defaultDuplicationOptions.maxGapTokens&&(t?.minSimilarityPercent??n.defaultDuplicationOptions.minSimilarityPercent)===n.defaultDuplicationOptions.minSimilarityPercent}const p=new u;function m(e,t){return p.measure(e,t)}function h(e,t){return p.collectDuplicationCandidates(e,t)}function g(e,t){let n=P(e.halsteadCounts);return{language:e.language,bytes:e.bytes,lines:e.lines,functions:e.functions.map(e=>({name:e.name,nodeType:e.nodeType,startLine:e.startLine,startColumn:e.startColumn,endLine:e.endLine,returnsJsx:e.returnsJsx,cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,nestingDepth:e.nestingDepth,ncss:e.ncss,callCount:e.callCount,uniqueCalleeCount:e.uniqueCalleeCount,fanIn:e.fanIn,fanOut:e.fanOut,parameterCount:e.parameterCount,recursive:e.recursive})),classCount:e.classCount,functionCount:e.functionCount,cyclomaticComplexity:e.cyclomaticComplexity,maxCyclomaticComplexity:e.maxCyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,maxCognitiveComplexity:e.maxCognitiveComplexity,nestingDepth:e.nestingDepth,ncssCount:e.ncssCount,callGraph:e.callGraph,coupling:e.coupling,module:e.module,cohesion:e.cohesion,syntaxFeatures:e.syntaxFeatures,typeComplexity:e.typeComplexity,duplication:e.duplication,halstead:n,maintainabilityIndex:_n(n.volume,e.cyclomaticComplexity,e.lines.code),syntaxTree:t?e.syntaxTree:void 0}}function _(e,t,n){let r=je(e,n),i=t.map((e,t)=>ee(e,n,t,r)),a=ne(i);return{functions:i.map(e=>({name:e.name,nodeType:e.nodeType,startLine:e.startLine,startColumn:e.startColumn,endLine:e.endLine,returnsJsx:e.returnsJsx,cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,nestingDepth:e.nestingDepth,ncss:e.ncss,callCount:e.callCount,uniqueCalleeCount:e.callees.size,fanIn:a.fanInByIndex.get(e.index)??0,fanOut:a.fanOutByIndex.get(e.index)??0,parameterCount:e.parameterCount,recursive:a.recursiveIndexes.has(e.index)})),callGraph:a.metrics,coupling:ft(e,n),module:Ue(e,n),cohesion:kt(i),syntaxFeatures:pt(e,n.name),typeComplexity:At(e)}}function ee(e,t,n,i){let a=me(e,t,0,!0),o=Ee(e,t,i);return{index:n,name:Ft(e),nodeType:e.type,hasImplementation:ae(e),startLine:e.startPosition.row+1,startColumn:e.startPosition.column,endLine:e.endPosition.row+1,returnsJsx:Ie(e,t),cyclomaticComplexity:a.cyclomaticComplexity,cognitiveComplexity:a.cognitiveComplexity,nestingDepth:a.nestingDepth,ncss:r.countFunctionNcss(e,t),callCount:o.callCount,parameterCount:te(e),callees:o.callees,identifiers:Ne(e)}}function te(e){if(e.childForFieldName(`parameter`))return 1;let t=y(e);if(!t)return 0;if(t.type===`identifier`)return 1;let n=new Set(J(t,`locals`).map(e=>e.id));return $(t.namedChildren.filter(e=>e.type!==`comment`&&e.type!==`self_parameter`&&e.type!==`receiver_parameter`&&e.type!==`positional_separator`&&e.type!==`keyword_separator`&&!n.has(e.id)&&!v(e)).map(e=>e.type===`parameter_declaration`?Math.max(1,J(e,`name`).length):1))+t.children.filter(e=>!e.isNamed&&e.text===`...`).length}function v(e){return e.type===`parameter_declaration`&&e.childForFieldName(`declarator`)===null&&e.childForFieldName(`type`)?.text===`void`}function y(e){let t=e.childForFieldName(`parameters`);if(t)return t;if(e.type===`compact_constructor_declaration`)return e.parent?.parent?.childForFieldName(`parameters`)??void 0;let n=e.childForFieldName(`declarator`);for(;n;){let e=n.childForFieldName(`parameters`);if(e)return e;n=I(n)}return e.namedChildren.find(e=>e.type===`formal_parameters`||e.type===`parameter_list`)}function ne(e){let t=re(e),n=new Set(t.keys()),r=new Map,i=new Map,a=new Map,o=0,s=0,c=new Set;for(let l of e){o+=l.callCount;for(let e of l.callees)c.add(e);let e=new Set([...l.callees].filter(e=>n.has(e))),u=new Set;for(let n of e){let e=t.get(n);e!==void 0&&u.add(e)}a.set(l.index,u),i.set(l.index,e.size),s+=e.size;for(let e of u)r.set(e,(r.get(e)??0)+1)}let l=fn(a);return{fanInByIndex:r,fanOutByIndex:i,recursiveIndexes:l,metrics:{callCount:o,uniqueCalleeCount:c.size,internalCallCount:s,internalEdgeCount:$([...a.values()].map(e=>e.size)),recursiveFunctionCount:l.size,maxFanIn:yn(r),maxFanOut:yn(i),maxCallDepth:mn(a)}}}function re(e){let t=new Map;for(let n of e)!n.name||!n.hasImplementation||t.set(n.name,t.has(n.name)?void 0:n.index);return new Map([...t.entries()].filter(e=>e[1]!==void 0))}const ie=new Set([`function_definition`,`constructor_declaration`,`compact_constructor_declaration`,`function_signature_item`]);function ae(e){return e.type!==`method_declaration`||e.childForFieldName(`body`)!==null}function oe(e){return!ie.has(e.type)||e.childForFieldName(`body`)!==null||e.namedChildren.some(e=>e.type===`try_statement`)}function se(e){return(e.type===`block`||e.type===`do_block`)&&e.parent?.type===`lambda`}function b(e,t){return t.has(e.type)&&!se(e)}const ce=new Set([`switch_statement`,`switch_expression`,`expression_switch_statement`,`type_switch_statement`,`select_statement`,`match_expression`,`match_statement`,`case`,`case_match`]),le=new Set([`case_clause`,`switch_case`,`switch_block_statement_group`,`switch_rule`,`case_statement`,`expression_case`,`type_case`,`communication_case`,`match_arm`,`when`,`in_clause`]),ue=new Set([`if_statement`,`if_expression`,`if`,`unless`]),de=new Set([`throw_statement`]),x=new WeakMap;function fe(e){x.delete(e)}function pe(e){let t=x.get(e);return t||(t={functionNodes:new Set(e.functionNodeTypes),decisionNodes:new Set(e.decisionNodeTypes),nestingNodes:new Set(e.nestingNodeTypes)},x.set(e,t)),t}function me(e,t,n,r){let i=1,a=0,o=n,{functionNodes:s,decisionNodes:c,nestingNodes:l}=pe(t);function u(e,t,n,d,f,p){let m=e.type===`class_body`&&d;m&&(f=!0,n+=1),b(e,s)&&(d&&(f=!0,p||(n+=1)),d=!0);let h=t+n,g=!(r&&f),_=e.isNamed&&c.has(e.type)&&!Ce(e),ee=e.isNamed&&le.has(e.type),te=e.isNamed&&(l.has(e.type)||e.type===`else`&&(e.parent?.type===`case`||e.parent?.type===`case_match`)),v=_&&Se(e);_&&g&&(i+=1),_&&!ee&&!de.has(e.type)&&(a+=v?1:1+h),e.isNamed&&ce.has(e.type)&&(a+=1+h),a+=he(e),ge(e)&&(a+=1),Te(e)&&(g&&(i+=1),ve(e)&&(a+=1)),xe(e)&&(g&&(i+=1),a+=1);let y=te&&!v?t+1:t;g&&(o=Math.max(o,y));for(let t of e.children)u(t,y,n,d,f,m)}for(let t of e.children)u(t,n,0,r,!1,!1);return{cyclomaticComplexity:i,cognitiveComplexity:a,nestingDepth:o}}function he(e){if(!e.isNamed)return 0;if(e.type===`else`)return e.parent?.type===`case`||e.parent?.type===`case_match`?0:1;if(e.type===`else_clause`)return+!e.namedChildren.some(e=>ue.has(e.type));if(e.type!==`if_statement`&&e.type!==`if_expression`)return 0;let t=0;for(let n=0;n<e.childCount;n+=1){let r=e.child(n);r&&e.fieldNameForChild(n)===`alternative`&&r.type!==`else_clause`&&r.type!==`elif_clause`&&!ue.has(r.type)&&(t+=1)}return t}function ge(e){return e.isNamed?e.type===`goto_statement`?!0:e.type===`break_expression`||e.type===`continue_expression`?e.namedChildren.some(e=>e.type===`label`||e.type===`loop_label`):(e.type===`break_statement`||e.type===`continue_statement`)&&e.namedChildren.some(e=>!r.commentNodeTypes.has(e.type)):!1}const _e=new Set([`parenthesized_expression`,`parenthesized_statements`]);function ve(e){let t=e.parent;if(!t)return!0;let n=t.parent;for(;n&&_e.has(n.type);)n=n.parent;return!n||n.type!==t.type||ye(be(n))!==ye(e.text)}function ye(e){return e===`and`?`&&`:e===`or`?`||`:e}function be(e){let t=e.childForFieldName(`operator`);return t?t.text:e.children.find(e=>!e.isNamed&&o.has(e.text))?.text}function xe(e){return e.isNamed?e.type===`guard`||e.type===`if_guard`||e.type===`unless_guard`||e.type===`if_clause`||e.type===`match_pattern`&&e.children.some(e=>!e.isNamed&&e.type===`if`):!1}function Se(e){if(e.type===`elsif`||e.type===`elif_clause`)return!0;if(e.type!==`if_statement`&&e.type!==`if_expression`&&e.type!==`if`)return!1;let t=e.parent;return t?t.type===`else_clause`||t.childForFieldName(`alternative`)?.id===e.id:!1}function Ce(e){if(e.type===`case_statement`)return e.childForFieldName(`value`)===null;if(e.type===`switch_block_statement_group`||e.type===`switch_rule`){let t=e.namedChildren.find(e=>e.type===`switch_label`);return t!==void 0&&t.namedChildCount===0}if(e.type===`case_clause`||e.type===`match_arm`){let t=e.namedChildren.find(e=>e.type===`case_pattern`||e.type===`match_pattern`);if(!t)return!1;if(t.child(0)?.type===`_`&&(t.childCount===1||t.child(1)?.type===`if`))return!0;let n=t.namedChildCount===1?t.namedChild(0):void 0;return e.type===`case_clause`&&n?.type===`dotted_name`&&n.namedChildCount===1&&n.namedChild(0)?.type===`identifier`}return e.type===`in_clause`&&e.namedChild(0)?.type===`identifier`}const we=new Set([`binary_expression`,`binary`,`boolean_operator`]);function Te(e){if(e.isNamed||!o.has(e.text))return!1;let t=e.parent;return t!==null&&we.has(t.type)}function Ee(e,t,n=new Set){let r=new Set,i=pe(t).functionNodes,a=0;function o(e,s){if(!(!s&&b(e,i))){if(!(t.name===`cpp`&&Oe(e)))if(L(e)){a+=1;let i=t.name===`cpp`&&(e.type===`new_expression`||e.type===`call_expression`&&n.has(S(e.childForFieldName(`function`))??``))?void 0:Gt(e);if(i&&r.add(i),t.name===`ruby`&&e.type===`call`&&e.parent?.type===`operator_assignment`&&e.parent.childForFieldName(`left`)?.id===e.id){a+=1;let t=e.childForFieldName(`method`);t&&r.add(`${t.text}=`)}}else(Me(e,t)||ke(e,n))&&(a+=1);for(let t of e.namedChildren)o(t,!1)}}return o(e,!0),{callCount:a,callees:r}}const De=new Set([`static_cast`,`dynamic_cast`,`const_cast`,`reinterpret_cast`]);function Oe(e){if(e.type!==`call_expression`)return!1;let t=e.childForFieldName(`function`);if(t?.type===`primitive_type`)return!0;let n=t?.type===`template_function`?t.childForFieldName(`name`)?.text:t?.text;return n!==void 0&&De.has(n)}function ke(e,t){if(t.size===0)return!1;if(e.type===`compound_literal_expression`)return t.has(S(e.childForFieldName(`type`))??``);if(e.type===`init_declarator`){let n=e.childForFieldName(`value`);return n?.type!==`argument_list`&&n?.type!==`initializer_list`?!1:t.has(S(e.parent?.childForFieldName(`type`))??``)}if((e.type===`identifier`||e.type===`array_declarator`)&&e.parent?.type===`declaration`&&J(e.parent,`declarator`).some(t=>t.id===e.id)&&!k(e.parent,`extern`)){let n=e;for(;n?.type===`array_declarator`;)n=n.childForFieldName(`declarator`);return n?.type===`identifier`&&t.has(S(e.parent.childForFieldName(`type`))??``)}if(e.type===`field_initializer`){let n=e.namedChild(0),r=n?.type===`field_identifier`?n.text:S(n);return t.has(r??``)}return!1}function S(e){let t=e;for(;t;){if(t.type===`type_identifier`||t.type===`identifier`)return t.text;if(t.type===`qualified_identifier`||t.type===`scoped_identifier`||t.type===`template_type`||t.type===`template_function`){t=t.childForFieldName(`name`);continue}return}}const Ae=new Set([`class_specifier`,`struct_specifier`,`union_specifier`]);function je(e,t){let n=new Set;if(t.name!==`cpp`)return n;for(let t of C(e,Ae)){let e=t.childForFieldName(`name`)?.text;e&&t.childForFieldName(`body`)&&n.add(e)}return n}function Me(e,t){return t.name===`ruby`?e.type===`yield`||e.type===`super`&&e.parent?.type!==`call`:!1}function Ne(e){let t=new Set;function n(e){(e.type===`identifier`||e.type===`property_identifier`||e.type===`field_identifier`||e.type===`constant`||e.type===`instance_variable`||e.type===`class_variable`||e.type===`global_variable`)&&t.add(e.text);for(let t of e.namedChildren)n(t)}return n(e),t}function Pe(e,t){return C(e,new Set(t.classNodeTypes)).filter(Fe).length}function Fe(e){return e.type===`object_creation_expression`||e.type===`enum_constant`?e.namedChildren.some(e=>e.type===`class_body`):!e.type.endsWith(`_specifier`)||e.childForFieldName(`body`)!==null}function C(e,t){let n=[];function r(e){t.has(e.type)&&n.push(e);for(let t of e.namedChildren)r(t)}return r(e),n}function Ie(e,t){let n=new Set(t.functionNodeTypes);function r(t,i){if(!i&&n.has(t.type))return!1;if(t.type===`return_statement`||e.type===`arrow_function`&&t.id===w(e)?.id&&t.type!==`statement_block`&&!n.has(t.type))return T(t,n)||E(t,n);for(let e of t.namedChildren)if(r(e,!1))return!0;return!1}return r(e,!0)}function w(e){return e.childForFieldName(`body`)??e.namedChild(e.namedChildCount-1)??void 0}function T(e,t){return Le(e,t,e=>e.type.startsWith(`jsx_`)||Re(e,t))}function E(e,t){return Le(e,t,Yt)}function Le(e,t,n){function r(e,i){if(!i&&t.has(e.type))return!1;if(n(e))return!0;for(let t of e.namedChildren)if(r(t,!1))return!0;return!1}return r(e,!0)}function Re(e,t){return!L(e)||!ze(e.childForFieldName(`function`)??e.namedChild(0))?!1:e.namedChildren.some(e=>Be(e,t))}function ze(e){if(!e)return!1;let t=R(e);return t===`map`||t===`flatMap`}function Be(e,t){return t.has(e.type)?Ve(e,t):e.namedChildren.some(e=>Be(e,t))}function Ve(e,t){let n=e.type===`arrow_function`?w(e):void 0;return n&&n.type!==`statement_block`&&!t.has(n.type)?T(n,t)||E(n,t):He(e,t,e=>T(e,t)||E(e,t))}function He(e,t,n){function r(e,i){if(!i&&t.has(e.type))return!1;if(e.type===`return_statement`&&n(e))return!0;for(let t of e.namedChildren)if(r(t,!1))return!0;return!1}return r(e,!0)}function Ue(e,t){let n=new Set;function r(e){if(Zt(e,t))for(let r of $t(e,t,{expandPythonSubmodules:!0}))n.add(r);for(let t of e.namedChildren)r(t)}return r(e),{declarations:We(e,t),importSources:[...n]}}function We(e,t){let n=st(e),r=t.name===`java`?Ge(e):``;return e.namedChildren.flatMap(e=>D(e,!1,r,t.name===`cpp`)).map(e=>n.has(e.name)?{...e,exported:!0}:e)}function Ge(e){let t=e.namedChildren.find(e=>e.type===`package_declaration`)?.namedChildren.find(e=>e.type===`scoped_identifier`||e.type===`identifier`);return t?`${t.text}::`:``}const Ke=new Set([`module`,`class`,`singleton_class`]);function D(e,t,n=``,r=!1){if(it(e))return e.namedChildren.flatMap(e=>D(e,!0,n,r));if(e.type===`namespace_definition`){let i=e.childForFieldName(`name`)?.text;return i?(e.childForFieldName(`body`)?.namedChildren??[]).flatMap(e=>D(e,t,`${n}${i}::`,r)):[]}return at(e)?e.namedChildren.flatMap(e=>D(e,t,n,r)):e.type===`declaration`?O(et(e,t,r),n):e.type===`type_definition`?O(Ye(e,t),n):Ke.has(e.type)?qe(e,t,n):e.type===`assignment`||e.type===`operator_assignment`?O(Je(e,t),n,!0):O(A(e,t),n)}function O(e,t,n=!1){return t?e.map(e=>n&&e.name.includes(`::`)?e:{...e,name:`${t}${e.name}`}):e}function qe(e,t,n=``){let r=O(A(e,t),n,!0),i=r[0]?`${r[0].name}::`:n,a=e.childForFieldName(`body`);for(let e of a?.namedChildren??[])Ke.has(e.type)?r.push(...qe(e,t,i)):(e.type===`assignment`||e.type===`operator_assignment`)&&r.push(...O(Je(e,t),i,!0));return r}function Je(e,t){if(e.type===`operator_assignment`&&!e.children.some(e=>!e.isNamed&&e.text===`||=`))return[];let n=e.childForFieldName(`left`);return n?(n.type===`left_assignment_list`?n.namedChildren:[n]).filter(e=>e.type===`constant`||e.type===`scope_resolution`&&e.childForFieldName(`name`)?.type===`constant`).map(e=>({exported:t,name:e.text,startLine:e.startPosition.row+1})):[]}function Ye(e,t){let n=e.childForFieldName(`type`),r=n?A(n,t):[],i=n?.type.endsWith(`_specifier`)&&!n.childForFieldName(`body`)?n.childForFieldName(`name`)?.text:void 0,a=new Set(r.map(e=>e.name));for(let n of J(e,`declarator`)){let e=n.type===`type_identifier`?n.text:F(n);e&&e!==i&&!a.has(e)&&(a.add(e),r.push({exported:t,name:e,startLine:n.startPosition.row+1}))}return r}const Xe=new Set([`init_declarator`,`pointer_declarator`,`array_declarator`,`reference_declarator`,`identifier`,`field_identifier`]);function Ze(e){if(e.type===`pointer_declarator`||e.type===`reference_declarator`){let t=e;for(;t&&(t.type===`pointer_declarator`||t.type===`reference_declarator`||t.type===`array_declarator`);)t=I(t);return t?.type!==`function_declarator`||t.childForFieldName(`declarator`)?.type===`parenthesized_declarator`}return Xe.has(e.type)?!0:e.type===`function_declarator`&&e.childForFieldName(`declarator`)?.type===`parenthesized_declarator`}function k(e,t){return e.children.some(e=>e.type===`storage_class_specifier`&&e.text===t)}function Qe(e){let t=e.childForFieldName(`type`);return t?.type!==`type_identifier`||t.text!==`import`&&t.text!==`export`&&t.text!==`module`?!1:!$e(e,t.text)}function $e(e,t){let n=e;for(;n.parent;)n=n.parent;return C(n,new Set([`type_definition`,`alias_declaration`])).some(e=>(e.childForFieldName(`declarator`)??e.childForFieldName(`name`))?.text===t)}function et(e,t,n=!1){if(n&&Qe(e)||k(e,`static`))return[];let r=e.childForFieldName(`type`),i=r?A(r,t):[],a=new Set(i.map(e=>e.name)),o=k(e,`extern`);for(let r of e.namedChildren.filter(Ze)){if(o&&r.type!==`init_declarator`||n&&!o&&!k(e,`inline`)&&!tt(r)&&!M(e,r))continue;let s=F(r);s&&!a.has(s)&&(a.add(s),i.push({exported:t,name:s,startLine:r.startPosition.row+1}))}return i}function tt(e){let t=e.type===`init_declarator`?e.childForFieldName(`declarator`)??e:e;for(;t;){if(t.type===`reference_declarator`)return!0;t=I(t)}return!1}function A(e,t){if(!ot(e)||e.type.endsWith(`_specifier`)&&!e.childForFieldName(`body`)||k(e,`static`))return[];if(e.type===`enum_specifier`)return nt(e,t);let n=rt(e);return n?[{exported:t,name:n,startLine:e.startPosition.row+1}]:[]}function nt(e,t){let n=[],r=e.childForFieldName(`name`)?.text;r&&n.push({exported:t,name:r,startLine:e.startPosition.row+1});let i=e.children.some(e=>!e.isNamed&&(e.text===`class`||e.text===`struct`));for(let a of e.childForFieldName(`body`)?.namedChildren??[]){if(a.type!==`enumerator`)continue;let e=a.childForFieldName(`name`)?.text;e&&n.push({exported:t,name:i&&r?`${r}::${e}`:e,startLine:a.startPosition.row+1})}return n}function rt(e){if(e.type===`method_declaration`&&e.childForFieldName(`receiver`))return ut(e);let t=e.childForFieldName(`name`);return t?.type===`template_type`&&(t=t.childForFieldName(`name`)),t?.type===`scope_resolution`?t.text:t?j(t)?t.text:void 0:F(e.childForFieldName(`declarator`),!0)||e.namedChildren.find(j)?.text}function it(e){return e.type===`export_statement`||e.type===`export_declaration`}function at(e){return e.type===`lexical_declaration`||e.type===`variable_declaration`||e.type===`decorated_definition`||e.type===`type_declaration`||e.type===`const_declaration`||e.type===`var_declaration`||e.type===`var_spec_list`||e.type===`linkage_specification`||e.type===`template_declaration`||e.type===`declaration_list`||e.type===`preproc_ifdef`||e.type===`preproc_if`||e.type===`preproc_else`||e.type===`preproc_elif`}function ot(e){return e.type===`function_declaration`||e.type===`function_definition`||e.type===`function_item`||e.type===`method_declaration`||e.type===`class_declaration`||e.type===`class_definition`||e.type===`interface_declaration`||e.type===`type_alias_declaration`||e.type===`type_declaration`||e.type===`type_spec`||e.type===`const_spec`||e.type===`var_spec`||e.type===`variable_declarator`||e.type===`struct_item`||e.type===`enum_item`||e.type===`union_item`||e.type===`trait_item`||e.type===`type_item`||e.type===`const_item`||e.type===`static_item`||e.type===`mod_item`||e.type===`enum_declaration`||e.type===`record_declaration`||e.type===`annotation_type_declaration`||e.type===`method`||e.type===`singleton_method`||e.type===`class`||e.type===`module`||e.type===`alias_declaration`||e.type===`struct_specifier`||e.type===`class_specifier`||e.type===`enum_specifier`||e.type===`union_specifier`}function j(e){return e.type===`identifier`||e.type===`type_identifier`||e.type===`property_identifier`||e.type===`field_identifier`||e.type===`constant`}function st(e){let t=new Set;function n(e,r){if(!r&&ct(e)){let n=lt(e);n&&t.add(n)}let i=r||it(e)&&e.childForFieldName(`source`)!==null;for(let t of e.namedChildren)n(t,i)}return n(e,!1),t}function ct(e){return e.type===`export_specifier`||e.type===`namespace_export`}function lt(e){let t=e.childForFieldName(`name`)??e.childForFieldName(`alias`)??e.namedChildren.find(j);return t&&j(t)?t.text:void 0}function ut(e){let t=e.childForFieldName(`name`),n=e.childForFieldName(`receiver`)?.namedChildren[0]?.childForFieldName(`type`);return!t||!j(t)||!n?t&&j(t)?t.text:void 0:`${dt(n.text)}.${t.text}`}function dt(e){return e.replaceAll(/\s+/gu,``).replace(/^\*+/u,``)}function ft(e,t){let n=new Set,r=0,i=0;function a(e){if((t.name!==`go`||e.type!==`import_declaration`&&e.type!==`import_spec_list`)&&(Xt(e)||Qt(e,t)||z(e,t)||B(e)||V(e,t))&&(r+=1),Zt(e,t))for(let r of $t(e,t,{expandPythonSubmodules:!1}))n.add(r);dn(e)&&(i+=1);for(let t of e.namedChildren)a(t)}a(e);let o=[...n].filter(e=>on(e,t.name)).length;return{importCount:r,importSourceCount:n.size,relativeImportCount:o,externalImportCount:n.size-o,exportCount:i}}function pt(e,t){let n={assignmentCount:0,awaitExpressionCount:0,loopStatementCount:0,mutableBindingCount:0,returnStatementCount:0,throwStatementCount:0,tryStatementCount:0};function r(e){mt(e)&&(n.assignmentCount+=1),ht(e)&&(n.awaitExpressionCount+=1),gt(e)&&(n.loopStatementCount+=1),n.mutableBindingCount+=_t(e,t),wt(e)&&(n.returnStatementCount+=1),Tt(e)&&(n.throwStatementCount+=1),Dt(e)&&(n.tryStatementCount+=1);for(let t of e.namedChildren)r(t)}return r(e),n}function mt(e){return e.type===`assignment_expression`||e.type===`augmented_assignment_expression`||e.type===`assignment_statement`||e.type===`assignment`||e.type===`augmented_assignment`||e.type===`operator_assignment`||e.type===`short_var_declaration`||e.type===`compound_assignment_expr`||e.type===`named_expression`||e.type===`update_expression`||e.type===`inc_statement`||e.type===`dec_statement`}function ht(e){return e.type===`await_expression`||e.type===`await`||e.type===`co_await_expression`}function gt(e){return e.type===`for_statement`||e.type===`for_in_statement`||e.type===`enhanced_for_statement`||e.type===`for_range_loop`||e.type===`while_statement`||e.type===`do_statement`||e.type===`for_expression`||e.type===`while_expression`||e.type===`loop_expression`||e.type===`while`||e.type===`until`||e.type===`for`||e.type===`while_modifier`||e.type===`until_modifier`}function _t(e,t){let n=t===`c`||t===`cpp`;if(e.type===`local_variable_declaration`||e.type===`field_declaration`&&t===`java`){let t=e.namedChildren.filter(e=>e.type===`variable_declarator`);return xt(e)?t.length:0}if(n&&(e.type===`declaration`||e.type===`field_declaration`))return vt(e,t===`cpp`);if(n&&e.type===`function_definition`){let n=e.childForFieldName(`declarator`);return n?.type===`field_identifier`||n?.type===`identifier`?vt(e,t===`cpp`):0}if(e.type===`enhanced_for_statement`)return+!!xt(e);if(t===`java`&&(e.type===`instanceof_expression`||e.type===`type_pattern`||e.type===`record_pattern_component`)){let t=e.type===`instanceof_expression`?e.childForFieldName(`name`)!==null:e.namedChildren.some(e=>e.type===`identifier`),n=e.children.some(e=>!e.isNamed&&e.text===`final`);return t&&!n?1:0}if(n&&e.type===`for_range_loop`){let t=e.childForFieldName(`declarator`);return t&&M(e,t)?yt(t):0}return+!!bt(e)}function vt(e,t){return t&&Qe(e)?0:$(e.namedChildren.filter(t=>Ze(t)&&M(e,t)).map(yt))}function yt(e){let t=e.type===`init_declarator`?e.childForFieldName(`declarator`)??e:e;return t.type===`structured_binding_declarator`?Math.max(1,t.namedChildren.filter(e=>e.type===`identifier`).length):1}function bt(e){return e.type===`lexical_declaration`&&e.firstChild?.text===`let`||e.type===`variable_declaration`&&e.firstChild?.text===`var`||e.type===`var_declaration`||e.type===`let_declaration`&&Ct(e)}function xt(e){return!e.namedChildren.find(e=>e.type===`modifiers`)?.children.some(e=>e.text===`final`)}function M(e,t){let n=t.type===`init_declarator`?t.childForFieldName(`declarator`)??t:t,r=!1;for(;n.type===`reference_declarator`||n.type===`pointer_declarator`||n.type===`array_declarator`||n.type===`parenthesized_declarator`||n.type===`function_declarator`;){if(n.type===`reference_declarator`)return!1;let e=I(n);if(!e)break;if(n.type===`pointer_declarator`&&(r=!0,N(n)&&!St(e)))return!1;n=e}return r||!N(e)}function St(e){let t=e;for(;t;){if(t.type===`pointer_declarator`)return!0;t=I(t)}return!1}function N(e){return e.namedChildren.some(e=>e.type===`type_qualifier`&&(e.text===`const`||e.text===`constexpr`))}function Ct(e){if(e.children.some(e=>e.type===`mutable_specifier`))return!0;let t=e.childForFieldName(`pattern`);return t?t.descendantsOfType(`mutable_specifier`).some(e=>e.parent?.type!==`reference_pattern`):!1}function wt(e){return e.type===`return_statement`||e.type===`return_expression`||e.type===`return`||e.type===`co_return_statement`}function Tt(e){return e.type===`throw_statement`||e.type===`raise_statement`||Et(e)}function Et(e){if(e.type!==`call`||e.childForFieldName(`receiver`))return!1;let t=e.childForFieldName(`method`);return t?.type===`identifier`&&(t.text===`raise`||t.text===`fail`)}function Dt(e){return e.type===`try_statement`||e.type===`try_with_resources_statement`||e.type===`rescue_modifier`||Ot(e)}function Ot(e){return(e.type===`begin`||e.type===`body_statement`)&&e.namedChildren.some(e=>e.type===`rescue`||e.type===`ensure`)}function kt(e){let t=new Map;for(let n of e)for(let e of n.identifiers)t.set(e,(t.get(e)??0)+1);let n=0;for(let e of t.values())e>=2&&(n+=1);let r=e.length,i=r*(r-1)/2,a=Math.max(1,Math.ceil(i/25e4)),o=0,s=0,c=0,l=0,u=r-1;for(let t=0;t<i;t+=a){for(;t>=l+u;)l+=u,c+=1,u=r-1-c;let n=c+1+(t-l),i=e[c],a=e[n];if(!i||!a)continue;let d=gn(i.identifiers,a.identifiers),f=i.identifiers.size+a.identifiers.size-d;o+=f===0?0:d/f,s+=1}return{averageFunctionIdentifierOverlap:s===0?1:o/s,sharedIdentifierCount:n,uniqueIdentifierCount:t.size}}function At(e){let t={typeAnnotationCount:0,typeAliasCount:0,interfaceCount:0,genericParameterCount:0,unionTypeCount:0,intersectionTypeCount:0,conditionalTypeCount:0,typeAssertionCount:0,nonNullAssertionCount:0,satisfiesExpressionCount:0};function n(e){switch(e.type){case`type_annotation`:t.typeAnnotationCount+=1;break;case`type_alias_declaration`:t.typeAliasCount+=1;break;case`interface_declaration`:t.interfaceCount+=1;break;case`type_parameters`:case`type_parameter`:t.genericParameterCount+=+(e.type===`type_parameter`);break;case`union_type`:t.unionTypeCount+=1;break;case`intersection_type`:t.intersectionTypeCount+=1;break;case`conditional_type`:t.conditionalTypeCount+=1;break;case`as_expression`:case`type_assertion`:t.typeAssertionCount+=1;break;case`non_null_expression`:t.nonNullAssertionCount+=1;break;case`satisfies_expression`:t.satisfiesExpressionCount+=1}for(let t of e.namedChildren)n(t)}return n(e),t}function jt(e,t){let n=e.length===0?[]:e.split(/\r\n|\n|\r/),r=new Map;for(let e of Mt(t)){let t=r.get(e.line)??[];t.push(e),r.set(e.line,t)}let i=0,a=0,o=new Set;for(let[e,t]of n.entries()){if(t.trim()===``){i+=1;continue}Nt(t,r.get(e)??[])?a+=1:o.add(e+1)}return{lines:{total:n.length,code:o.size,comment:a,blank:i},codeLineNumbers:o}}function Mt(e){let t=[];function n(e){if(e.type===`comment`||e.type===`line_comment`||e.type===`block_comment`)for(let n=e.startPosition.row;n<=e.endPosition.row;n+=1)t.push({line:n,startColumn:n===e.startPosition.row?e.startPosition.column:0,endColumn:n===e.endPosition.row?e.endPosition.column:1/0});for(let t of e.namedChildren)n(t)}return n(e),t}function Nt(e,t){if(t.length===0)return!1;for(let n=0;n<e.length;n+=1)if(!/\s/u.test(e[n]??` `)&&!t.some(e=>e.startColumn<=n&&n<e.endColumn))return!1;return!0}function Pt(e,t){let n=new Map,r=new Map;function i(e){if(e.type!==`comment`&&e.type!==`line_comment`&&e.type!==`block_comment`){if(l.has(e.type)){Q(r,t.slice(e.startIndex,e.endIndex));return}if(e.childCount===0){let i=t.slice(e.startIndex,e.endIndex);c.has(e.type)?Q(r,i):(s.has(i)||s.has(e.type))&&Jt(e,i)&&Q(n,i||e.type);return}for(let t of e.children)i(t)}}return i(e),P({distinctOperators:n.size,distinctOperands:r.size,totalOperators:$(n.values()),totalOperands:$(r.values())})}function P(e){let{distinctOperators:t,distinctOperands:n,totalOperators:r,totalOperands:i}=e,a=t+n,o=r+i,s=a===0?0:o*Math.log2(a),c=n===0?0:t/2*(i/n),l=c*s;return{distinctOperators:t,distinctOperands:n,totalOperators:r,totalOperands:i,vocabulary:a,length:o,volume:s,difficulty:c,effort:l,time:l/18,bugs:s/3e3}}function Ft(e){let t=Vt(e);if(t)return t;let n=e.childForFieldName(`name`);if(n)return n.text;let r=It(e);if(r)return r;let i=e.parent;if(i){if(e.type===`closure_expression`&&i.type===`let_declaration`){let e=i.childForFieldName(`pattern`);return e?.type===`identifier`?e.text:void 0}return e.type===`lambda_expression`&&i.type===`init_declarator`?F(i.childForFieldName(`declarator`)):e.type===`func_literal`&&i.type===`expression_list`?zt(e,i):e.type===`lambda`&&i.type===`assignment`?Lt(i):(e.type===`block`||e.type===`do_block`)&&Rt(i)?i.parent?.type===`assignment`?Lt(i.parent):void 0:i.childForFieldName(`name`)?.text}}function It(e){return F(e.childForFieldName(`declarator`))}function F(e,t=!1){let n=e,r=``;for(;n;)switch(n.type){case`identifier`:case`field_identifier`:case`type_identifier`:case`destructor_name`:case`operator_name`:return r?`${r}::${n.text}`:n.text;case`operator_cast`:{let e=`operator ${n.childForFieldName(`type`)?.text??``}`.trimEnd();return r?`${r}::${e}`:e}case`template_function`:n=n.childForFieldName(`name`);break;case`qualified_identifier`:if(t){let e=n.childForFieldName(`scope`)?.text.replaceAll(/\s+/gu,``);e&&(r=r?`${r}::${e}`:e)}n=n.childForFieldName(`name`);break;default:n=I(n)}}function I(e){let t=e.childForFieldName(`declarator`);if(t)return t;if(e.type===`reference_declarator`||e.type===`parenthesized_declarator`)return e.namedChild(0)??void 0}function Lt(e){let t=e.childForFieldName(`left`);return t?.type===`identifier`||t?.type===`constant`?t.text:void 0}function Rt(e){if(e.type!==`call`||e.childForFieldName(`receiver`))return!1;let t=e.childForFieldName(`method`);return t?.type===`identifier`&&(t.text===`lambda`||t.text===`proc`)}function zt(e,t){let n=t.parent,r=t.namedChildren.filter(e=>e.type!==`comment`).findIndex(t=>t.id===e.id);if(!(!n||r===-1)){if(n.type===`short_var_declaration`){let e=n.childForFieldName(`left`)?.namedChildren.filter(e=>e.type!==`comment`);return Bt(e?.[r])}if(n.type===`var_spec`){let e=J(n,`name`)[r];return Bt(e)}}}function Bt(e){return e?.type===`identifier`&&e.text!==`_`?e.text:void 0}function Vt(e){let t=e;for(;t;){let e=t.parent,n=e?.parent;if(e?.type!==`arguments`||n?.type!==`call_expression`||!Ht(n))return;let r=n.parent;if(r?.type===`variable_declarator`)return r.childForFieldName(`name`)?.text;t=n}}function Ht(e){let t=e.childForFieldName(`function`)??e.namedChild(0);return t?.text===`memo`||t?.text===`React.memo`||t?.text===`forwardRef`||t?.text===`React.forwardRef`}function L(e){return e.type===`call_expression`||e.type===`call`||e.type===`method_invocation`||e.type===`macro_invocation`||e.type===`new_expression`||e.type===`object_creation_expression`||e.type===`explicit_constructor_invocation`}function Ut(e){for(let t of e.children)if(t.type===`ERROR`){let e=t.children.find(e=>e.type===`operator_name`);if(e)return e.text}let t=e.childForFieldName(`function`);if(t?.type===`field_expression`){let e=t.children.findIndex(e=>e.type===`ERROR`&&e.text===`operator`),n=e===-1?void 0:t.children[e+1];if(n?.type===`field_identifier`||n?.type===`primitive_type`)return`operator ${n.text}`}}const Wt=new Set([`arrow_function`,`function_expression`,`function`,`lambda`,`lambda_expression`,`closure_expression`,`func_literal`,`anonymous_function`]);function Gt(e){if(e.type===`call`){let t=e.childForFieldName(`method`),n=e.childForFieldName(`receiver`);if(t?.text===`call`&&n?.type===`identifier`)return n.text;if(t&&e.parent?.type===`assignment`&&e.parent.childForFieldName(`left`)?.id===e.id)return`${t.text}=`;if(t?.type===`operator`)return t.text}if(e.type===`call_expression`){let t=Ut(e);if(t)return t}let t=e.childForFieldName(`function`)??e.childForFieldName(`name`)??e.childForFieldName(`method`)??e.childForFieldName(`constructor`)??e.childForFieldName(`type`)??e.namedChild(0);if(!t)return;let n=Kt(t);if(!Wt.has(n.type))return R(n)}function Kt(e){let t=e;for(;t.type===`parenthesized_expression`&&t.namedChildCount===1;){let e=t.namedChild(0);if(!e)break;t=e}return t}const qt=new Set([`ternary_expression`,`conditional_expression`,`conditional`,`try_expression`,`conditional_type`]);function Jt(e,t){if(t===`@`){let t=e.parent?.type;return t===`binary_operator`||t===`augmented_assignment`}if(t!==`?`)return!0;let n=e.parent?.type;return n!==void 0&&qt.has(n)}function R(e){if(e.type===`generic_function`||e.type===`template_function`||e.type===`template_method`){let t=e.childForFieldName(`function`)??e.childForFieldName(`name`);if(t)return R(t)}if(e.type===`destructor_name`)return e.text;if(e.type===`generic_type`){let t=e.namedChildren.find(e=>e.type===`type_identifier`||e.type===`scoped_type_identifier`);if(t)return R(t)}if(e.type===`identifier`||e.type===`property_identifier`||e.type===`field_identifier`||e.type===`type_identifier`||e.type===`attribute`)return e.text;for(let t=e.namedChildCount-1;t>=0;--t){let n=e.namedChild(t);if(!n)continue;let r=R(n);if(r)return r}}function Yt(e){if(!L(e))return!1;let t=e.childForFieldName(`function`)??e.namedChild(0);return t?.text===`React.createElement`||t?.text===`createElement`}function Xt(e){return e.type===`import_statement`||e.type===`import_declaration`||e.type===`import_from_statement`||e.type===`import_spec`||e.type===`import_spec_list`||e.type===`use_declaration`||e.type===`extern_crate_declaration`||e.type===`requires_module_directive`||e.type===`preproc_include`}function Zt(e,t){return Xt(e)||Qt(e,t)||z(e,t)||B(e)||V(e,t)||dn(e)&&e.childForFieldName(`source`)!==null}function z(e,t){if(t.name!==`cpp`)return!1;if(e.type===`declaration`){let t=e.childForFieldName(`type`);return t?.type===`type_identifier`?t.text===`import`?!$e(e,`import`):t.text===`export`&&/^export\s+import\b/u.test(e.text):!1}return e.type===`labeled_statement`||e.type===`expression_statement`?e.parent?.type===`translation_unit`&&/^import\s+[:"<]/u.test(e.text):!1}function Qt(e,t){return t.name===`rust`&&e.type===`mod_item`&&!e.childForFieldName(`body`)}function B(e){return L(e)?(e.childForFieldName(`function`)??e.namedChild(0))?.text===`import`:!1}function $t(e,t,n){if(t.name===`python`){let t=cn(e,n);if(t.length>0)return t}if(t.name===`rust`)return sn(e);if(t.name===`java`&&e.type===`requires_module_directive`){let t=e.childForFieldName(`module`);return t?[Y(t.text)]:[]}if(t.name===`java`&&e.type===`import_declaration`){let t=e.namedChild(0);if(!t)return[];let n=e.children.some(e=>e.type===`static`),r=e.namedChildren.some(e=>e.type===`asterisk`),i=Y(t.text);return[r&&!n?`${i}.*`:i]}if(z(e,t)){let t=/^(?:export\s+)?import\s+([\w.:]+|"[^"]+"|<[^>]+>)/u.exec(e.text)?.[1];return t?t.startsWith(`"`)?[`./${Z(t)}`]:[t]:[]}if(V(e,t))return tn(e);if(e.type===`preproc_include`){let t=e.childForFieldName(`path`);if(!t)return[];let n=Z(t.text);return[t.type===`string_literal`&&!n.startsWith(`.`)&&!n.startsWith(`/`)?`./${n}`:n]}if(B(e))return an(e);let r=e.childForFieldName(`source`)??un(e);return r?[Z(r.text)]:[]}const en=new Set([`require`,`require_relative`,`load`]);function V(e,t){if(t.name!==`ruby`||e.type!==`call`)return!1;let n=e.childForFieldName(`method`);if(n?.type!==`identifier`)return!1;if(n.text===`autoload`){let t=e.childForFieldName(`receiver`);return t===null||t.type===`constant`||t.type===`scope_resolution`}return e.childForFieldName(`receiver`)===null&&en.has(n.text)}function tn(e){let t=e.childForFieldName(`arguments`),n=e.childForFieldName(`method`)?.text===`autoload`,r=t?.namedChild(+!!n);if(!r||r.type!==`string`||r.namedChildren.some(e=>e.type===`interpolation`))return[];let i=r.namedChildren.filter(e=>e.type===`string_content`||e.type===`escape_sequence`),a=i.length>0?i.map(e=>e.type===`escape_sequence`?rn(e.text):e.text).join(``):Z(r.text);return e.childForFieldName(`method`)?.text===`require_relative`?[a.startsWith(`.`)?a:`./${a}`]:[a.replace(/^(?:\.\.?\/)+/u,``)]}const nn=new Map([[`n`,`
|
|
2
2
|
`],[`t`,` `],[`r`,`\r`],[`s`,` `],[`0`,`\0`]]);function rn(e){let t=e.slice(1);return nn.get(t)??t}function an(e){let t=e.childForFieldName(`arguments`)?.namedChild(0);return t&&X(t)?[Z(t.text)]:[]}function on(e,t){return e.startsWith(`.`)||e.startsWith(`/`)?!0:t===`rust`&&H(e)}function H(e){return/^(?:crate|self|super)(?:::|$)/u.test(e)}function sn(e){if(e.type===`mod_item`){let t=e.childForFieldName(`name`);return t?[`self::${Y(t.text)}`]:[]}if(e.type===`extern_crate_declaration`){let t=e.childForFieldName(`name`);return t?[Y(t.text)]:[]}let t=e.childForFieldName(`argument`);return t?U(t,``):[]}function U(e,t){switch(e.type){case`use_list`:return e.namedChildren.flatMap(e=>U(e,t));case`scoped_use_list`:{let n=e.childForFieldName(`list`),r=G(t,W(e.childForFieldName(`path`)));return n?U(n,r):K(r)}case`scoped_identifier`:{let n=G(t,Y(e.text));return H(n)?K(n):K(G(t,W(e.childForFieldName(`path`))))}case`use_wildcard`:return K(G(t,W(e.namedChild(0))));case`use_as_clause`:{let n=e.childForFieldName(`path`);return n?U(n,t):[]}case`self`:return K(t);case`identifier`:case`crate`:case`super`:return e.type===`identifier`&&H(t)?K(G(t,Y(e.text))):K(t===``?Y(e.text):t);default:return[]}}function W(e){return e?Y(e.text):``}function G(e,t){return t?e?`${e}::${t}`:t:e}function K(e){return e?[e]:[]}function cn(e,t){if(e.type===`import_from_statement`){let n=e.childForFieldName(`module_name`);if(!n)return[];let r=Y(n.text),i=J(e,`name`);if(!t.expandPythonSubmodules||!r.startsWith(`.`))return[r];if(/^\.+$/u.test(r)&&i.length>0)return i.flatMap(q).map(e=>`${r}${e}`);let a=i.flatMap(q).map(e=>`${r}.${e}`);return a.length>0?[r,...a]:[r]}return e.type===`import_statement`?e.namedChildren.map(e=>ln(e)).filter(e=>e!==void 0):[]}function q(e){if(e.type===`aliased_import`){let t=e.childForFieldName(`name`);return t?q(t):[]}return e.type===`identifier`?[e.text]:e.type===`dotted_name`?[Y(e.text)]:e.namedChildren.flatMap(q)}function J(e,t){let n=[];for(let r=0;r<e.childCount;r+=1){let i=e.child(r);i&&e.fieldNameForChild(r)===t&&n.push(i)}return n}function ln(e){if(e.type===`dotted_name`||e.type===`relative_import`)return Y(e.text);let t=e.childForFieldName(`name`);if(t)return Y(t.text);for(let t of e.namedChildren){let e=ln(t);if(e)return e}}function Y(e){return e.replaceAll(/\s+/gu,``)}function un(e){if(X(e))return e;for(let t of e.namedChildren){let e=un(t);if(e)return e}}function X(e){return e.type===`string`||e.type===`string_literal`||e.type===`interpreted_string_literal`}function Z(e){return e.replaceAll(/^['"`<]|['"`>]$/gu,``)}function dn(e){return e.type.startsWith(`export`)&&e.type!==`exports_module_directive`||e.type===`public_field_definition`}function fn(e){let t=new Set;for(let n of e.keys())pn(n,n,e,new Set)&&t.add(n);return t}function pn(e,t,n,r){let i=n.get(e);if(!i)return!1;for(let e of i)if(e===t||!r.has(e)&&(r.add(e),pn(e,t,n,r)))return!0;return!1}function mn(e){let t=new Map,n=0;for(let r of e.keys())n=Math.max(n,hn(r,e,new Set,t).depth);return n}function hn(e,t,n,r){let i=r.get(e);if(i!==void 0)return{depth:i,tainted:!1};let a=t.get(e);if(!a||a.size===0)return{depth:0,tainted:!1};if(n.has(e))return{depth:0,tainted:!0};n.add(e);let o=0,s=!1;for(let e of a){let i=hn(e,t,n,r);o=Math.max(o,1+i.depth),s||=i.tainted}return n.delete(e),s||r.set(e,o),{depth:o,tainted:s}}function gn(e,t){let[n,r]=e.size<=t.size?[e,t]:[t,e],i=0;for(let e of n)r.has(e)&&(i+=1);return i}function _n(e,t,n){if(n===0)return 100;let r=171-5.2*Math.log(Math.max(e,1))-.23*t-16.2*Math.log(n);return Math.max(0,Math.min(100,r*100/171))}function Q(e,t){e.set(t,(e.get(t)??0)+1)}function vn(e,t){return e.length===0?0:Math.max(...e.map(e=>e[t]))}function yn(e){let t=0;for(let n of e.values())t=Math.max(t,n);return t}function $(e){let t=0;for(let n of e)t+=n;return t}exports.TreeMeasurer=u,exports.collectDuplicationCandidates=h,exports.defaultMeasurer=p,exports.measureCode=m;
|
|
3
3
|
//# sourceMappingURL=metrics.cjs.map
|