diff --git a/docs/languages/r-provider.md b/docs/languages/r-provider.md index 923831888..40d601a8c 100644 --- a/docs/languages/r-provider.md +++ b/docs/languages/r-provider.md @@ -28,7 +28,7 @@ Types: Imports: - `library()` and `require()` resolve to local packages, and `source()` to local files. The argument is `package=` / `file=` wherever it sits, else the first unnamed argument after any named one; named-first arguments such as `lib.loc=` and `local=` are not imports. -- NAMESPACE `importFrom()` names bind to definitions in local packages. +- NAMESPACE `importFrom()` names bind to definitions in local packages. A name that can never bind (not exported, not defined at top level, or dotted) creates no import, so its calls keep the `global-name-fallback` guess. Calls: diff --git a/gitnexus/src/core/ingestion/import-resolvers/r.ts b/gitnexus/src/core/ingestion/import-resolvers/r.ts index 73e693e3e..32e33d4f9 100644 --- a/gitnexus/src/core/ingestion/import-resolvers/r.ts +++ b/gitnexus/src/core/ingestion/import-resolvers/r.ts @@ -158,7 +158,7 @@ const stripBackticks = (s: string): string => s.replace(/^`+|`+$/g, ''); * layout where the bare def comes first). `null` degrades the call site to the * baseline `global-name-fallback` guess. Never builds a workspace file index. */ -function resolveRNamedImport( +export function resolveRNamedImport( rConfig: RPackageConfig | null, targetRaw: string, importedName: string, diff --git a/gitnexus/src/core/ingestion/languages/r/namespace-imports.ts b/gitnexus/src/core/ingestion/languages/r/namespace-imports.ts index d404f44a3..ac0d86841 100644 --- a/gitnexus/src/core/ingestion/languages/r/namespace-imports.ts +++ b/gitnexus/src/core/ingestion/languages/r/namespace-imports.ts @@ -42,6 +42,7 @@ import type { ParsedFile, ParsedImport } from 'gitnexus-shared'; import type { RPackageConfig } from './package-config.js'; import { unescapeRString } from './export-pattern.js'; +import { resolveRNamedImport } from '../../import-resolvers/r.js'; /** One `importFrom(pkg, name)` pair, in NAMESPACE file order. */ export interface RNamespaceImportFromEntry { @@ -622,6 +623,11 @@ export function populateRNamespaceImports( const synthesised: Extract[] = []; for (const [name, pkg] of plan.bindable) { if (ownNames?.has(name) === true) continue; + // `null` means the name can never bind. A synthesised import for it would stay unresolved + // yet count as an explicit name claim, suppressing the documented `global-name-fallback`. + // (Importing the resolver forms a cycle with import-resolvers/r.ts; every use is deferred + // to call time, so it is safe at runtime.) + if (resolveRNamedImport(cfg, pkg, name, parsedFiles) === null) continue; synthesised.push({ kind: 'named', localName: name, importedName: name, targetRaw: pkg }); } if (synthesised.length === 0) continue;