gitnexus 1.6.5-rc.36 → 1.6.5-rc.38
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* C++ argument-dependent lookup (ADL / Koenig lookup)
|
|
2
|
+
* C++ argument-dependent lookup (ADL / Koenig lookup).
|
|
3
3
|
*
|
|
4
4
|
* When ordinary unqualified lookup fails for a free-call site, ADL also
|
|
5
5
|
* considers candidates declared in the **associated namespaces** of the
|
|
@@ -13,17 +13,18 @@
|
|
|
13
13
|
* `using` anything. With V1 ADL: `audit::record` is discovered via
|
|
14
14
|
* `audit::Event`'s associated namespace.
|
|
15
15
|
*
|
|
16
|
-
* ##
|
|
16
|
+
* ## Current boundary
|
|
17
17
|
*
|
|
18
|
-
*
|
|
18
|
+
* The current implementation covers ONE associated-entity rule: an argument that's a directly-named
|
|
19
19
|
* class type (`audit::Event e`) contributes its **direct enclosing
|
|
20
|
-
* namespace** to the candidate set.
|
|
20
|
+
* namespace** to the candidate set. V2 extends that one step to
|
|
21
|
+
* pointer-typed class args (`audit::Event* p`, `audit::Event** pp`):
|
|
22
|
+
* they contribute the pointee class's enclosing namespace too. Reference
|
|
21
23
|
* arguments, function-pointer arguments, template specializations,
|
|
22
|
-
* base-class associated namespaces
|
|
23
|
-
* deliberately excluded.
|
|
24
|
-
* locks the exclusion in CI.
|
|
24
|
+
* base-class associated namespaces, and the rest of the full closure are
|
|
25
|
+
* still deliberately excluded.
|
|
25
26
|
*
|
|
26
|
-
*
|
|
27
|
+
* The current implementation also short-circuits to ADL only when ordinary lookup is empty
|
|
27
28
|
* (`findCallableBindingInScope` returned undefined). ISO C++ would
|
|
28
29
|
* normally merge ADL candidates with ordinary-lookup candidates and
|
|
29
30
|
* run overload resolution over the union; V1 defers that merge to V2.
|
|
@@ -54,16 +55,16 @@
|
|
|
54
55
|
import type { ParsedFile, SymbolDefinition } from '../../../../_shared/index.js';
|
|
55
56
|
import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
|
|
56
57
|
/**
|
|
57
|
-
* Per-argument shape information collected at capture time. ADL
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* Per-argument shape information collected at capture time. ADL fires for
|
|
59
|
+
* arguments where `simpleClassName !== ''` AND `!isReference`, including
|
|
60
|
+
* class pointers whose declarator chain resolves to a named class type.
|
|
60
61
|
*/
|
|
61
62
|
export interface CppAdlArgInfo {
|
|
62
63
|
/** Simple class-like type name (last segment of qualified name); empty
|
|
63
64
|
* for primitives, literals, function pointers, template specs, etc. */
|
|
64
65
|
readonly simpleClassName: string;
|
|
65
|
-
/** True when the variable's declarator
|
|
66
|
-
*
|
|
66
|
+
/** True when the variable's declarator contained one or more
|
|
67
|
+
* `pointer_declarator` wrappers. */
|
|
67
68
|
readonly isPointer: boolean;
|
|
68
69
|
/** True when the variable's declarator was a `reference_declarator`. */
|
|
69
70
|
readonly isReference: boolean;
|
|
@@ -105,8 +106,8 @@ export declare function populateCppAssociatedNamespaces(parsed: ParsedFile): voi
|
|
|
105
106
|
*
|
|
106
107
|
* Fires only when:
|
|
107
108
|
* - the call site is not in `noAdlSites` (parenthesized form), AND
|
|
108
|
-
* - at least one argument
|
|
109
|
-
* not reference,
|
|
109
|
+
* - at least one argument resolves to a named class type (value or
|
|
110
|
+
* pointer, but not reference, function pointer, literal, or primitive).
|
|
110
111
|
*/
|
|
111
112
|
export declare function pickCppAdlCandidates(site: {
|
|
112
113
|
readonly name: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* C++ argument-dependent lookup (ADL / Koenig lookup)
|
|
2
|
+
* C++ argument-dependent lookup (ADL / Koenig lookup).
|
|
3
3
|
*
|
|
4
4
|
* When ordinary unqualified lookup fails for a free-call site, ADL also
|
|
5
5
|
* considers candidates declared in the **associated namespaces** of the
|
|
@@ -13,17 +13,18 @@
|
|
|
13
13
|
* `using` anything. With V1 ADL: `audit::record` is discovered via
|
|
14
14
|
* `audit::Event`'s associated namespace.
|
|
15
15
|
*
|
|
16
|
-
* ##
|
|
16
|
+
* ## Current boundary
|
|
17
17
|
*
|
|
18
|
-
*
|
|
18
|
+
* The current implementation covers ONE associated-entity rule: an argument that's a directly-named
|
|
19
19
|
* class type (`audit::Event e`) contributes its **direct enclosing
|
|
20
|
-
* namespace** to the candidate set.
|
|
20
|
+
* namespace** to the candidate set. V2 extends that one step to
|
|
21
|
+
* pointer-typed class args (`audit::Event* p`, `audit::Event** pp`):
|
|
22
|
+
* they contribute the pointee class's enclosing namespace too. Reference
|
|
21
23
|
* arguments, function-pointer arguments, template specializations,
|
|
22
|
-
* base-class associated namespaces
|
|
23
|
-
* deliberately excluded.
|
|
24
|
-
* locks the exclusion in CI.
|
|
24
|
+
* base-class associated namespaces, and the rest of the full closure are
|
|
25
|
+
* still deliberately excluded.
|
|
25
26
|
*
|
|
26
|
-
*
|
|
27
|
+
* The current implementation also short-circuits to ADL only when ordinary lookup is empty
|
|
27
28
|
* (`findCallableBindingInScope` returned undefined). ISO C++ would
|
|
28
29
|
* normally merge ADL candidates with ordinary-lookup candidates and
|
|
29
30
|
* run overload resolution over the union; V1 defers that merge to V2.
|
|
@@ -118,8 +119,8 @@ export function populateCppAssociatedNamespaces(parsed) {
|
|
|
118
119
|
*
|
|
119
120
|
* Fires only when:
|
|
120
121
|
* - the call site is not in `noAdlSites` (parenthesized form), AND
|
|
121
|
-
* - at least one argument
|
|
122
|
-
* not reference,
|
|
122
|
+
* - at least one argument resolves to a named class type (value or
|
|
123
|
+
* pointer, but not reference, function pointer, literal, or primitive).
|
|
123
124
|
*/
|
|
124
125
|
export function pickCppAdlCandidates(site, callerParsed, scopes, parsedFiles) {
|
|
125
126
|
const key = siteKey(callerParsed.filePath, site.atRange.startLine, site.atRange.startCol);
|
|
@@ -128,12 +129,12 @@ export function pickCppAdlCandidates(site, callerParsed, scopes, parsedFiles) {
|
|
|
128
129
|
const args = argInfoBySite.get(key);
|
|
129
130
|
if (args === undefined || args.length === 0)
|
|
130
131
|
return undefined;
|
|
131
|
-
// Collect associated namespace QNames from every
|
|
132
|
+
// Collect associated namespace QNames from every participating class-typed arg.
|
|
132
133
|
const associatedNamespaces = new Set();
|
|
133
134
|
for (const arg of args) {
|
|
134
135
|
if (arg.simpleClassName === '')
|
|
135
136
|
continue;
|
|
136
|
-
if (arg.
|
|
137
|
+
if (arg.isReference)
|
|
137
138
|
continue;
|
|
138
139
|
const classDef = findCppClassDefBySimpleName(arg.simpleClassName, scopes);
|
|
139
140
|
if (classDef === undefined)
|
|
@@ -657,14 +657,15 @@ function isParenthesizedFunctionCall(callNode) {
|
|
|
657
657
|
}
|
|
658
658
|
/**
|
|
659
659
|
* Per-argument ADL classification: walk each argument of a free call and
|
|
660
|
-
* decide whether it
|
|
661
|
-
*
|
|
662
|
-
* pointer, template specialization
|
|
660
|
+
* decide whether it resolves to a directly-named class or class-pointer
|
|
661
|
+
* type (ADL fires) or to an excluded shape such as a reference, function
|
|
662
|
+
* pointer, primitive, literal, or template specialization.
|
|
663
663
|
*
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
664
|
+
* Class-typed values and class pointers (`N::S`, `N::S*`, `N::S**`) all
|
|
665
|
+
* preserve the pointee class name for associated-namespace lookup.
|
|
666
|
+
* Function pointers remain excluded even when their return type names a
|
|
667
|
+
* class, because the associated entity is the pointed-to function type,
|
|
668
|
+
* not the return type.
|
|
668
669
|
*/
|
|
669
670
|
function inferCppCallAdlArgs(callNode) {
|
|
670
671
|
const argList = callNode.childForFieldName('arguments');
|
|
@@ -727,15 +728,23 @@ function lookupAdlIdentifierType(identNode) {
|
|
|
727
728
|
continue;
|
|
728
729
|
// Unwrap declarator chain to find pointer/reference markers and the
|
|
729
730
|
// variable name. `init_declarator > pointer_declarator > identifier`
|
|
730
|
-
// means pointer-typed;
|
|
731
|
-
//
|
|
731
|
+
// means pointer-typed; repeated pointer wrappers still count as pointer
|
|
732
|
+
// typed; `init_declarator > reference_declarator > ...` means
|
|
733
|
+
// reference-typed; bare `init_declarator > identifier` is value.
|
|
734
|
+
// Function-pointer wrappers (`pointer_declarator > function_declarator`)
|
|
735
|
+
// must not contribute ADL associated namespaces.
|
|
732
736
|
let isPointer = false;
|
|
733
737
|
let isReference = false;
|
|
738
|
+
let isFunctionPointer = false;
|
|
734
739
|
let inner = declarator;
|
|
735
740
|
let nameText = null;
|
|
736
741
|
let safety = 16; // bound walk depth defensively
|
|
737
742
|
while (safety-- > 0) {
|
|
738
743
|
if (inner.type === 'pointer_declarator') {
|
|
744
|
+
if (findFirstDescendantOfType(inner, 'function_declarator') !== null) {
|
|
745
|
+
isFunctionPointer = true;
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
739
748
|
isPointer = true;
|
|
740
749
|
const next = inner.childForFieldName('declarator');
|
|
741
750
|
if (next === null)
|
|
@@ -766,11 +775,15 @@ function lookupAdlIdentifierType(identNode) {
|
|
|
766
775
|
inner = next;
|
|
767
776
|
continue;
|
|
768
777
|
}
|
|
778
|
+
if (inner.type === 'function_declarator') {
|
|
779
|
+
isFunctionPointer = true;
|
|
780
|
+
break;
|
|
781
|
+
}
|
|
769
782
|
// Reached the leaf — usually `identifier`. Take its text.
|
|
770
783
|
nameText = inner.text;
|
|
771
784
|
break;
|
|
772
785
|
}
|
|
773
|
-
if (nameText !== varName)
|
|
786
|
+
if (isFunctionPointer || nameText !== varName)
|
|
774
787
|
continue;
|
|
775
788
|
const simpleClassName = extractAdlSimpleTypeName(typeNode);
|
|
776
789
|
return { simpleClassName, isPointer, isReference };
|
|
@@ -778,9 +791,9 @@ function lookupAdlIdentifierType(identNode) {
|
|
|
778
791
|
return EMPTY_ADL_ARG;
|
|
779
792
|
}
|
|
780
793
|
/** Extract the simple class-like type name from a `type:` field node.
|
|
781
|
-
* Returns '' for primitives, template specializations,
|
|
782
|
-
*
|
|
783
|
-
*
|
|
794
|
+
* Returns '' for primitives, template specializations, and any other
|
|
795
|
+
* unsupported type-only shape. Function pointers are filtered at the
|
|
796
|
+
* declarator level in `lookupAdlIdentifierType`. */
|
|
784
797
|
function extractAdlSimpleTypeName(typeNode) {
|
|
785
798
|
if (typeNode.type === 'primitive_type')
|
|
786
799
|
return '';
|
|
@@ -10,6 +10,7 @@ import { realpathSync } from 'fs';
|
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import os from 'os';
|
|
12
12
|
import { getInferredRepoName, resolveRepoIdentityRoot } from './git.js';
|
|
13
|
+
import { logger } from '../core/logger.js';
|
|
13
14
|
/**
|
|
14
15
|
* Normalise a repo path for registry comparison across platforms
|
|
15
16
|
* (#664 review feedback from @evander-wang).
|
|
@@ -200,13 +201,42 @@ export const findRepo = async (startPath) => {
|
|
|
200
201
|
}
|
|
201
202
|
return null;
|
|
202
203
|
};
|
|
204
|
+
function isReadOnlyFilesystemError(err) {
|
|
205
|
+
const code = err?.code;
|
|
206
|
+
return code === 'EROFS' || code === 'EACCES' || code === 'EPERM';
|
|
207
|
+
}
|
|
203
208
|
/**
|
|
204
209
|
* Keep generated index files ignored without modifying the user's root .gitignore.
|
|
205
210
|
*/
|
|
206
211
|
export const ensureGitNexusIgnored = async (repoPath) => {
|
|
207
212
|
const gitignorePath = path.join(getStoragePath(repoPath), '.gitignore');
|
|
208
|
-
|
|
209
|
-
|
|
213
|
+
const desired = '*\n';
|
|
214
|
+
// Idempotent fast path: skip the write entirely when the file already has
|
|
215
|
+
// the expected content. Lets this run cleanly on read-only mounts (e.g.
|
|
216
|
+
// the documented Docker workflow with WORKSPACE_DIR bound :ro) when an
|
|
217
|
+
// earlier `analyze` already created the file. See issue #1549.
|
|
218
|
+
try {
|
|
219
|
+
if ((await fs.readFile(gitignorePath, 'utf-8')) === desired) {
|
|
220
|
+
await ensureGitInfoExclude(repoPath);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
if (err?.code !== 'ENOENT')
|
|
226
|
+
throw err;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
await fs.mkdir(path.dirname(gitignorePath), { recursive: true });
|
|
230
|
+
await fs.writeFile(gitignorePath, desired, 'utf-8');
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
if (isReadOnlyFilesystemError(err)) {
|
|
234
|
+
logger.warn({ path: gitignorePath, code: err.code }, 'GitNexus storage filesystem is not writable; skipping .gitnexus/.gitignore. Generated files may appear as untracked in this repo locally.');
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
throw err;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
210
240
|
await ensureGitInfoExclude(repoPath);
|
|
211
241
|
};
|
|
212
242
|
const ensureGitInfoExclude = async (repoPath) => {
|
|
@@ -220,7 +250,6 @@ const ensureGitInfoExclude = async (repoPath) => {
|
|
|
220
250
|
catch {
|
|
221
251
|
return;
|
|
222
252
|
}
|
|
223
|
-
await fs.mkdir(path.dirname(excludePath), { recursive: true });
|
|
224
253
|
let content = '';
|
|
225
254
|
try {
|
|
226
255
|
content = await fs.readFile(excludePath, 'utf-8');
|
|
@@ -236,7 +265,18 @@ const ensureGitInfoExclude = async (repoPath) => {
|
|
|
236
265
|
if (excludes.includes(GITNEXUS_DIR) || excludes.includes(GITNEXUS_EXCLUDE_ENTRY))
|
|
237
266
|
return;
|
|
238
267
|
const separator = content.length === 0 || content.endsWith('\n') ? '' : '\n';
|
|
239
|
-
|
|
268
|
+
try {
|
|
269
|
+
await fs.mkdir(path.dirname(excludePath), { recursive: true });
|
|
270
|
+
await fs.writeFile(excludePath, `${content}${separator}${GITNEXUS_EXCLUDE_ENTRY}\n`, 'utf-8');
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
if (isReadOnlyFilesystemError(err)) {
|
|
274
|
+
logger.warn({ path: excludePath, code: err.code }, 'GitNexus storage filesystem is not writable; skipping .git/info/exclude update. .gitnexus/ may appear as untracked in `git status` locally.');
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
throw err;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
240
280
|
};
|
|
241
281
|
// ─── Global Registry (~/.gitnexus/registry.json) ───────────────────────
|
|
242
282
|
/**
|
package/package.json
CHANGED