gitnexus 1.6.5 → 1.6.6-rc.10
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/dist/_shared/index.d.ts +2 -2
- package/dist/_shared/index.d.ts.map +1 -1
- package/dist/_shared/index.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/context.d.ts +37 -0
- package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/registries/context.js.map +1 -1
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +4 -2
- package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/registries/lookup-core.js +8 -23
- package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -1
- package/dist/_shared/scope-resolution/symbol-definition.d.ts +20 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/cli/analyze.js +65 -3
- package/dist/cli/eval-server.d.ts +14 -3
- package/dist/cli/eval-server.js +79 -9
- package/dist/cli/index.js +3 -1
- package/dist/cli/serve.js +6 -1
- package/dist/cli/wiki.d.ts +1 -0
- package/dist/cli/wiki.js +32 -8
- package/dist/core/ingestion/language-provider.d.ts +29 -0
- package/dist/core/ingestion/languages/c-cpp.js +46 -0
- package/dist/core/ingestion/languages/cpp/arity-metadata.d.ts +17 -0
- package/dist/core/ingestion/languages/cpp/arity-metadata.js +51 -2
- package/dist/core/ingestion/languages/cpp/arity.d.ts +4 -1
- package/dist/core/ingestion/languages/cpp/arity.js +4 -1
- package/dist/core/ingestion/languages/cpp/captures.js +73 -0
- package/dist/core/ingestion/languages/cpp/constraint-extractor.d.ts +73 -0
- package/dist/core/ingestion/languages/cpp/constraint-extractor.js +308 -0
- package/dist/core/ingestion/languages/cpp/constraint-filter.d.ts +31 -0
- package/dist/core/ingestion/languages/cpp/constraint-filter.js +135 -0
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +6 -0
- package/dist/core/ingestion/languages/cpp/type-classifier.d.ts +26 -0
- package/dist/core/ingestion/languages/cpp/type-classifier.js +49 -0
- package/dist/core/ingestion/model/field-registry.d.ts +17 -3
- package/dist/core/ingestion/model/field-registry.js +19 -4
- package/dist/core/ingestion/model/owned-members-lookup.d.ts +19 -0
- package/dist/core/ingestion/model/owned-members-lookup.js +43 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +2 -1
- package/dist/core/ingestion/model/symbol-table.js +3 -0
- package/dist/core/ingestion/model/type-registry.d.ts +9 -0
- package/dist/core/ingestion/model/type-registry.js +18 -0
- package/dist/core/ingestion/parsing-processor.js +35 -2
- package/dist/core/ingestion/resolve-references.d.ts +3 -1
- package/dist/core/ingestion/resolve-references.js +5 -1
- package/dist/core/ingestion/scope-extractor.js +63 -0
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +21 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +1 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +14 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +12 -0
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +11 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +83 -28
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +30 -7
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +49 -18
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +1 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +10 -4
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +4 -2
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +48 -9
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +3 -0
- package/dist/core/ingestion/utils/template-arguments.d.ts +19 -0
- package/dist/core/ingestion/utils/template-arguments.js +30 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +2 -1
- package/dist/core/ingestion/workers/parse-worker.js +1 -0
- package/dist/core/lbug/lbug-adapter.d.ts +3 -1
- package/dist/core/lbug/lbug-adapter.js +33 -19
- package/dist/core/lbug/lbug-config.d.ts +1 -1
- package/dist/core/lbug/lbug-config.js +1 -1
- package/dist/core/lbug/pool-adapter.d.ts +0 -7
- package/dist/core/lbug/pool-adapter.js +10 -35
- package/dist/core/lbug/query-params.d.ts +1 -0
- package/dist/core/lbug/query-params.js +21 -0
- package/dist/core/search/bm25-index.js +4 -6
- package/dist/core/wiki/generator.d.ts +13 -0
- package/dist/core/wiki/generator.js +45 -4
- package/dist/core/wiki/llm-client.d.ts +1 -1
- package/dist/core/wiki/llm-client.js +23 -6
- package/dist/mcp/local/local-backend.d.ts +18 -3
- package/dist/mcp/local/local-backend.js +105 -11
- package/dist/mcp/tools.js +10 -0
- package/dist/server/api.d.ts +3 -0
- package/dist/server/api.js +36 -24
- package/package.json +1 -1
package/dist/cli/wiki.js
CHANGED
|
@@ -14,6 +14,19 @@ import { WikiGenerator } from '../core/wiki/generator.js';
|
|
|
14
14
|
import { resolveLLMConfig } from '../core/wiki/llm-client.js';
|
|
15
15
|
import { detectCursorCLI } from '../core/wiki/cursor-client.js';
|
|
16
16
|
import { logger } from '../core/logger.js';
|
|
17
|
+
function parsePositiveIntegerOption(value, flag, multiplier = 1) {
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
const trimmed = value.trim();
|
|
21
|
+
if (!/^[1-9]\d*$/.test(trimmed)) {
|
|
22
|
+
throw new Error(`${flag} must be a positive integer`);
|
|
23
|
+
}
|
|
24
|
+
const parsed = parseInt(trimmed, 10);
|
|
25
|
+
if (parsed > Math.floor(Number.MAX_SAFE_INTEGER / multiplier)) {
|
|
26
|
+
throw new Error(`${flag} is too large`);
|
|
27
|
+
}
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
17
30
|
/**
|
|
18
31
|
* Prompt the user for input via stdin.
|
|
19
32
|
*/
|
|
@@ -100,6 +113,17 @@ export const wikiCommand = async (inputPath, options) => {
|
|
|
100
113
|
process.exitCode = 1;
|
|
101
114
|
return;
|
|
102
115
|
}
|
|
116
|
+
let timeoutSeconds;
|
|
117
|
+
let retries;
|
|
118
|
+
try {
|
|
119
|
+
timeoutSeconds = parsePositiveIntegerOption(options?.timeout, '--timeout', 1000);
|
|
120
|
+
retries = parsePositiveIntegerOption(options?.retries, '--retries');
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.log(` Error: ${error.message}\n`);
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
103
127
|
// ── Resolve LLM config (with interactive fallback) ─────────────────
|
|
104
128
|
// Save any CLI overrides immediately
|
|
105
129
|
if (options?.apiKey ||
|
|
@@ -305,15 +329,11 @@ export const wikiCommand = async (inputPath, options) => {
|
|
|
305
329
|
}
|
|
306
330
|
}
|
|
307
331
|
// ── Apply per-run overrides not saved to config ────────────────────
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
if (!isNaN(secs) && secs > 0)
|
|
311
|
-
llmConfig.requestTimeoutMs = secs * 1000;
|
|
332
|
+
if (timeoutSeconds !== undefined) {
|
|
333
|
+
llmConfig.requestTimeoutMs = timeoutSeconds * 1000;
|
|
312
334
|
}
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
if (!isNaN(n) && n > 0)
|
|
316
|
-
llmConfig.maxAttempts = n;
|
|
335
|
+
if (retries !== undefined) {
|
|
336
|
+
llmConfig.maxAttempts = retries;
|
|
317
337
|
}
|
|
318
338
|
// ── Setup progress bar with elapsed timer ──────────────────────────
|
|
319
339
|
const bar = new cliProgress.SingleBar({
|
|
@@ -344,6 +364,7 @@ export const wikiCommand = async (inputPath, options) => {
|
|
|
344
364
|
force: options?.force,
|
|
345
365
|
concurrency: options?.concurrency ? parseInt(options.concurrency, 10) : undefined,
|
|
346
366
|
reviewOnly: options?.review,
|
|
367
|
+
lang: options?.lang,
|
|
347
368
|
};
|
|
348
369
|
const generator = new WikiGenerator(repoPath, storagePath, lbugPath, llmConfig, wikiOptions, (phase, percent, detail) => {
|
|
349
370
|
const label = detail || phase;
|
|
@@ -473,6 +494,9 @@ export const wikiCommand = async (inputPath, options) => {
|
|
|
473
494
|
if (err.message?.includes('No source files')) {
|
|
474
495
|
console.log(`\n ${err.message}\n`);
|
|
475
496
|
}
|
|
497
|
+
else if (err.message?.includes('LLM request timed out after')) {
|
|
498
|
+
console.log(`\n Timeout: ${err.message}\n`);
|
|
499
|
+
}
|
|
476
500
|
else if (err.message?.includes('content filter')) {
|
|
477
501
|
// Content filter block — actionable message
|
|
478
502
|
console.log(`\n Content Filter: ${err.message}\n`);
|
|
@@ -155,6 +155,35 @@ interface LanguageProviderConfig {
|
|
|
155
155
|
funcName: string;
|
|
156
156
|
label: NodeLabel;
|
|
157
157
|
} | null;
|
|
158
|
+
/**
|
|
159
|
+
* Extract a per-language template-constraint payload for a templated
|
|
160
|
+
* function / method definition. Used by `parsing-processor` to
|
|
161
|
+
* disambiguate same-name same-arity overloads whose distinguishing
|
|
162
|
+
* signal is their template constraints rather than their parameter
|
|
163
|
+
* types — the canonical C++ SFINAE case (issue #1579):
|
|
164
|
+
*
|
|
165
|
+
* template<class T, std::enable_if_t<is_integral_v<T>, int> = 0>
|
|
166
|
+
* void process(T); // overload A
|
|
167
|
+
*
|
|
168
|
+
* template<class T, std::enable_if_t<is_floating_point_v<T>, int> = 0>
|
|
169
|
+
* void process(T); // overload B
|
|
170
|
+
*
|
|
171
|
+
* Both overloads' `parameterTypes` collapse to `['T']`, so without a
|
|
172
|
+
* constraint fingerprint in the graph node ID they merge into one
|
|
173
|
+
* Function node and the resolver only ever sees one candidate to
|
|
174
|
+
* narrow. The hook's return value is stamped onto the node's ID via
|
|
175
|
+
* `templateConstraintsIdTag()` AND stored on the node's
|
|
176
|
+
* `templateConstraints` property so `resolveDefGraphId` can look up
|
|
177
|
+
* the right overload by re-hashing the def's constraints at resolve
|
|
178
|
+
* time.
|
|
179
|
+
*
|
|
180
|
+
* Returns the opaque payload (any JSON-serializable shape — the
|
|
181
|
+
* producing adapter owns it; shared code MUST NOT inspect) or
|
|
182
|
+
* `undefined` when no constraints exist / the node isn't a templated
|
|
183
|
+
* function. Languages without SFINAE / concept semantics leave this
|
|
184
|
+
* undefined and the disambiguation is a pass-through.
|
|
185
|
+
*/
|
|
186
|
+
readonly extractTemplateConstraints?: (definitionNode: SyntaxNode) => unknown;
|
|
158
187
|
/** Override the default node label for definition.function captures.
|
|
159
188
|
* Return null to skip (C/C++ duplicate), a different label to reclassify
|
|
160
189
|
* (e.g., 'Method' for Kotlin), or defaultLabel to keep as-is.
|
|
@@ -39,6 +39,7 @@ import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
|
|
39
39
|
import { stripUeMacros } from '../cpp-ue-preprocessor.js';
|
|
40
40
|
import { emitCScopeCaptures, interpretCImport, interpretCTypeBinding, cArityCompatibility, cBindingScopeFor, cImportOwningScope, cReceiverBinding, } from './c/index.js';
|
|
41
41
|
import { emitCppScopeCaptures, interpretCppImport, interpretCppTypeBinding, cppArityCompatibility, cppBindingScopeFor, cppImportOwningScope, cppReceiverBinding, } from './cpp/index.js';
|
|
42
|
+
import { extractCppTemplateConstraints } from './cpp/constraint-extractor.js';
|
|
42
43
|
const C_BUILT_INS = new Set([
|
|
43
44
|
'printf',
|
|
44
45
|
'fprintf',
|
|
@@ -417,6 +418,7 @@ export const cppProvider = defineLanguage({
|
|
|
417
418
|
heritageExtractor: createHeritageExtractor(SupportedLanguages.CPlusPlus),
|
|
418
419
|
labelOverride: cppLabelOverride,
|
|
419
420
|
builtInNames: C_BUILT_INS,
|
|
421
|
+
extractTemplateConstraints: extractCppTemplateConstraintsForProvider,
|
|
420
422
|
// ── RFC #909 Ring 3: scope-based resolution hooks (RFC §5) ──────────
|
|
421
423
|
emitScopeCaptures: emitCppScopeCaptures,
|
|
422
424
|
interpretImport: interpretCppImport,
|
|
@@ -427,3 +429,47 @@ export const cppProvider = defineLanguage({
|
|
|
427
429
|
arityCompatibility: cppArityCompatibility,
|
|
428
430
|
// mergeBindings + resolveImportTarget live on ScopeResolver (see cpp/scope-resolver.ts).
|
|
429
431
|
});
|
|
432
|
+
/**
|
|
433
|
+
* LanguageProvider hook: walk from a function definition node up to its
|
|
434
|
+
* enclosing `template_declaration` and extract the SFINAE / `requires`-
|
|
435
|
+
* clause constraint payload. Used by `parsing-processor` to fingerprint
|
|
436
|
+
* the graph node ID so two SFINAE overloads with identical
|
|
437
|
+
* `parameterTypes` get distinct nodes (issue #1579).
|
|
438
|
+
*
|
|
439
|
+
* Returns `undefined` for non-templated functions and for templated
|
|
440
|
+
* functions whose constraints the extractor can't model — both cases
|
|
441
|
+
* result in no constraint suffix on the node ID.
|
|
442
|
+
*/
|
|
443
|
+
function extractCppTemplateConstraintsForProvider(definitionNode) {
|
|
444
|
+
// Walk up to the enclosing template_declaration. Bound the walk so we
|
|
445
|
+
// can't accidentally land on a far-ancestor template_declaration that
|
|
446
|
+
// wraps an unrelated function.
|
|
447
|
+
let cur = definitionNode.parent;
|
|
448
|
+
let hops = 8;
|
|
449
|
+
let templateDecl = null;
|
|
450
|
+
while (cur !== null && hops-- > 0) {
|
|
451
|
+
if (cur.type === 'template_declaration') {
|
|
452
|
+
templateDecl = cur;
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
if (cur.type === 'translation_unit')
|
|
456
|
+
break;
|
|
457
|
+
cur = cur.parent;
|
|
458
|
+
}
|
|
459
|
+
if (templateDecl === null)
|
|
460
|
+
return undefined;
|
|
461
|
+
// Find the function_declarator inside the function definition so the
|
|
462
|
+
// extractor can map template params to function-argument indices.
|
|
463
|
+
let declarator = definitionNode.childForFieldName('declarator');
|
|
464
|
+
let walk = 8;
|
|
465
|
+
while (declarator !== null && walk-- > 0) {
|
|
466
|
+
if (declarator.type === 'function_declarator')
|
|
467
|
+
break;
|
|
468
|
+
if (declarator.type === 'pointer_declarator' || declarator.type === 'reference_declarator') {
|
|
469
|
+
declarator = declarator.childForFieldName('declarator');
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
return extractCppTemplateConstraints(templateDecl, declarator);
|
|
475
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
|
2
|
+
import type { ParameterTypeClass } from '../../../../_shared/index.js';
|
|
2
3
|
export interface CppArityInfo {
|
|
3
4
|
parameterCount?: number;
|
|
4
5
|
requiredParameterCount?: number;
|
|
5
6
|
parameterTypes?: string[];
|
|
7
|
+
parameterTypeClasses?: ParameterTypeClass[];
|
|
6
8
|
}
|
|
7
9
|
/**
|
|
8
10
|
* Compute declaration arity from a C++ function definition or declaration node.
|
|
@@ -16,3 +18,18 @@ export declare function computeCppDeclarationArity(node: SyntaxNode): CppArityIn
|
|
|
16
18
|
* Compute call-site arity from a call_expression node.
|
|
17
19
|
*/
|
|
18
20
|
export declare function computeCppCallArity(node: SyntaxNode): number;
|
|
21
|
+
/**
|
|
22
|
+
* Normalize a C++ parameter type for overload disambiguation.
|
|
23
|
+
* Maps common qualified/aliased types to their canonical short forms
|
|
24
|
+
* so that `narrowOverloadCandidates` can match against literal-inferred
|
|
25
|
+
* argument types (e.g. `inferCppLiteralType` returns `'string'` for
|
|
26
|
+
* string literals, not `'std::string'`).
|
|
27
|
+
*
|
|
28
|
+
* This intentionally remains coarse and graph-ID-stable: cv-qualifiers,
|
|
29
|
+
* reference markers, and pointer markers are stripped here. C++ callers
|
|
30
|
+
* that need those distinctions should read `parameterTypeClasses`, which
|
|
31
|
+
* is an additive sidecar and does not participate in overload node ID
|
|
32
|
+
* hashing.
|
|
33
|
+
*/
|
|
34
|
+
export declare function normalizeCppParamType(raw: string): string;
|
|
35
|
+
export declare function classifyCppParameterType(rawType: string, declaratorText?: string, fullParameterText?: string): ParameterTypeClass;
|
|
@@ -55,27 +55,34 @@ export function computeCppDeclarationArity(node) {
|
|
|
55
55
|
p.type === 'variadic_parameter_declaration').length;
|
|
56
56
|
const totalNonVariadic = requiredCount + optionalCount;
|
|
57
57
|
const types = [];
|
|
58
|
+
const typeClasses = [];
|
|
58
59
|
for (const p of params) {
|
|
59
60
|
if (p.type === 'variadic_parameter') {
|
|
60
61
|
types.push('...');
|
|
62
|
+
typeClasses.push(unknownTypeClass('...'));
|
|
61
63
|
}
|
|
62
64
|
else if (p.type === 'variadic_parameter_declaration') {
|
|
63
65
|
// Parameter pack: treated as variadic
|
|
64
66
|
types.push('...');
|
|
67
|
+
typeClasses.push(unknownTypeClass('...'));
|
|
65
68
|
}
|
|
66
69
|
else {
|
|
67
70
|
const typeNode = p.childForFieldName('type');
|
|
68
|
-
|
|
71
|
+
const rawType = typeNode?.text ?? 'unknown';
|
|
72
|
+
types.push(normalizeCppParamType(rawType));
|
|
73
|
+
typeClasses.push(classifyCppParameterType(rawType, p.childForFieldName('declarator')?.text, p.text));
|
|
69
74
|
}
|
|
70
75
|
}
|
|
71
76
|
// Append '...' for C-style variadic if not already in types
|
|
72
77
|
if (hasEllipsis && !types.includes('...')) {
|
|
73
78
|
types.push('...');
|
|
79
|
+
typeClasses.push(unknownTypeClass('...'));
|
|
74
80
|
}
|
|
75
81
|
return {
|
|
76
82
|
parameterCount: isVariadic ? undefined : totalNonVariadic,
|
|
77
83
|
requiredParameterCount: requiredCount,
|
|
78
84
|
parameterTypes: types,
|
|
85
|
+
parameterTypeClasses: typeClasses,
|
|
79
86
|
};
|
|
80
87
|
}
|
|
81
88
|
/**
|
|
@@ -102,8 +109,14 @@ export function computeCppCallArity(node) {
|
|
|
102
109
|
* so that `narrowOverloadCandidates` can match against literal-inferred
|
|
103
110
|
* argument types (e.g. `inferCppLiteralType` returns `'string'` for
|
|
104
111
|
* string literals, not `'std::string'`).
|
|
112
|
+
*
|
|
113
|
+
* This intentionally remains coarse and graph-ID-stable: cv-qualifiers,
|
|
114
|
+
* reference markers, and pointer markers are stripped here. C++ callers
|
|
115
|
+
* that need those distinctions should read `parameterTypeClasses`, which
|
|
116
|
+
* is an additive sidecar and does not participate in overload node ID
|
|
117
|
+
* hashing.
|
|
105
118
|
*/
|
|
106
|
-
function normalizeCppParamType(raw) {
|
|
119
|
+
export function normalizeCppParamType(raw) {
|
|
107
120
|
let t = raw.trim();
|
|
108
121
|
// Strip const, volatile, etc.
|
|
109
122
|
t = t.replace(/\b(const|volatile|restrict|mutable|constexpr)\b/g, '').trim();
|
|
@@ -140,6 +153,42 @@ function normalizeCppParamType(raw) {
|
|
|
140
153
|
};
|
|
141
154
|
return STD_MAP[t] ?? t;
|
|
142
155
|
}
|
|
156
|
+
export function classifyCppParameterType(rawType, declaratorText, fullParameterText) {
|
|
157
|
+
const source = fullParameterText ?? `${rawType} ${declaratorText ?? ''}`.trim();
|
|
158
|
+
if (rawType === 'unknown')
|
|
159
|
+
return unknownTypeClass('unknown');
|
|
160
|
+
const hasConst = /\bconst\b/.test(source);
|
|
161
|
+
const hasVolatile = /\bvolatile\b/.test(source);
|
|
162
|
+
const cv = hasConst && hasVolatile
|
|
163
|
+
? 'const volatile'
|
|
164
|
+
: hasConst
|
|
165
|
+
? 'const'
|
|
166
|
+
: hasVolatile
|
|
167
|
+
? 'volatile'
|
|
168
|
+
: 'none';
|
|
169
|
+
const pointerDepth = (source.match(/\*/g) ?? []).length;
|
|
170
|
+
const indirection = pointerDepth > 0
|
|
171
|
+
? 'pointer'
|
|
172
|
+
: /&&/.test(source)
|
|
173
|
+
? 'rvalue-ref'
|
|
174
|
+
: /&/.test(source)
|
|
175
|
+
? 'lvalue-ref'
|
|
176
|
+
: 'value';
|
|
177
|
+
return {
|
|
178
|
+
base: normalizeCppParamType(rawType),
|
|
179
|
+
cv,
|
|
180
|
+
indirection,
|
|
181
|
+
pointerDepth,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function unknownTypeClass(base) {
|
|
185
|
+
return {
|
|
186
|
+
base,
|
|
187
|
+
cv: 'unknown',
|
|
188
|
+
indirection: 'unknown',
|
|
189
|
+
pointerDepth: 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
143
192
|
function findFuncDeclarator(node) {
|
|
144
193
|
let decl = node.childForFieldName('declarator');
|
|
145
194
|
if (decl === null) {
|
|
@@ -7,7 +7,10 @@ import type { Callsite, SymbolDefinition } from '../../../../_shared/index.js';
|
|
|
7
7
|
* - Default parameters (requiredParameterCount < parameterCount)
|
|
8
8
|
* - Variadic functions (C-style `...`)
|
|
9
9
|
* - Parameter packs (V1: treated as variadic)
|
|
10
|
-
* - Templates
|
|
10
|
+
* - Templates: arity check on non-template params; SFINAE / `requires`
|
|
11
|
+
* constraints are filtered separately via `constraintCompatibility`
|
|
12
|
+
* (see `constraint-filter.ts` and issue #1579). Type-argument generic
|
|
13
|
+
* substitution (`List<T>` ≡ `List<U>`) remains out of V1 scope.
|
|
11
14
|
*
|
|
12
15
|
* Verdict:
|
|
13
16
|
* - 'compatible': callsite.arity fits within [required, total] range
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* - Default parameters (requiredParameterCount < parameterCount)
|
|
7
7
|
* - Variadic functions (C-style `...`)
|
|
8
8
|
* - Parameter packs (V1: treated as variadic)
|
|
9
|
-
* - Templates
|
|
9
|
+
* - Templates: arity check on non-template params; SFINAE / `requires`
|
|
10
|
+
* constraints are filtered separately via `constraintCompatibility`
|
|
11
|
+
* (see `constraint-filter.ts` and issue #1579). Type-argument generic
|
|
12
|
+
* substitution (`List<T>` ≡ `List<U>`) remains out of V1 scope.
|
|
10
13
|
*
|
|
11
14
|
* Verdict:
|
|
12
15
|
* - 'compatible': callsite.arity fits within [required, total] range
|
|
@@ -8,6 +8,7 @@ import { markCppAnonymousNamespaceRange, markFileLocal } from './file-local-link
|
|
|
8
8
|
import { markCppDependentBase } from './two-phase-lookup.js';
|
|
9
9
|
import { markCppAdlSiteArgs, markCppAdlSiteNoAdl } from './adl.js';
|
|
10
10
|
import { markCppInlineNamespaceRange } from './inline-namespaces.js';
|
|
11
|
+
import { extractCppTemplateConstraints } from './constraint-extractor.js';
|
|
11
12
|
export function emitCppScopeCaptures(sourceText, filePath, cachedTree) {
|
|
12
13
|
let tree = cachedTree;
|
|
13
14
|
if (tree === undefined) {
|
|
@@ -85,6 +86,9 @@ export function emitCppScopeCaptures(sourceText, filePath, cachedTree) {
|
|
|
85
86
|
if (arity.parameterTypes !== undefined) {
|
|
86
87
|
grouped['@declaration.parameter-types'] = syntheticCapture('@declaration.parameter-types', fnNode, JSON.stringify(arity.parameterTypes));
|
|
87
88
|
}
|
|
89
|
+
if (arity.parameterTypeClasses !== undefined) {
|
|
90
|
+
grouped['@declaration.parameter-type-classes'] = syntheticCapture('@declaration.parameter-type-classes', fnNode, JSON.stringify(arity.parameterTypeClasses));
|
|
91
|
+
}
|
|
88
92
|
// Detect static storage class (file-local linkage)
|
|
89
93
|
if (hasStaticStorageClass(fnNode)) {
|
|
90
94
|
const nameText = grouped['@declaration.name']?.text;
|
|
@@ -99,6 +103,19 @@ export function emitCppScopeCaptures(sourceText, filePath, cachedTree) {
|
|
|
99
103
|
markFileLocal(filePath, nameText);
|
|
100
104
|
}
|
|
101
105
|
}
|
|
106
|
+
// SFINAE / `requires`-clause aware constraints for overload
|
|
107
|
+
// narrowing (issue #1579). Walk from the enclosing
|
|
108
|
+
// `template_declaration` — not the inner `function_definition` —
|
|
109
|
+
// so inline method templates (`template<...> class C { template<...> void f(); }`)
|
|
110
|
+
// pick up the correct outer constraint scope.
|
|
111
|
+
const templateDecl = findEnclosingTemplateDeclaration(fnNode);
|
|
112
|
+
if (templateDecl !== null) {
|
|
113
|
+
const funcDeclarator = findFunctionDeclarator(fnNode);
|
|
114
|
+
const constraints = extractCppTemplateConstraints(templateDecl, funcDeclarator);
|
|
115
|
+
if (constraints !== undefined) {
|
|
116
|
+
grouped['@declaration.template-constraints'] = syntheticCapture('@declaration.template-constraints', fnNode, JSON.stringify(constraints));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
102
119
|
}
|
|
103
120
|
}
|
|
104
121
|
// ── Detect static variables (file-local linkage) ────────────────
|
|
@@ -484,6 +501,53 @@ function extractBaseLookupName(baseNode) {
|
|
|
484
501
|
}
|
|
485
502
|
return '';
|
|
486
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* Walk parent chain from a function_definition / declaration / field_declaration
|
|
506
|
+
* to find the enclosing `template_declaration`. Returns null when the function
|
|
507
|
+
* isn't templated. The walk only ascends through wrapper nodes the C++
|
|
508
|
+
* grammar inserts between `template_declaration` and the function — direct
|
|
509
|
+
* parent in the common case, two hops for member templates whose outer
|
|
510
|
+
* class is also templated (we return the INNERMOST template_declaration,
|
|
511
|
+
* which carries this function's own template parameters).
|
|
512
|
+
*/
|
|
513
|
+
function findEnclosingTemplateDeclaration(fnNode) {
|
|
514
|
+
let cur = fnNode.parent;
|
|
515
|
+
// Cap the walk — `template_declaration` is typically the immediate parent
|
|
516
|
+
// or one wrapper away. Anything deeper is an inline-method-in-template
|
|
517
|
+
// shape and we still want the innermost templates_declaration whose body
|
|
518
|
+
// wraps `fnNode`.
|
|
519
|
+
let hops = 8;
|
|
520
|
+
while (cur !== null && hops-- > 0) {
|
|
521
|
+
if (cur.type === 'template_declaration')
|
|
522
|
+
return cur;
|
|
523
|
+
// Don't ascend past structural boundaries that should reset template scope.
|
|
524
|
+
if (cur.type === 'translation_unit')
|
|
525
|
+
return null;
|
|
526
|
+
cur = cur.parent;
|
|
527
|
+
}
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Locate the `function_declarator` AST node within a function definition
|
|
532
|
+
* or declaration. Unwraps pointer/reference declarator wrappers. Returns
|
|
533
|
+
* null when no function_declarator is found (e.g. variable declaration
|
|
534
|
+
* mis-classified upstream).
|
|
535
|
+
*/
|
|
536
|
+
function findFunctionDeclarator(fnNode) {
|
|
537
|
+
const direct = fnNode.childForFieldName('declarator');
|
|
538
|
+
let cur = direct;
|
|
539
|
+
let hops = 8;
|
|
540
|
+
while (cur !== null && hops-- > 0) {
|
|
541
|
+
if (cur.type === 'function_declarator')
|
|
542
|
+
return cur;
|
|
543
|
+
if (cur.type === 'pointer_declarator' || cur.type === 'reference_declarator') {
|
|
544
|
+
cur = cur.childForFieldName('declarator');
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
return findFirstDescendantOfType(fnNode, 'function_declarator');
|
|
550
|
+
}
|
|
487
551
|
/** Find the first direct child matching one of the given types. */
|
|
488
552
|
function findChildOfType(node, types) {
|
|
489
553
|
for (let i = 0; i < node.childCount; i++) {
|
|
@@ -589,6 +653,15 @@ function inferCppLiteralType(node) {
|
|
|
589
653
|
* - `int n = ...` → 'int'
|
|
590
654
|
* - `const int n = ...` → 'int'
|
|
591
655
|
* Returns empty string if no declaration found or type is auto/placeholder.
|
|
656
|
+
*
|
|
657
|
+
* Limitation: only `declaration` siblings inside the enclosing
|
|
658
|
+
* `compound_statement` are inspected. Function parameters live in the
|
|
659
|
+
* `function_declarator`'s `parameter_list` and are NOT resolved here, so
|
|
660
|
+
* `void run(int n) { process(n); }`
|
|
661
|
+
* infers `''` for `n` and the constraint filter falls through to
|
|
662
|
+
* `'unknown'` → ambiguity suppression → 0 CALLS edges. This is a
|
|
663
|
+
* "degrade not lie" gap (no wrong edges, just missing ones); extending
|
|
664
|
+
* the scan to `parameter_list` is tracked under #1579 as a follow-up.
|
|
592
665
|
*/
|
|
593
666
|
function lookupDeclaredTypeForIdentifier(identNode) {
|
|
594
667
|
const varName = identNode.text;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract C++ template constraint expressions for SFINAE-aware overload
|
|
3
|
+
* narrowing (issue #1579). Recognizes 3 AST shapes:
|
|
4
|
+
*
|
|
5
|
+
* F1 — unqualified non-type template param default:
|
|
6
|
+
* `template<class T, enable_if_t<P, int> = 0> void f(T);`
|
|
7
|
+
* F2 — `std::`-qualified variant (canonical ticket form):
|
|
8
|
+
* `template<class T, std::enable_if_t<P, int> = 0> void f(T);`
|
|
9
|
+
* F4 — C++20 leading requires-clause:
|
|
10
|
+
* `template<class T> requires P void f(T);`
|
|
11
|
+
*
|
|
12
|
+
* Deferred (return `{kind:'unknown'}`):
|
|
13
|
+
* F3 — void-default `typename = enable_if_t<P>` (cppref labels this
|
|
14
|
+
* `/* WRONG *\/` because adjacent overloads collapse to redeclarations)
|
|
15
|
+
* F5 — trailing requires (`void f(T) requires P;`)
|
|
16
|
+
* `requires_expression` blocks (`requires { typename T::U; }`)
|
|
17
|
+
* `decltype(...)`, fold-expressions, user-defined `_v` aliases.
|
|
18
|
+
*
|
|
19
|
+
* The output payload is opaque to shared code — only
|
|
20
|
+
* `constraint-filter.ts` consumes it. See ISO `[temp.constr.normal]` /
|
|
21
|
+
* `<https://en.cppreference.com/w/cpp/language/constraints>` for the
|
|
22
|
+
* normalization the Kleene 3-valued evaluator implements.
|
|
23
|
+
*/
|
|
24
|
+
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
|
25
|
+
export type ConstraintExpr = {
|
|
26
|
+
readonly kind: 'atomic';
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly args: readonly string[];
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: 'and';
|
|
31
|
+
readonly children: readonly ConstraintExpr[];
|
|
32
|
+
} | {
|
|
33
|
+
readonly kind: 'or';
|
|
34
|
+
readonly children: readonly ConstraintExpr[];
|
|
35
|
+
} | {
|
|
36
|
+
readonly kind: 'not';
|
|
37
|
+
readonly child: ConstraintExpr;
|
|
38
|
+
} | {
|
|
39
|
+
readonly kind: 'unknown';
|
|
40
|
+
};
|
|
41
|
+
export interface CppConstraintPayload {
|
|
42
|
+
/** Ordered template parameter names (type-params only — non-type defaults
|
|
43
|
+
* carrying enable_if predicates are folded into `expr`). */
|
|
44
|
+
readonly templateParams: readonly string[];
|
|
45
|
+
/**
|
|
46
|
+
* Mapping from each template parameter name to the call-site argument
|
|
47
|
+
* index where its deduced type lives. Computed by scanning the function's
|
|
48
|
+
* parameter list for the first parameter whose type is the bare template
|
|
49
|
+
* parameter name (or template-typed by it). Missing entries → 'unknown'
|
|
50
|
+
* verdict at evaluation time.
|
|
51
|
+
*/
|
|
52
|
+
readonly paramArgIndex: {
|
|
53
|
+
readonly [paramName: string]: number;
|
|
54
|
+
};
|
|
55
|
+
/** Root constraint expression. When multiple constraints (multiple
|
|
56
|
+
* enable_if defaults, requires clause, etc.) are present they are
|
|
57
|
+
* implicitly conjoined under a top-level `and` node. */
|
|
58
|
+
readonly expr: ConstraintExpr;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Walk a `template_declaration` AST node and extract its constraint
|
|
62
|
+
* payload. Caller is responsible for passing the OUTER `template_declaration`
|
|
63
|
+
* — for class-member template functions, that means the enclosing
|
|
64
|
+
* template_declaration of the class OR of the method, whichever
|
|
65
|
+
* directly precedes the function definition.
|
|
66
|
+
*
|
|
67
|
+
* Returns `undefined` when the template_declaration declares no
|
|
68
|
+
* constraints worth tracking (no enable_if default, no requires clause).
|
|
69
|
+
* Returns a payload whose `expr.kind === 'unknown'` when constraints are
|
|
70
|
+
* present but the extractor cannot model them — monotonicity guarantees
|
|
71
|
+
* the filter keeps the candidate in that case.
|
|
72
|
+
*/
|
|
73
|
+
export declare function extractCppTemplateConstraints(templateDecl: SyntaxNode, funcDeclarator: SyntaxNode | null): CppConstraintPayload | undefined;
|