@shapeshift-labs/frontier-lang-compiler 0.2.32 → 0.2.34

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/index.js CHANGED
@@ -277,6 +277,24 @@ export const ProjectionTargetLossClasses = Object.freeze([
277
277
  'missingAdapter'
278
278
  ]);
279
279
 
280
+ export const NativeParserFeatureCategories = Object.freeze([
281
+ 'syntax',
282
+ 'semantic',
283
+ 'type',
284
+ 'controlFlow',
285
+ 'macroMetaprogramming',
286
+ 'sourcePreservation'
287
+ ]);
288
+
289
+ export const NativeParserFeatureCoverageStatuses = Object.freeze([
290
+ 'full',
291
+ 'partial',
292
+ 'evidence-required',
293
+ 'missing',
294
+ 'blocked',
295
+ 'not-applicable'
296
+ ]);
297
+
280
298
  export const NativeImportLanguageProfiles = Object.freeze([
281
299
  nativeImportLanguageProfile('javascript', {
282
300
  aliases: ['js', 'mjs', 'cjs', 'jsx'],
@@ -439,6 +457,18 @@ export const NativeParserAstFormatProfiles = Object.freeze([
439
457
  supportsErrorRecovery: true,
440
458
  notes: ['Java compiler/parser ASTs expose package/import/type/member declarations and source ranges; classpath/module-path, bindings, annotation processors, generated sources, Lombok expansion, comments/trivia, and control-flow evidence remain host-owned.']
441
459
  }),
460
+ nativeParserAstFormatProfile('kotlin-psi', {
461
+ aliases: ['kotlin-compiler', 'kotlin-compiler-psi', 'intellij-psi', 'kt-psi', 'kotlin-ast'],
462
+ kind: 'compiler-ast',
463
+ languages: ['kotlin'],
464
+ parserAdapters: ['kotlin-compiler', 'kotlin-psi', 'intellij-psi'],
465
+ exactness: 'exact-parser-ast',
466
+ sourceRangeModel: 'text-range-line-column',
467
+ preservesTokens: true,
468
+ preservesTrivia: true,
469
+ supportsErrorRecovery: true,
470
+ notes: ['Kotlin PSI exposes source syntax and IntelliJ parser errors; Analysis API symbols, FIR/K2 types, expect/actual matching, compiler plugins, generated sources, scripts, and build variants remain host-owned evidence.']
471
+ }),
442
472
  nativeParserAstFormatProfile('roslyn-csharp', {
443
473
  aliases: ['roslyn', 'csharp-roslyn', 'c#-roslyn', 'microsoft-codeanalysis-csharp', 'csharp-syntax'],
444
474
  kind: 'compiler-ast',
@@ -2357,6 +2387,78 @@ export function createNativeParserAstFormatMatrix(input = {}) {
2357
2387
  };
2358
2388
  }
2359
2389
 
2390
+ export function createNativeParserFeatureMatrix(input = {}) {
2391
+ const imports = input.imports ?? [];
2392
+ const adapters = input.adapters ?? [];
2393
+ const profiles = mergeNativeImportProfiles(input.languages ?? NativeImportLanguageProfiles, imports, adapters);
2394
+ const parsers = nativeParserFeatureRowsForProfiles(profiles, {
2395
+ imports,
2396
+ adapters,
2397
+ requiredFeatures: input.requiredFeatures,
2398
+ minimumReadiness: input.minimumReadiness,
2399
+ includeEmptyParsers: input.includeEmptyParsers
2400
+ });
2401
+ const summary = nativeParserFeatureMatrixSummary(parsers);
2402
+ return {
2403
+ kind: 'frontier.lang.nativeParserFeatureMatrix',
2404
+ version: 1,
2405
+ generatedAt: input.generatedAt ?? Date.now(),
2406
+ parsers,
2407
+ languages: summarizeNativeParserFeatureLanguages(profiles, parsers),
2408
+ summary,
2409
+ metadata: {
2410
+ categories: [...NativeParserFeatureCategories],
2411
+ statuses: [...NativeParserFeatureCoverageStatuses],
2412
+ requiredFeatures: normalizeNativeParserRequiredFeatures(input.requiredFeatures),
2413
+ minimumReadiness: normalizeSemanticMergeReadiness(input.minimumReadiness ?? 'ready'),
2414
+ note: 'Native parser feature coverage is admission evidence per language/parser. It does not promote lightweight scans or host adapters beyond their declared and observed capabilities.'
2415
+ }
2416
+ };
2417
+ }
2418
+
2419
+ export function queryNativeParserFeatureMatrix(matrixOrInput = {}, query = {}) {
2420
+ const matrix = matrixOrInput?.kind === 'frontier.lang.nativeParserFeatureMatrix'
2421
+ ? matrixOrInput
2422
+ : createNativeParserFeatureMatrix(matrixOrInput);
2423
+ const language = normalizeNativeLanguageId(query.language);
2424
+ const parser = query.parser === undefined ? undefined : parserAstFormatIdForParser(query.parser);
2425
+ const requiredFeatures = normalizeNativeParserRequiredFeatures(query.requiredFeatures ?? matrix.metadata?.requiredFeatures);
2426
+ const minimumReadiness = normalizeSemanticMergeReadiness(query.minimumReadiness ?? matrix.metadata?.minimumReadiness ?? 'ready');
2427
+ const row = matrix.parsers.find((entry) => {
2428
+ if (language && normalizeNativeLanguageId(entry.language) !== language && !(entry.aliases ?? []).map(normalizeNativeLanguageId).includes(language)) {
2429
+ return false;
2430
+ }
2431
+ if (!parser) return true;
2432
+ const parserIds = [
2433
+ entry.parser,
2434
+ entry.parserFormat,
2435
+ ...(entry.parserAliases ?? []),
2436
+ ...(entry.parserAdapters ?? [])
2437
+ ].map(parserAstFormatIdForParser);
2438
+ return parserIds.includes(parser);
2439
+ });
2440
+ const merge = row
2441
+ ? nativeParserFeatureMergeAssessment(row, { requiredFeatures, minimumReadiness })
2442
+ : {
2443
+ mergeReady: false,
2444
+ readiness: 'blocked',
2445
+ requiredFeatures,
2446
+ minimumReadiness,
2447
+ blockingFeatures: requiredFeatures,
2448
+ reviewFeatures: [],
2449
+ reasons: [`No native parser feature coverage row matched language=${query.language ?? '*'} parser=${query.parser ?? '*'}.`]
2450
+ };
2451
+ return {
2452
+ kind: 'frontier.lang.nativeParserFeatureQuery',
2453
+ version: 1,
2454
+ found: Boolean(row),
2455
+ language: row?.language ?? language,
2456
+ parser: row?.parser ?? parser,
2457
+ row,
2458
+ merge
2459
+ };
2460
+ }
2461
+
2360
2462
  export function createProjectionTargetLossMatrix(input = {}) {
2361
2463
  const imports = input.imports ?? [];
2362
2464
  const adapters = input.adapters ?? [];
@@ -2958,6 +3060,78 @@ export function createJavaAstNativeImporterAdapter(options = {}) {
2958
3060
  };
2959
3061
  }
2960
3062
 
3063
+ export function createKotlinPsiNativeImporterAdapter(options = {}) {
3064
+ return {
3065
+ id: options.id ?? 'frontier.kotlin-psi-native-importer',
3066
+ language: options.language ?? 'kotlin',
3067
+ parser: options.parser ?? 'kotlin-psi',
3068
+ version: options.version,
3069
+ capabilities: uniqueStrings(['nativeAst', 'semanticIndex', 'sourceMaps', 'diagnostics', ...(options.capabilities ?? [])]),
3070
+ coverage: nativeImporterAdapterCoverage({
3071
+ exactness: 'exact-parser-ast',
3072
+ exactAst: true,
3073
+ tokens: true,
3074
+ trivia: true,
3075
+ diagnostics: true,
3076
+ sourceRanges: true,
3077
+ generatedRanges: false,
3078
+ semanticCoverage: declarationSemanticCoverage(),
3079
+ notes: [
3080
+ 'Normalizes caller-owned Kotlin PSI/KtFile-shaped syntax trees into native AST nodes and declaration-level semantic index records.',
3081
+ 'Kotlin PSI imports do not resolve Analysis API symbols, FIR/K2 types, overloads, nullability flow, expect/actual matching, compiler-plugin generated declarations, KSP/KAPT output, scripts, build variants, or control flow by themselves; attach host evidence for those claims.'
3082
+ ]
3083
+ }, options.coverage),
3084
+ supportedExtensions: options.supportedExtensions ?? ['.kt', '.kts'],
3085
+ diagnostics: options.diagnostics,
3086
+ parse(input) {
3087
+ const parsed = input.options?.ast
3088
+ ?? input.options?.nativeAst
3089
+ ?? input.options?.ktFile
3090
+ ?? input.options?.file
3091
+ ?? input.options?.sourceFile
3092
+ ?? input.options?.root
3093
+ ?? options.ast
3094
+ ?? options.ktFile
3095
+ ?? options.file
3096
+ ?? options.sourceFile
3097
+ ?? options.root
3098
+ ?? parseKotlinPsiSource(input, options);
3099
+ const root = kotlinPsiRoot(parsed);
3100
+ if (!root) {
3101
+ return missingInjectedParserResult(input, {
3102
+ parser: options.parser ?? 'kotlin-psi',
3103
+ adapterId: options.id ?? 'frontier.kotlin-psi-native-importer',
3104
+ message: 'createKotlinPsiNativeImporterAdapter requires an injected Kotlin PSI KtFile-shaped object, parserModule.parse function, parse function, or adapterOptions.ast.'
3105
+ });
3106
+ }
3107
+ const parseDiagnostics = normalizeParserErrors(parsed?.errors ?? parsed?.diagnostics ?? parsed?.parseDiagnostics, input, {
3108
+ parser: options.parser ?? 'kotlin-psi'
3109
+ });
3110
+ return createNativeImportFromKotlinPsi(root, input, {
3111
+ parser: options.parser ?? 'kotlin-psi',
3112
+ astFormat: 'kotlin-psi',
3113
+ maxNodes: options.maxNodes,
3114
+ diagnostics: parseDiagnostics,
3115
+ kotlinVersion: options.kotlinVersion ?? input.options?.kotlinVersion ?? parsed?.kotlinVersion,
3116
+ languageVersion: options.languageVersion ?? input.options?.languageVersion ?? parsed?.languageVersion,
3117
+ apiVersion: options.apiVersion ?? input.options?.apiVersion ?? parsed?.apiVersion,
3118
+ script: input.options?.script ?? options.script ?? parsed?.script ?? /\.kts$/i.test(input.sourcePath ?? ''),
3119
+ generated: input.options?.generated ?? options.generated ?? parsed?.generated ?? kotlinGeneratedSourcePath(input.sourcePath),
3120
+ analysisApiEvidence: input.options?.analysisApiEvidence ?? options.analysisApiEvidence ?? parsed?.analysisApiEvidence,
3121
+ firEvidence: input.options?.firEvidence ?? options.firEvidence ?? parsed?.firEvidence,
3122
+ compilerPluginEvidence: input.options?.compilerPluginEvidence ?? options.compilerPluginEvidence ?? parsed?.compilerPluginEvidence,
3123
+ kspEvidence: input.options?.kspEvidence ?? options.kspEvidence ?? parsed?.kspEvidence,
3124
+ kaptEvidence: input.options?.kaptEvidence ?? options.kaptEvidence ?? parsed?.kaptEvidence,
3125
+ multiplatformEvidence: input.options?.multiplatformEvidence ?? options.multiplatformEvidence ?? parsed?.multiplatformEvidence,
3126
+ buildVariantEvidence: input.options?.buildVariantEvidence ?? options.buildVariantEvidence ?? parsed?.buildVariantEvidence,
3127
+ positionResolver: input.options?.positionResolver ?? options.positionResolver,
3128
+ lineMap: input.options?.lineMap ?? options.lineMap ?? parsed?.lineMap,
3129
+ includeAnnotations: options.includeAnnotations ?? input.options?.includeAnnotations
3130
+ });
3131
+ }
3132
+ };
3133
+ }
3134
+
2961
3135
  export function createCSharpRoslynNativeImporterAdapter(options = {}) {
2962
3136
  return {
2963
3137
  id: options.id ?? 'frontier.csharp-roslyn-native-importer',
@@ -7439,136 +7613,688 @@ function normalizeParserAstFormatId(format) {
7439
7613
  return String(format ?? '').trim().toLowerCase().replace(/[_\s]+/g, '-');
7440
7614
  }
7441
7615
 
7442
- function mergeNativeParserAstFormatProfiles(profiles, imports, adapters) {
7443
- const byId = new Map((profiles ?? []).map((profile) => [normalizeParserAstFormatId(profile.id ?? profile), nativeParserAstFormatProfile(normalizeParserAstFormatId(profile.id ?? profile), profile)]));
7444
- for (const adapter of adapters ?? []) {
7445
- const summary = safeNativeImporterAdapterSummary(adapter);
7446
- if (!summary) continue;
7447
- const formatId = parserAstFormatIdForParser(summary.parser);
7448
- if (!byId.has(formatId)) {
7449
- byId.set(formatId, nativeParserAstFormatProfile(formatId, {
7450
- languages: [summary.language],
7451
- parserAdapters: [summary.parser],
7452
- exactness: summary.coverage.exactness,
7453
- sourceRangeModel: summary.coverage.sourceRanges ? 'adapter-reported' : 'unknown'
7454
- }));
7455
- }
7456
- }
7457
- for (const imported of imports ?? []) {
7458
- const formatId = parserAstFormatIdForImport(imported);
7459
- if (formatId && !byId.has(formatId)) {
7460
- byId.set(formatId, nativeParserAstFormatProfile(formatId, {
7461
- languages: [imported.language].filter(Boolean),
7462
- parserAdapters: [imported.parser ?? imported.nativeAst?.parser ?? formatId],
7463
- exactness: 'unknown',
7464
- sourceRangeModel: (imported.sourceMaps ?? []).some((sourceMap) => sourceMap.mappings?.some((mapping) => mapping.sourceSpan)) ? 'adapter-reported' : 'unknown'
7465
- }));
7616
+ const nativeParserMacroMetaprogrammingLossKinds = new Set([
7617
+ 'macroExpansion',
7618
+ 'macroHygiene',
7619
+ 'preprocessor',
7620
+ 'conditionalCompilation',
7621
+ 'metaprogramming',
7622
+ 'reflection',
7623
+ 'generatedCode'
7624
+ ]);
7625
+
7626
+ const nativeParserTypeCoverageLossKinds = new Set([
7627
+ 'typeInference',
7628
+ 'overloadResolution',
7629
+ 'overloadTypeInference',
7630
+ 'unsupportedSemantic'
7631
+ ]);
7632
+
7633
+ function nativeParserFeatureRowsForProfiles(profiles, context) {
7634
+ const rows = [];
7635
+ for (const profile of profiles) {
7636
+ const matchingImports = nativeParserFeatureImportsForProfile(profile, context.imports);
7637
+ const matchingAdapters = nativeParserFeatureAdapterSummariesForProfile(profile, context.adapters);
7638
+ for (const parser of nativeParserFeatureParserSlots(profile, matchingImports, matchingAdapters)) {
7639
+ const row = nativeParserFeatureRowForParser(profile, parser, {
7640
+ ...context,
7641
+ imports: matchingImports.filter((imported) => nativeParserFeatureParserMatches(nativeParserParserForImport(imported), parser)),
7642
+ adapters: matchingAdapters.filter((adapter) => nativeParserFeatureParserMatches(adapter.parser, parser))
7643
+ });
7644
+ if (context.includeEmptyParsers === false && row.imports.total === 0 && row.adapters.total === 0) continue;
7645
+ rows.push(row);
7466
7646
  }
7467
7647
  }
7468
- return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
7648
+ return rows.sort((left, right) => {
7649
+ const languageOrder = left.language.localeCompare(right.language);
7650
+ return languageOrder || left.parser.localeCompare(right.parser);
7651
+ });
7469
7652
  }
7470
7653
 
7471
- function nativeParserAstFormatCoverageForProfile(profile, imports, adapters) {
7472
- const formatIds = new Set([profile.id, ...profile.aliases].map(normalizeParserAstFormatId));
7473
- const adapterParsers = new Set(profile.parserAdapters.map(parserAstFormatIdForParser));
7474
- const matchingAdapters = (adapters ?? [])
7475
- .map((adapter) => safeNativeImporterAdapterSummary(adapter))
7476
- .filter(Boolean)
7477
- .filter((adapter) => formatIds.has(parserAstFormatIdForParser(adapter.parser)) || adapterParsers.has(parserAstFormatIdForParser(adapter.parser)));
7478
- const matchingImports = (imports ?? [])
7479
- .filter((imported) => {
7480
- const formatId = parserAstFormatIdForImport(imported);
7481
- return formatId && (formatIds.has(formatId) || adapterParsers.has(formatId));
7482
- });
7483
- const effectiveCapabilities = {};
7484
- for (const adapter of matchingAdapters) {
7485
- for (const row of adapter.coverage.capabilityEvidence?.capabilities ?? []) {
7486
- if (row.effective) effectiveCapabilities[row.capability] = (effectiveCapabilities[row.capability] ?? 0) + 1;
7487
- }
7488
- }
7489
- const readiness = matchingImports.reduce(
7490
- (current, imported) => maxSemanticMergeReadiness(current, nativeImportReadiness(imported)),
7491
- matchingImports.length ? 'ready' : 'needs-review'
7492
- );
7493
- return {
7494
- id: profile.id,
7495
- kind: profile.kind,
7496
- languages: profile.languages,
7497
- parserAdapters: profile.parserAdapters,
7498
- exactness: profile.exactness,
7499
- sourceRangeModel: profile.sourceRangeModel,
7500
- preservesTokens: profile.preservesTokens,
7501
- preservesTrivia: profile.preservesTrivia,
7502
- supportsIncremental: profile.supportsIncremental,
7503
- supportsErrorRecovery: profile.supportsErrorRecovery,
7504
- notes: profile.notes,
7654
+ function nativeParserFeatureRowForParser(profile, parser, context) {
7655
+ const imports = context.imports ?? [];
7656
+ const adapters = context.adapters ?? [];
7657
+ const parserFormat = parserAstFormatIdForParser(parser);
7658
+ const parserProfile = getNativeParserAstFormatProfile(parserFormat);
7659
+ const adapterCoverage = summarizeNativeImporterAdapterCoverageEntries([
7660
+ ...imports.map((imported) => nativeImporterAdapterCoverageEntryFromImport(imported)).filter(Boolean),
7661
+ ...adapters.map((adapter) => ({
7662
+ adapterId: adapter.id,
7663
+ language: adapter.language,
7664
+ parser: adapter.parser,
7665
+ coverage: adapter.coverage
7666
+ }))
7667
+ ]);
7668
+ const losses = imports.flatMap((imported) => imported?.losses ?? imported?.nativeAst?.losses ?? []);
7669
+ const evidence = imports.flatMap((imported) => imported?.evidence ?? []);
7670
+ const lossSummary = summarizeNativeImportLosses(losses, { evidence, parser });
7671
+ const semanticEvidence = nativeParserFeatureSemanticEvidence(imports);
7672
+ const sourceMaps = imports.flatMap((imported) => imported?.sourceMaps ?? imported?.universalAst?.sourceMaps ?? []);
7673
+ const sourceMapMappings = sourceMaps.reduce((sum, sourceMap) => sum + (sourceMap?.mappings?.length ?? 0), 0);
7674
+ const sourcePreservation = summarizeImportSourcePreservation(undefined, imports);
7675
+ const nativeAstNodes = imports.reduce((sum, imported) => sum + Object.keys(imported?.nativeAst?.nodes ?? imported?.nativeSource?.ast?.nodes ?? {}).length, 0);
7676
+ const featureContext = {
7677
+ profile,
7678
+ parser,
7679
+ parserFormat,
7680
+ parserProfile,
7681
+ imports,
7682
+ adapters,
7683
+ adapterCoverage,
7684
+ losses,
7685
+ evidence,
7686
+ lossSummary,
7687
+ semanticEvidence,
7688
+ sourceMaps,
7689
+ sourceMapMappings,
7690
+ sourcePreservation,
7691
+ nativeAstNodes
7692
+ };
7693
+ const features = {
7694
+ syntax: nativeParserSyntaxFeature(featureContext),
7695
+ semantic: nativeParserSemanticFeature(featureContext),
7696
+ type: nativeParserTypeFeature(featureContext),
7697
+ controlFlow: nativeParserControlFlowFeature(featureContext),
7698
+ macroMetaprogramming: nativeParserMacroMetaprogrammingFeature(featureContext),
7699
+ sourcePreservation: nativeParserSourcePreservationFeature(featureContext)
7700
+ };
7701
+ const importReadiness = imports.length
7702
+ ? lossSummary.semanticMergeReadiness
7703
+ : adapters.length ? 'needs-review' : normalizeSemanticMergeReadiness(profile.defaultReadiness) ?? 'needs-review';
7704
+ const row = {
7705
+ language: profile.language,
7706
+ aliases: profile.aliases,
7707
+ parser,
7708
+ parserFormat,
7709
+ parserAliases: uniqueStrings([...(parserProfile?.aliases ?? []), ...(parserProfile?.parserAdapters ?? [])]),
7710
+ parserAdapters: uniqueStrings([parser, ...(parserProfile?.parserAdapters ?? [])]),
7711
+ extensions: profile.extensions,
7712
+ supportsLightweightScan: profile.supportsLightweightScan,
7713
+ projectionTargets: profile.projectionTargets,
7714
+ knownLossKinds: uniqueStrings([...(profile.knownLossKinds ?? []), ...Object.keys(lossSummary.byKind)]),
7715
+ defaultReadiness: profile.defaultReadiness,
7716
+ notes: uniqueStrings([...(profile.notes ?? []), ...(parserProfile?.notes ?? [])]),
7505
7717
  adapters: {
7506
- total: matchingAdapters.length,
7507
- ids: matchingAdapters.map((adapter) => adapter.id),
7508
- parsers: uniqueStrings(matchingAdapters.map((adapter) => adapter.parser)),
7509
- effectiveCapabilities
7718
+ total: adapters.length,
7719
+ ids: adapters.map((adapter) => adapter.id),
7720
+ versions: uniqueStrings(adapters.map((adapter) => adapter.version).filter(Boolean)),
7721
+ exactness: uniqueStrings(adapters.map((adapter) => adapter.coverage?.exactness).filter(Boolean)),
7722
+ coverage: adapterCoverage
7510
7723
  },
7511
7724
  imports: {
7512
- total: matchingImports.length,
7513
- sourcePaths: matchingImports.map((imported) => imported.sourcePath).filter(Boolean),
7514
- readiness,
7515
- nativeAstNodes: matchingImports.reduce((sum, imported) => sum + Object.keys(imported.nativeAst?.nodes ?? {}).length, 0),
7516
- symbols: matchingImports.reduce((sum, imported) => sum + (imported.semanticIndex?.symbols?.length ?? 0), 0),
7517
- sourceMapMappings: matchingImports.reduce((sum, imported) => sum + (imported.sourceMaps ?? []).reduce((mapSum, sourceMap) => mapSum + (sourceMap.mappings?.length ?? 0), 0), 0),
7518
- losses: matchingImports.reduce((sum, imported) => sum + (imported.losses?.length ?? 0), 0)
7519
- }
7725
+ total: imports.length,
7726
+ sourcePaths: uniqueStrings(imports.map((imported) => imported?.sourcePath ?? imported?.nativeSource?.sourcePath ?? imported?.nativeAst?.sourcePath).filter(Boolean)),
7727
+ readiness: importReadiness,
7728
+ readinessReasons: imports.length ? lossSummary.readinessReasons : nativeImportCoverageReasons(profile),
7729
+ nativeAstNodes,
7730
+ symbols: semanticEvidence.symbols,
7731
+ references: semanticEvidence.references,
7732
+ types: semanticEvidence.types,
7733
+ controlFlow: semanticEvidence.controlFlow,
7734
+ sourceMaps: sourceMaps.length,
7735
+ sourceMapMappings,
7736
+ losses: lossSummary.total,
7737
+ lossKinds: lossSummary.byKind,
7738
+ lossCategories: lossSummary.categories,
7739
+ sourcePreservation
7740
+ },
7741
+ features
7742
+ };
7743
+ return {
7744
+ ...row,
7745
+ merge: nativeParserFeatureMergeAssessment(row, {
7746
+ requiredFeatures: context.requiredFeatures,
7747
+ minimumReadiness: context.minimumReadiness
7748
+ })
7520
7749
  };
7521
7750
  }
7522
7751
 
7523
- function parserAstFormatIdForParser(parser) {
7524
- const text = normalizeParserAstFormatId(parser);
7525
- if (text.includes('typescript')) return 'typescript-compiler-api';
7526
- if (text.includes('python') && text.includes('ast')) return 'python-ast';
7527
- if (text === 'syn' || text.includes('rust-syn')) return 'rust-syn';
7528
- if (text.includes('rust-analyzer') || text.includes('rowan')) return 'rust-analyzer-rowan';
7529
- if (text.includes('clang') || text.includes('libclang')) return 'clang-ast-json';
7530
- if (text === 'go' || text.includes('go-parser') || text.includes('go-ast') || text.includes('go/parser') || text.includes('go/ast')) return 'go-ast';
7531
- if (text === 'java' || text.includes('javac') || text.includes('jdt') || text.includes('javaparser') || text.includes('java-parser') || text.includes('java-ast')) return 'java-ast';
7532
- if (text === 'csharp' || text === 'c#' || text === 'cs' || text.includes('roslyn') || text.includes('microsoft-codeanalysis-csharp') || text.includes('csharp-syntax')) return 'roslyn-csharp';
7533
- if (text.includes('swift-syntax') || text.includes('swiftsyntax') || text.includes('swiftparser') || text.includes('swift-parser')) return 'swift-syntax';
7534
- if (text.includes('tree-sitter') || text.includes('treesitter')) return 'tree-sitter';
7535
- if (text.includes('babel')) return 'babel';
7536
- if (text.includes('estree')) return 'estree';
7537
- if (text.includes('libcst')) return 'libcst';
7538
- if (text.includes('scip')) return 'scip';
7539
- if (text.includes('lsif')) return 'lsif';
7540
- return text || 'unknown';
7752
+ function nativeParserSyntaxFeature(context) {
7753
+ const blockingSyntaxLosses = context.losses.filter((loss) => loss.severity === 'error' && (loss.kind === 'unsupportedSyntax' || loss.kind === 'parserDiagnostic'));
7754
+ const exactAst = context.adapterCoverage.effective.exactAst ?? 0;
7755
+ const sourceRanges = context.adapterCoverage.effective.sourceRanges ?? 0;
7756
+ const parserDiagnostics = context.adapterCoverage.effective.parserDiagnostics ?? 0;
7757
+ let status = 'missing';
7758
+ const reasons = [];
7759
+ if (blockingSyntaxLosses.length) {
7760
+ status = 'blocked';
7761
+ reasons.push('Parser diagnostics or unsupported syntax errors block syntax coverage.');
7762
+ } else if (exactAst > 0 && (sourceRanges > 0 || context.sourceMapMappings > 0)) {
7763
+ status = 'full';
7764
+ reasons.push('Exact parser AST and source-range evidence are available.');
7765
+ } else if (exactAst > 0 || sourceRanges > 0 || context.nativeAstNodes > 1 || context.sourceMapMappings > 0) {
7766
+ status = 'partial';
7767
+ reasons.push('Syntax evidence exists, but exact AST/source-range coverage is incomplete.');
7768
+ } else if (context.adapters.length || context.parserProfile) {
7769
+ status = 'evidence-required';
7770
+ reasons.push('Parser slot is declared, but no observed syntax import evidence is attached.');
7771
+ } else {
7772
+ reasons.push('No syntax parser coverage is declared or observed.');
7773
+ }
7774
+ return nativeParserFeatureCoverage('syntax', status, {
7775
+ capabilities: {
7776
+ exactAst,
7777
+ sourceRanges,
7778
+ parserDiagnostics,
7779
+ nativeAstNodes: context.nativeAstNodes,
7780
+ sourceMapMappings: context.sourceMapMappings
7781
+ },
7782
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['exactAst', 'sourceRanges', 'parserDiagnostics']),
7783
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, ['unsupportedSyntax', 'parserDiagnostic']),
7784
+ reasons,
7785
+ notes: ['Syntax coverage covers parser AST/CST structure, diagnostics, source ranges, and source-map anchors.']
7786
+ });
7541
7787
  }
7542
7788
 
7543
- function parserAstFormatIdForImport(imported) {
7544
- return parserAstFormatIdForParser(
7545
- imported?.metadata?.adapter?.parser
7546
- ?? imported?.metadata?.astFormat
7547
- ?? imported?.nativeAst?.metadata?.astFormat
7548
- ?? imported?.nativeAst?.parser
7549
- ?? imported?.parser
7550
- ?? imported?.metadata?.parser
7551
- );
7789
+ function nativeParserSemanticFeature(context) {
7790
+ const declarations = (context.adapterCoverage.effective.semanticDeclarations ?? 0) + context.semanticEvidence.declarations;
7791
+ const symbols = (context.adapterCoverage.effective.semanticSymbols ?? 0) + context.semanticEvidence.symbols;
7792
+ let status = 'missing';
7793
+ const reasons = [];
7794
+ if (symbols > 0 && declarations > 0) {
7795
+ status = 'full';
7796
+ reasons.push('Declaration and symbol evidence are available.');
7797
+ } else if (symbols > 0 || declarations > 0 || context.nativeAstNodes > 1) {
7798
+ status = 'partial';
7799
+ reasons.push('Semantic evidence is present, but declaration/symbol coverage is incomplete.');
7800
+ } else if (context.adapters.length || context.imports.length) {
7801
+ status = 'evidence-required';
7802
+ reasons.push('Import evidence exists, but no semantic declarations or symbols were observed.');
7803
+ } else {
7804
+ reasons.push('No semantic index evidence is declared or observed.');
7805
+ }
7806
+ return nativeParserFeatureCoverage('semantic', status, {
7807
+ capabilities: {
7808
+ declarations,
7809
+ symbols,
7810
+ semanticIndexLevel: nativeParserFeatureSemanticLevel(context.adapterCoverage, context.semanticEvidence)
7811
+ },
7812
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['semanticDeclarations', 'semanticSymbols']),
7813
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, ['partialSemanticIndex', 'unsupportedSemantic']),
7814
+ reasons,
7815
+ notes: ['Semantic coverage covers declaration and symbol evidence. References, types, and control flow are reported separately.']
7816
+ });
7552
7817
  }
7553
7818
 
7554
- function mergeNativeImportProfiles(languages, imports, adapters, targetAdapters = []) {
7555
- const profilesByLanguage = new Map();
7556
- for (const profile of languages) {
7557
- const normalized = normalizeNativeLanguageId(profile.language ?? profile);
7558
- profilesByLanguage.set(normalized, normalizeNativeImportLanguageProfile(profile, normalized));
7559
- }
7560
- for (const imported of imports) {
7561
- const normalized = normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language);
7562
- if (!normalized || profilesByLanguage.has(normalized)) continue;
7563
- profilesByLanguage.set(normalized, nativeImportLanguageProfile(normalized, {
7564
- supportsLightweightScan: false,
7565
- parserAdapters: [],
7566
- defaultReadiness: 'blocked',
7567
- lossKinds: ['unsupportedSyntax'],
7568
- notes: ['language appeared in import evidence but has no declared Frontier coverage profile']
7569
- }));
7819
+ function nativeParserTypeFeature(context) {
7820
+ const types = (context.adapterCoverage.effective.types ?? 0) + context.semanticEvidence.types;
7821
+ const typeLossKinds = nativeParserFeaturePresentLossKinds(context, nativeParserTypeCoverageLossKinds);
7822
+ let status = 'missing';
7823
+ const reasons = [];
7824
+ if (types > 0) {
7825
+ status = 'full';
7826
+ reasons.push('Resolved or declared type evidence is available.');
7827
+ } else if (typeLossKinds.length > 0 || context.semanticEvidence.symbols > 0) {
7828
+ status = 'evidence-required';
7829
+ reasons.push('Type-sensitive coverage needs compiler or language-server evidence.');
7830
+ } else {
7831
+ reasons.push('No type evidence is declared or observed.');
7570
7832
  }
7571
- for (const adapter of adapters) {
7833
+ return nativeParserFeatureCoverage('type', status, {
7834
+ capabilities: { types },
7835
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['types']),
7836
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, [...nativeParserTypeCoverageLossKinds]),
7837
+ reasons,
7838
+ notes: ['Type coverage covers declared/inferred type facts and overload or inference evidence.']
7839
+ });
7840
+ }
7841
+
7842
+ function nativeParserControlFlowFeature(context) {
7843
+ const controlFlow = (context.adapterCoverage.effective.controlFlow ?? 0) + context.semanticEvidence.controlFlow;
7844
+ let status = 'missing';
7845
+ const reasons = [];
7846
+ if (controlFlow > 0) {
7847
+ status = 'full';
7848
+ reasons.push('Control-flow or CFG evidence is available.');
7849
+ } else if (context.imports.length || context.adapters.length) {
7850
+ status = 'evidence-required';
7851
+ reasons.push('Control-flow evidence was not observed for this parser row.');
7852
+ } else {
7853
+ reasons.push('No control-flow evidence is declared or observed.');
7854
+ }
7855
+ return nativeParserFeatureCoverage('controlFlow', status, {
7856
+ capabilities: { controlFlow },
7857
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['controlFlow']),
7858
+ lossKinds: {},
7859
+ reasons,
7860
+ notes: ['Control-flow coverage covers call/branch/CFG facts supplied by host parsers or semantic indexers.']
7861
+ });
7862
+ }
7863
+
7864
+ function nativeParserMacroMetaprogrammingFeature(context) {
7865
+ const macroLossKinds = nativeParserFeaturePresentLossKinds(context, nativeParserMacroMetaprogrammingLossKinds);
7866
+ const macroLosses = context.losses.filter((loss) => nativeParserMacroMetaprogrammingLossKinds.has(loss.kind));
7867
+ const featureEvidence = summarizeNativeImportFeatureEvidence(macroLosses, { evidence: context.evidence });
7868
+ const generatedRanges = context.adapterCoverage.effective.generatedRanges ?? 0;
7869
+ let status = 'not-applicable';
7870
+ const reasons = [];
7871
+ if (!macroLossKinds.length) {
7872
+ reasons.push('No macro, preprocessor, generator, or metaprogramming coverage risk is declared for this parser row.');
7873
+ } else if (macroLosses.some((loss) => loss.severity === 'error')) {
7874
+ status = 'blocked';
7875
+ reasons.push('Macro or metaprogramming evidence includes blocking loss records.');
7876
+ } else if (featureEvidence.missingRequiredEvidence.length > 0 || generatedRanges === 0) {
7877
+ status = 'evidence-required';
7878
+ reasons.push('Macro/metaprogramming coverage requires generated-range and policy evidence before merge admission.');
7879
+ } else {
7880
+ status = 'partial';
7881
+ reasons.push('Macro/metaprogramming risk has attached evidence, but this facade still treats generated behavior as review-required.');
7882
+ }
7883
+ return nativeParserFeatureCoverage('macroMetaprogramming', status, {
7884
+ capabilities: {
7885
+ generatedRanges,
7886
+ policyKinds: featureEvidence.policyKinds,
7887
+ highestRisk: featureEvidence.highestRisk
7888
+ },
7889
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['generatedRanges']),
7890
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, [...nativeParserMacroMetaprogrammingLossKinds]),
7891
+ reasons: uniqueStrings([...reasons, ...featureEvidence.reasons]),
7892
+ notes: ['Macro/metaprogramming coverage covers macros, preprocessors, generated code, reflection, and conditional compilation evidence.']
7893
+ });
7894
+ }
7895
+
7896
+ function nativeParserSourcePreservationFeature(context) {
7897
+ const exactSource = context.sourcePreservation.exactSourceAvailable;
7898
+ const tokens = context.sourcePreservation.tokens + (context.adapterCoverage.effective.tokens ?? 0);
7899
+ const trivia = context.sourcePreservation.trivia + (context.adapterCoverage.effective.trivia ?? 0);
7900
+ const sourceRanges = context.adapterCoverage.effective.sourceRanges ?? 0;
7901
+ let status = 'missing';
7902
+ const reasons = [];
7903
+ if (exactSource > 0 && (tokens > 0 || trivia > 0 || sourceRanges > 0)) {
7904
+ status = 'full';
7905
+ reasons.push('Exact source text and token/trivia or source-range evidence are available.');
7906
+ } else if (exactSource > 0 || tokens > 0 || trivia > 0 || sourceRanges > 0) {
7907
+ status = 'partial';
7908
+ reasons.push('Source-preservation evidence exists, but exact source, tokens, trivia, or ranges are incomplete.');
7909
+ } else if (context.imports.length || context.adapters.length) {
7910
+ status = 'evidence-required';
7911
+ reasons.push('Import or adapter evidence exists, but no exact source-preservation record was observed.');
7912
+ } else {
7913
+ reasons.push('No source-preservation evidence is declared or observed.');
7914
+ }
7915
+ return nativeParserFeatureCoverage('sourcePreservation', status, {
7916
+ capabilities: {
7917
+ exactSourceAvailable: exactSource,
7918
+ tokens,
7919
+ trivia,
7920
+ comments: context.sourcePreservation.comments,
7921
+ whitespace: context.sourcePreservation.whitespace,
7922
+ directives: context.sourcePreservation.directives,
7923
+ sourceRanges
7924
+ },
7925
+ gaps: nativeParserFeatureCapabilityGaps(context.adapterCoverage, ['tokens', 'trivia', 'sourceRanges']),
7926
+ lossKinds: nativeParserFeatureLossKindCounts(context.losses, ['sourcePreservation', 'commentsTrivia', 'sourceMapApproximation']),
7927
+ reasons,
7928
+ notes: ['Source-preservation coverage covers exact source text, token/trivia retention, comments, whitespace, directives, and source ranges.']
7929
+ });
7930
+ }
7931
+
7932
+ function nativeParserFeatureCoverage(category, status, input = {}) {
7933
+ const normalizedStatus = NativeParserFeatureCoverageStatuses.includes(status) ? status : 'missing';
7934
+ return Object.freeze({
7935
+ category,
7936
+ status: normalizedStatus,
7937
+ readiness: nativeParserFeatureReadinessForStatus(normalizedStatus),
7938
+ mergeReady: nativeParserFeatureStatusMergeReady(normalizedStatus),
7939
+ supported: normalizedStatus === 'full' || normalizedStatus === 'partial' || normalizedStatus === 'not-applicable',
7940
+ capabilities: Object.freeze(input.capabilities ?? {}),
7941
+ gaps: Object.freeze(uniqueStrings(input.gaps ?? [])),
7942
+ lossKinds: Object.freeze(input.lossKinds ?? {}),
7943
+ reasons: Object.freeze(uniqueStrings(input.reasons ?? [])),
7944
+ notes: Object.freeze(uniqueStrings(input.notes ?? []))
7945
+ });
7946
+ }
7947
+
7948
+ function nativeParserFeatureReadinessForStatus(status) {
7949
+ if (status === 'full' || status === 'not-applicable') return 'ready';
7950
+ if (status === 'partial') return 'ready-with-losses';
7951
+ if (status === 'blocked') return 'blocked';
7952
+ return 'needs-review';
7953
+ }
7954
+
7955
+ function nativeParserFeatureStatusMergeReady(status) {
7956
+ return status === 'full' || status === 'not-applicable';
7957
+ }
7958
+
7959
+ function nativeParserFeatureMergeAssessment(row, input = {}) {
7960
+ const requiredFeatures = normalizeNativeParserRequiredFeatures(input.requiredFeatures);
7961
+ const minimumReadiness = normalizeSemanticMergeReadiness(input.minimumReadiness ?? 'ready') ?? 'ready';
7962
+ const featureReadiness = requiredFeatures.reduce(
7963
+ (current, category) => maxSemanticMergeReadiness(current, row.features?.[category]?.readiness ?? 'blocked'),
7964
+ 'ready'
7965
+ );
7966
+ const readiness = maxSemanticMergeReadiness(row.imports?.readiness ?? 'needs-review', featureReadiness);
7967
+ const blockingFeatures = requiredFeatures.filter((category) => !nativeParserFeatureStatusMergeReady(row.features?.[category]?.status));
7968
+ const reviewFeatures = requiredFeatures.filter((category) => {
7969
+ const featureReadiness = row.features?.[category]?.readiness ?? 'blocked';
7970
+ return semanticMergeReadinessRank[featureReadiness] > semanticMergeReadinessRank.ready
7971
+ && semanticMergeReadinessRank[featureReadiness] <= semanticMergeReadinessRank['needs-review'];
7972
+ });
7973
+ const reasons = [];
7974
+ if ((row.imports?.total ?? 0) === 0) reasons.push('No native import evidence matched this language/parser row.');
7975
+ for (const category of blockingFeatures) {
7976
+ const feature = row.features?.[category];
7977
+ reasons.push(`${category} coverage is ${feature?.status ?? 'missing'}: ${(feature?.reasons ?? []).join(' ')}`);
7978
+ }
7979
+ if (semanticMergeReadinessRank[readiness] > semanticMergeReadinessRank[minimumReadiness]) {
7980
+ reasons.push(`Readiness ${readiness} is weaker than required threshold ${minimumReadiness}.`);
7981
+ }
7982
+ const mergeReady = (row.imports?.total ?? 0) > 0
7983
+ && blockingFeatures.length === 0
7984
+ && semanticMergeReadinessRank[readiness] <= semanticMergeReadinessRank[minimumReadiness];
7985
+ if (mergeReady) reasons.push(`Native import is merge-ready for required features: ${requiredFeatures.join(', ')}.`);
7986
+ return Object.freeze({
7987
+ mergeReady,
7988
+ readiness,
7989
+ requiredFeatures,
7990
+ minimumReadiness,
7991
+ blockingFeatures,
7992
+ reviewFeatures,
7993
+ reasons: uniqueStrings(reasons)
7994
+ });
7995
+ }
7996
+
7997
+ function normalizeNativeParserRequiredFeatures(value) {
7998
+ const requested = normalizeStringList(value);
7999
+ const features = requested.length ? requested : ['syntax', 'semantic', 'sourcePreservation'];
8000
+ return uniqueStrings(features.map(normalizeNativeParserFeatureCategory).filter((feature) => NativeParserFeatureCategories.includes(feature)));
8001
+ }
8002
+
8003
+ function normalizeNativeParserFeatureCategory(value) {
8004
+ const normalized = String(value ?? '').trim().replace(/[-_\s]+([a-zA-Z])/g, (_, letter) => letter.toUpperCase());
8005
+ if (normalized.toLowerCase() === 'macrometaprogramming' || normalized.toLowerCase() === 'macro') return 'macroMetaprogramming';
8006
+ if (normalized.toLowerCase() === 'controlflow' || normalized.toLowerCase() === 'cfg') return 'controlFlow';
8007
+ if (normalized.toLowerCase() === 'sourcepreservation' || normalized.toLowerCase() === 'source') return 'sourcePreservation';
8008
+ if (normalized.toLowerCase() === 'types') return 'type';
8009
+ return normalized;
8010
+ }
8011
+
8012
+ function nativeParserFeatureMatrixSummary(rows) {
8013
+ const summary = {
8014
+ languages: new Set(),
8015
+ parsers: rows.length,
8016
+ imports: 0,
8017
+ adapters: 0,
8018
+ mergeReady: 0,
8019
+ byReadiness: {},
8020
+ byFeatureStatus: {},
8021
+ byFeatureReadiness: {}
8022
+ };
8023
+ for (const row of rows) {
8024
+ summary.languages.add(row.language);
8025
+ summary.imports += row.imports.total;
8026
+ summary.adapters += row.adapters.total;
8027
+ if (row.merge.mergeReady) summary.mergeReady += 1;
8028
+ summary.byReadiness[row.merge.readiness] = (summary.byReadiness[row.merge.readiness] ?? 0) + 1;
8029
+ for (const [category, feature] of Object.entries(row.features)) {
8030
+ summary.byFeatureStatus[category] ??= {};
8031
+ summary.byFeatureReadiness[category] ??= {};
8032
+ summary.byFeatureStatus[category][feature.status] = (summary.byFeatureStatus[category][feature.status] ?? 0) + 1;
8033
+ summary.byFeatureReadiness[category][feature.readiness] = (summary.byFeatureReadiness[category][feature.readiness] ?? 0) + 1;
8034
+ }
8035
+ }
8036
+ return {
8037
+ ...summary,
8038
+ languages: summary.languages.size
8039
+ };
8040
+ }
8041
+
8042
+ function summarizeNativeParserFeatureLanguages(profiles, rows) {
8043
+ return profiles.map((profile) => {
8044
+ const languageRows = rows.filter((row) => row.language === profile.language);
8045
+ return {
8046
+ language: profile.language,
8047
+ aliases: profile.aliases,
8048
+ parserRows: languageRows.length,
8049
+ parsers: languageRows.map((row) => row.parser),
8050
+ imports: languageRows.reduce((sum, row) => sum + row.imports.total, 0),
8051
+ adapters: languageRows.reduce((sum, row) => sum + row.adapters.total, 0),
8052
+ mergeReadyParsers: languageRows.filter((row) => row.merge.mergeReady).map((row) => row.parser),
8053
+ readiness: languageRows.reduce((current, row) => maxSemanticMergeReadiness(current, row.merge.readiness), 'ready')
8054
+ };
8055
+ });
8056
+ }
8057
+
8058
+ function nativeParserFeatureParserSlots(profile, imports, adapters) {
8059
+ const slots = [
8060
+ ...(profile.parserAdapters ?? []),
8061
+ ...adapters.map((adapter) => adapter.parser),
8062
+ ...imports.map(nativeParserParserForImport)
8063
+ ].filter(Boolean);
8064
+ if (!slots.length && profile.supportsLightweightScan) slots.push(`${profile.language}.lightweight-declaration-scan`);
8065
+ const seen = new Set();
8066
+ const result = [];
8067
+ for (const slot of slots) {
8068
+ const key = `${normalizeParserAstFormatId(slot)}#${parserAstFormatIdForParser(slot)}`;
8069
+ if (seen.has(key)) continue;
8070
+ seen.add(key);
8071
+ result.push(String(slot));
8072
+ }
8073
+ return result;
8074
+ }
8075
+
8076
+ function nativeParserFeatureImportsForProfile(profile, imports = []) {
8077
+ const languages = nativeParserFeatureLanguageSet(profile);
8078
+ return imports.filter((imported) => languages.has(normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language ?? imported?.nativeSource?.language)));
8079
+ }
8080
+
8081
+ function nativeParserFeatureAdapterSummariesForProfile(profile, adapters = []) {
8082
+ const languages = nativeParserFeatureLanguageSet(profile);
8083
+ return adapters
8084
+ .map((adapter) => safeNativeImporterAdapterSummary(adapter))
8085
+ .filter(Boolean)
8086
+ .filter((adapter) => languages.has(normalizeNativeLanguageId(adapter.language)));
8087
+ }
8088
+
8089
+ function nativeParserFeatureLanguageSet(profile) {
8090
+ return new Set([profile.language, ...(profile.aliases ?? [])].map(normalizeNativeLanguageId).filter(Boolean));
8091
+ }
8092
+
8093
+ function nativeParserParserForImport(imported) {
8094
+ return imported?.adapter?.parser
8095
+ ?? imported?.metadata?.adapterCoverage?.parser
8096
+ ?? imported?.nativeAst?.parser
8097
+ ?? imported?.nativeSource?.parser
8098
+ ?? imported?.parser
8099
+ ?? imported?.metadata?.parser;
8100
+ }
8101
+
8102
+ function nativeParserFeatureParserMatches(candidateParser, rowParser) {
8103
+ if (!candidateParser || !rowParser) return false;
8104
+ const candidate = normalizeParserAstFormatId(candidateParser);
8105
+ const row = normalizeParserAstFormatId(rowParser);
8106
+ return candidate === row || parserAstFormatIdForParser(candidateParser) === parserAstFormatIdForParser(rowParser);
8107
+ }
8108
+
8109
+ function nativeParserFeatureSemanticEvidence(imports) {
8110
+ const totals = {
8111
+ declarations: 0,
8112
+ symbols: 0,
8113
+ references: 0,
8114
+ types: 0,
8115
+ controlFlow: 0
8116
+ };
8117
+ for (const imported of imports) {
8118
+ const semanticIndex = imported?.semanticIndex ?? imported?.universalAst?.semanticIndex;
8119
+ const evidence = observeNativeImporterSemanticEvidence(semanticIndex);
8120
+ totals.declarations += evidence.declarations;
8121
+ totals.symbols += semanticIndex?.symbols?.length ?? 0;
8122
+ totals.references += evidence.references;
8123
+ totals.types += evidence.types;
8124
+ totals.controlFlow += evidence.controlFlow;
8125
+ }
8126
+ return totals;
8127
+ }
8128
+
8129
+ function nativeParserFeatureSemanticLevel(adapterCoverage, semanticEvidence) {
8130
+ if ((adapterCoverage.effective.types ?? 0) > 0 || (adapterCoverage.effective.controlFlow ?? 0) > 0 || semanticEvidence.types > 0 || semanticEvidence.controlFlow > 0 || semanticEvidence.references > 0) {
8131
+ return 'semantic-index';
8132
+ }
8133
+ if ((adapterCoverage.effective.semanticDeclarations ?? 0) > 0 || (adapterCoverage.effective.semanticSymbols ?? 0) > 0 || semanticEvidence.declarations > 0 || semanticEvidence.symbols > 0) {
8134
+ return 'declaration-index';
8135
+ }
8136
+ return 'native-ast';
8137
+ }
8138
+
8139
+ function nativeParserFeatureCapabilityGaps(adapterCoverage, capabilities) {
8140
+ const gaps = new Set();
8141
+ for (const capability of capabilities) {
8142
+ if ((adapterCoverage.effective?.[capability] ?? 0) === 0) gaps.add(capability);
8143
+ }
8144
+ for (const capability of Object.keys(adapterCoverage.gaps ?? {})) {
8145
+ if (capabilities.includes(capability)) gaps.add(capability);
8146
+ }
8147
+ return [...gaps];
8148
+ }
8149
+
8150
+ function nativeParserFeatureLossKindCounts(losses, kinds) {
8151
+ const wanted = new Set(kinds);
8152
+ const counts = {};
8153
+ for (const loss of losses) {
8154
+ if (!wanted.has(loss?.kind)) continue;
8155
+ counts[loss.kind] = (counts[loss.kind] ?? 0) + 1;
8156
+ }
8157
+ return counts;
8158
+ }
8159
+
8160
+ function nativeParserFeaturePresentLossKinds(context, kindSet) {
8161
+ return uniqueStrings([
8162
+ ...(context.profile.knownLossKinds ?? []),
8163
+ ...Object.keys(context.lossSummary.byKind ?? {})
8164
+ ].filter((kind) => kindSet.has(kind)));
8165
+ }
8166
+
8167
+ function mergeNativeParserAstFormatProfiles(profiles, imports, adapters) {
8168
+ const byId = new Map((profiles ?? []).map((profile) => [normalizeParserAstFormatId(profile.id ?? profile), nativeParserAstFormatProfile(normalizeParserAstFormatId(profile.id ?? profile), profile)]));
8169
+ for (const adapter of adapters ?? []) {
8170
+ const summary = safeNativeImporterAdapterSummary(adapter);
8171
+ if (!summary) continue;
8172
+ const formatId = parserAstFormatIdForParser(summary.parser);
8173
+ if (!byId.has(formatId)) {
8174
+ byId.set(formatId, nativeParserAstFormatProfile(formatId, {
8175
+ languages: [summary.language],
8176
+ parserAdapters: [summary.parser],
8177
+ exactness: summary.coverage.exactness,
8178
+ sourceRangeModel: summary.coverage.sourceRanges ? 'adapter-reported' : 'unknown'
8179
+ }));
8180
+ }
8181
+ }
8182
+ for (const imported of imports ?? []) {
8183
+ const formatId = parserAstFormatIdForImport(imported);
8184
+ if (formatId && !byId.has(formatId)) {
8185
+ byId.set(formatId, nativeParserAstFormatProfile(formatId, {
8186
+ languages: [imported.language].filter(Boolean),
8187
+ parserAdapters: [imported.parser ?? imported.nativeAst?.parser ?? formatId],
8188
+ exactness: 'unknown',
8189
+ sourceRangeModel: (imported.sourceMaps ?? []).some((sourceMap) => sourceMap.mappings?.some((mapping) => mapping.sourceSpan)) ? 'adapter-reported' : 'unknown'
8190
+ }));
8191
+ }
8192
+ }
8193
+ return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
8194
+ }
8195
+
8196
+ function nativeParserAstFormatCoverageForProfile(profile, imports, adapters) {
8197
+ const formatIds = new Set([profile.id, ...profile.aliases].map(normalizeParserAstFormatId));
8198
+ const adapterParsers = new Set(profile.parserAdapters.map(parserAstFormatIdForParser));
8199
+ const matchingAdapters = (adapters ?? [])
8200
+ .map((adapter) => safeNativeImporterAdapterSummary(adapter))
8201
+ .filter(Boolean)
8202
+ .filter((adapter) => formatIds.has(parserAstFormatIdForParser(adapter.parser)) || adapterParsers.has(parserAstFormatIdForParser(adapter.parser)));
8203
+ const matchingImports = (imports ?? [])
8204
+ .filter((imported) => {
8205
+ const formatId = parserAstFormatIdForImport(imported);
8206
+ return formatId && (formatIds.has(formatId) || adapterParsers.has(formatId));
8207
+ });
8208
+ const effectiveCapabilities = {};
8209
+ for (const adapter of matchingAdapters) {
8210
+ for (const row of adapter.coverage.capabilityEvidence?.capabilities ?? []) {
8211
+ if (row.effective) effectiveCapabilities[row.capability] = (effectiveCapabilities[row.capability] ?? 0) + 1;
8212
+ }
8213
+ }
8214
+ const readiness = matchingImports.reduce(
8215
+ (current, imported) => maxSemanticMergeReadiness(current, nativeImportReadiness(imported)),
8216
+ matchingImports.length ? 'ready' : 'needs-review'
8217
+ );
8218
+ return {
8219
+ id: profile.id,
8220
+ kind: profile.kind,
8221
+ languages: profile.languages,
8222
+ parserAdapters: profile.parserAdapters,
8223
+ exactness: profile.exactness,
8224
+ sourceRangeModel: profile.sourceRangeModel,
8225
+ preservesTokens: profile.preservesTokens,
8226
+ preservesTrivia: profile.preservesTrivia,
8227
+ supportsIncremental: profile.supportsIncremental,
8228
+ supportsErrorRecovery: profile.supportsErrorRecovery,
8229
+ notes: profile.notes,
8230
+ adapters: {
8231
+ total: matchingAdapters.length,
8232
+ ids: matchingAdapters.map((adapter) => adapter.id),
8233
+ parsers: uniqueStrings(matchingAdapters.map((adapter) => adapter.parser)),
8234
+ effectiveCapabilities
8235
+ },
8236
+ imports: {
8237
+ total: matchingImports.length,
8238
+ sourcePaths: matchingImports.map((imported) => imported.sourcePath).filter(Boolean),
8239
+ readiness,
8240
+ nativeAstNodes: matchingImports.reduce((sum, imported) => sum + Object.keys(imported.nativeAst?.nodes ?? {}).length, 0),
8241
+ symbols: matchingImports.reduce((sum, imported) => sum + (imported.semanticIndex?.symbols?.length ?? 0), 0),
8242
+ sourceMapMappings: matchingImports.reduce((sum, imported) => sum + (imported.sourceMaps ?? []).reduce((mapSum, sourceMap) => mapSum + (sourceMap.mappings?.length ?? 0), 0), 0),
8243
+ losses: matchingImports.reduce((sum, imported) => sum + (imported.losses?.length ?? 0), 0)
8244
+ }
8245
+ };
8246
+ }
8247
+
8248
+ function parserAstFormatIdForParser(parser) {
8249
+ const text = normalizeParserAstFormatId(parser);
8250
+ if (text.includes('typescript')) return 'typescript-compiler-api';
8251
+ if (text.includes('python') && text.includes('ast')) return 'python-ast';
8252
+ if (text === 'syn' || text.includes('rust-syn')) return 'rust-syn';
8253
+ if (text.includes('rust-analyzer') || text.includes('rowan')) return 'rust-analyzer-rowan';
8254
+ if (text.includes('clang') || text.includes('libclang')) return 'clang-ast-json';
8255
+ if (text === 'go' || text.includes('go-parser') || text.includes('go-ast') || text.includes('go/parser') || text.includes('go/ast')) return 'go-ast';
8256
+ if (text === 'java' || text.includes('javac') || text.includes('jdt') || text.includes('javaparser') || text.includes('java-parser') || text.includes('java-ast')) return 'java-ast';
8257
+ if (text === 'kotlin' || text === 'kt' || text.includes('kotlin-psi') || text.includes('kotlin-compiler') || text.includes('intellij-psi') || text.includes('kt-psi')) return 'kotlin-psi';
8258
+ if (text === 'csharp' || text === 'c#' || text === 'cs' || text.includes('roslyn') || text.includes('microsoft-codeanalysis-csharp') || text.includes('csharp-syntax')) return 'roslyn-csharp';
8259
+ if (text.includes('swift-syntax') || text.includes('swiftsyntax') || text.includes('swiftparser') || text.includes('swift-parser')) return 'swift-syntax';
8260
+ if (text.includes('tree-sitter') || text.includes('treesitter')) return 'tree-sitter';
8261
+ if (text.includes('babel')) return 'babel';
8262
+ if (text.includes('estree')) return 'estree';
8263
+ if (text.includes('libcst')) return 'libcst';
8264
+ if (text.includes('scip')) return 'scip';
8265
+ if (text.includes('lsif')) return 'lsif';
8266
+ return text || 'unknown';
8267
+ }
8268
+
8269
+ function parserAstFormatIdForImport(imported) {
8270
+ return parserAstFormatIdForParser(
8271
+ imported?.metadata?.adapter?.parser
8272
+ ?? imported?.metadata?.astFormat
8273
+ ?? imported?.nativeAst?.metadata?.astFormat
8274
+ ?? imported?.nativeAst?.parser
8275
+ ?? imported?.parser
8276
+ ?? imported?.metadata?.parser
8277
+ );
8278
+ }
8279
+
8280
+ function mergeNativeImportProfiles(languages, imports, adapters, targetAdapters = []) {
8281
+ const profilesByLanguage = new Map();
8282
+ for (const profile of languages) {
8283
+ const normalized = normalizeNativeLanguageId(profile.language ?? profile);
8284
+ profilesByLanguage.set(normalized, normalizeNativeImportLanguageProfile(profile, normalized));
8285
+ }
8286
+ for (const imported of imports) {
8287
+ const normalized = normalizeNativeLanguageId(imported?.language ?? imported?.nativeAst?.language);
8288
+ if (!normalized || profilesByLanguage.has(normalized)) continue;
8289
+ profilesByLanguage.set(normalized, nativeImportLanguageProfile(normalized, {
8290
+ supportsLightweightScan: false,
8291
+ parserAdapters: [],
8292
+ defaultReadiness: 'blocked',
8293
+ lossKinds: ['unsupportedSyntax'],
8294
+ notes: ['language appeared in import evidence but has no declared Frontier coverage profile']
8295
+ }));
8296
+ }
8297
+ for (const adapter of adapters) {
7572
8298
  const normalized = normalizeNativeLanguageId(adapter?.language);
7573
8299
  if (!normalized) continue;
7574
8300
  const existing = profilesByLanguage.get(normalized) ?? nativeImportLanguageProfile(normalized, { supportsLightweightScan: false, parserAdapters: [] });
@@ -8629,6 +9355,23 @@ function parseJavaAstSource(input, options) {
8629
9355
  return parse(input.sourceText, parserOptions);
8630
9356
  }
8631
9357
 
9358
+ function parseKotlinPsiSource(input, options) {
9359
+ const parse = options.parse ?? options.parserModule?.parse ?? options.kotlinPsi?.parse ?? options.kotlinCompiler?.parse ?? options.intellijPsi?.parse;
9360
+ if (typeof parse !== 'function') return undefined;
9361
+ const parserOptions = {
9362
+ sourcePath: input.sourcePath,
9363
+ filename: input.sourcePath,
9364
+ kotlinVersion: options.kotlinVersion ?? input.options?.kotlinVersion,
9365
+ languageVersion: options.languageVersion ?? input.options?.languageVersion,
9366
+ apiVersion: options.apiVersion ?? input.options?.apiVersion,
9367
+ script: options.script ?? input.options?.script ?? /\.kts$/i.test(input.sourcePath ?? ''),
9368
+ includeAnnotations: options.includeAnnotations ?? input.options?.includeAnnotations,
9369
+ ...(options.parserOptions ?? {}),
9370
+ ...(input.options?.parserOptions ?? {})
9371
+ };
9372
+ return parse(input.sourceText, parserOptions);
9373
+ }
9374
+
8632
9375
  function parseCSharpRoslynSource(input, options) {
8633
9376
  const parse = options.parse ?? options.parserModule?.parse ?? options.roslyn?.parse ?? options.csharpRoslyn?.parse;
8634
9377
  if (typeof parse !== 'function') return undefined;
@@ -8891,14 +9634,17 @@ function createNativeImportFromJavaAst(root, input, options) {
8891
9634
  };
8892
9635
  }
8893
9636
 
8894
- function createNativeImportFromCSharpRoslyn(root, input, options) {
9637
+ function createNativeImportFromKotlinPsi(root, input, options) {
8895
9638
  const context = createAstNormalizationContext(input, options);
8896
- visitCSharpRoslynNode(root, context, 'root');
9639
+ visitKotlinPsiNode(root, context, 'root');
8897
9640
  if (context.truncated) {
8898
9641
  context.losses.push(truncatedAstLoss(input, context, options));
8899
9642
  }
8900
9643
  if (options.generated && !context.losses.some((loss) => loss.kind === 'generatedCode')) {
8901
- context.losses.push(csharpGeneratedCodeLoss(input, context.rootId, undefined, options));
9644
+ context.losses.push(kotlinGeneratedCodeLoss(input, context.rootId, undefined, options));
9645
+ }
9646
+ if (options.script && !context.losses.some((loss) => loss.metadata?.script === true)) {
9647
+ context.losses.push(kotlinScriptLoss(input, context.rootId, undefined, options));
8902
9648
  }
8903
9649
  const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
8904
9650
  return {
@@ -8915,12 +9661,56 @@ function createNativeImportFromCSharpRoslyn(root, input, options) {
8915
9661
  metadata: {
8916
9662
  astFormat: options.astFormat,
8917
9663
  parser: options.parser,
8918
- csharpVersion: options.csharpVersion,
9664
+ kotlinVersion: options.kotlinVersion,
8919
9665
  languageVersion: options.languageVersion,
8920
- nullableContext: options.nullableContext,
9666
+ apiVersion: options.apiVersion,
9667
+ script: Boolean(options.script),
8921
9668
  generated: options.generated,
8922
- projectReferences: csharpEvidenceSummary(options.projectReferences),
8923
- analyzerDiagnostics: csharpEvidenceSummary(options.analyzerDiagnostics),
9669
+ analysisApiEvidence: kotlinEvidenceSummary(options.analysisApiEvidence),
9670
+ firEvidence: kotlinEvidenceSummary(options.firEvidence),
9671
+ compilerPluginEvidence: kotlinEvidenceSummary(options.compilerPluginEvidence),
9672
+ kspEvidence: kotlinEvidenceSummary(options.kspEvidence),
9673
+ kaptEvidence: kotlinEvidenceSummary(options.kaptEvidence),
9674
+ multiplatformEvidence: kotlinEvidenceSummary(options.multiplatformEvidence),
9675
+ buildVariantEvidence: kotlinEvidenceSummary(options.buildVariantEvidence),
9676
+ includeAnnotations: Boolean(options.includeAnnotations),
9677
+ normalizedNodeCount: Object.keys(context.nodes).length,
9678
+ declarationCount: context.declarations.length,
9679
+ truncated: context.truncated
9680
+ }
9681
+ };
9682
+ }
9683
+
9684
+ function createNativeImportFromCSharpRoslyn(root, input, options) {
9685
+ const context = createAstNormalizationContext(input, options);
9686
+ visitCSharpRoslynNode(root, context, 'root');
9687
+ if (context.truncated) {
9688
+ context.losses.push(truncatedAstLoss(input, context, options));
9689
+ }
9690
+ if (options.generated && !context.losses.some((loss) => loss.kind === 'generatedCode')) {
9691
+ context.losses.push(csharpGeneratedCodeLoss(input, context.rootId, undefined, options));
9692
+ }
9693
+ const semantic = semanticIndexFromNativeDeclarations(context.declarations, input, options);
9694
+ return {
9695
+ rootId: context.rootId,
9696
+ nodes: context.nodes,
9697
+ semanticIndex: semantic.semanticIndex,
9698
+ mappings: semantic.mappings,
9699
+ losses: mergeNativeLosses(context.losses, options.diagnostics?.map((diagnostic, index) => adapterDiagnosticToLoss(diagnostic, index, {
9700
+ id: input.adapterId,
9701
+ version: input.adapterVersion
9702
+ }, input)) ?? []),
9703
+ evidence: semantic.evidence,
9704
+ diagnostics: options.diagnostics,
9705
+ metadata: {
9706
+ astFormat: options.astFormat,
9707
+ parser: options.parser,
9708
+ csharpVersion: options.csharpVersion,
9709
+ languageVersion: options.languageVersion,
9710
+ nullableContext: options.nullableContext,
9711
+ generated: options.generated,
9712
+ projectReferences: csharpEvidenceSummary(options.projectReferences),
9713
+ analyzerDiagnostics: csharpEvidenceSummary(options.analyzerDiagnostics),
8924
9714
  semanticModelEvidence: csharpEvidenceSummary(options.semanticModelEvidence),
8925
9715
  sourceGeneratorEvidence: csharpEvidenceSummary(options.sourceGeneratorEvidence),
8926
9716
  normalizedNodeCount: Object.keys(context.nodes).length,
@@ -12348,6 +13138,658 @@ function javaBindingEvidenceSummary(value) {
12348
13138
  return Object.keys(summary).length ? summary : { present: true };
12349
13139
  }
12350
13140
 
13141
+ function kotlinPsiRoot(value) {
13142
+ if (!value || typeof value !== 'object') return undefined;
13143
+ if (isKotlinPsiNode(value)) return value;
13144
+ if (isKotlinPsiNode(value.ast)) return value.ast;
13145
+ if (isKotlinPsiNode(value.root)) return value.root;
13146
+ if (isKotlinPsiNode(value.rootNode)) return value.rootNode;
13147
+ if (isKotlinPsiNode(value.ktFile)) return value.ktFile;
13148
+ if (isKotlinPsiNode(value.file)) return value.file;
13149
+ if (isKotlinPsiNode(value.sourceFile)) return value.sourceFile;
13150
+ if (Array.isArray(value.declarations) || Array.isArray(value.imports) || value.packageDirective) {
13151
+ return { kind: 'KtFile', ...value };
13152
+ }
13153
+ return undefined;
13154
+ }
13155
+
13156
+ function isKotlinPsiNode(value) {
13157
+ return Boolean(value && typeof value === 'object' && typeof kotlinPsiKind(value) === 'string');
13158
+ }
13159
+
13160
+ function kotlinPsiKind(node) {
13161
+ if (!node || typeof node !== 'object') return undefined;
13162
+ const declared = node.kind ?? node.nodeType ?? node.elementType ?? node.psiKind ?? node._type ?? node.type;
13163
+ if (typeof declared === 'string') return normalizeKotlinPsiKind(declared);
13164
+ if (Array.isArray(node.declarations) || Array.isArray(node.imports) || node.packageDirective) return 'KtFile';
13165
+ if (node.fqName || node.packageFqName) return 'KtPackageDirective';
13166
+ if (node.importedFqName || node.importedReference) return 'KtImportDirective';
13167
+ if (node.classKind || node.primaryConstructor || node.superTypeListEntries) return 'KtClass';
13168
+ if (node.funKeyword || node.valueParameters || node.bodyExpression) return 'KtNamedFunction';
13169
+ if (node.valOrVarKeyword || node.delegateExpression || node.initializer) return 'KtProperty';
13170
+ return undefined;
13171
+ }
13172
+
13173
+ function normalizeKotlinPsiKind(kind) {
13174
+ const text = String(kind)
13175
+ .replace(/^org\.jetbrains\.kotlin\.psi\./, '')
13176
+ .replace(/^KtNodeTypes\./, '')
13177
+ .replace(/ElementType$/, '');
13178
+ const compact = text.replace(/[_\s.-]+/g, '').toLowerCase();
13179
+ const known = {
13180
+ kotlinfile: 'KtFile',
13181
+ ktfile: 'KtFile',
13182
+ file: 'KtFile',
13183
+ script: 'KtScript',
13184
+ ktscript: 'KtScript',
13185
+ packagedirective: 'KtPackageDirective',
13186
+ ktpackagedirective: 'KtPackageDirective',
13187
+ importdirective: 'KtImportDirective',
13188
+ ktimportdirective: 'KtImportDirective',
13189
+ class: 'KtClass',
13190
+ ktclass: 'KtClass',
13191
+ classorobject: 'KtClassOrObject',
13192
+ ktclassorobject: 'KtClassOrObject',
13193
+ objectdeclaration: 'KtObjectDeclaration',
13194
+ ktobjectdeclaration: 'KtObjectDeclaration',
13195
+ enumentry: 'KtEnumEntry',
13196
+ ktenumentry: 'KtEnumEntry',
13197
+ namedfunction: 'KtNamedFunction',
13198
+ ktnamedfunction: 'KtNamedFunction',
13199
+ function: 'KtNamedFunction',
13200
+ property: 'KtProperty',
13201
+ ktproperty: 'KtProperty',
13202
+ typealias: 'KtTypeAlias',
13203
+ kttypealias: 'KtTypeAlias',
13204
+ parameter: 'KtParameter',
13205
+ ktparameter: 'KtParameter',
13206
+ primaryconstructor: 'KtPrimaryConstructor',
13207
+ ktprimaryconstructor: 'KtPrimaryConstructor',
13208
+ secondaryconstructor: 'KtSecondaryConstructor',
13209
+ ktsecondaryconstructor: 'KtSecondaryConstructor',
13210
+ classinitializer: 'KtClassInitializer',
13211
+ ktclassinitializer: 'KtClassInitializer',
13212
+ annotationentry: 'KtAnnotationEntry',
13213
+ ktannotationentry: 'KtAnnotationEntry',
13214
+ contracteffect: 'KtContractEffect',
13215
+ ktcontracteffect: 'KtContractEffect',
13216
+ contractdescription: 'KtContractDescription',
13217
+ ktcontractdescription: 'KtContractDescription',
13218
+ error: 'PsiErrorElement',
13219
+ psierror: 'PsiErrorElement',
13220
+ psierrorelement: 'PsiErrorElement'
13221
+ };
13222
+ if (known[compact]) return known[compact];
13223
+ if (/^[A-Z0-9_]+$/.test(text)) return text.toLowerCase().split('_').map(upperFirst).join('');
13224
+ return text;
13225
+ }
13226
+
13227
+ function ignoredKotlinPsiField(key) {
13228
+ return key === '_type'
13229
+ || key === 'type'
13230
+ || key === 'kind'
13231
+ || key === 'nodeType'
13232
+ || key === 'elementType'
13233
+ || key === 'psiKind'
13234
+ || key === 'parent'
13235
+ || key === 'parentKind'
13236
+ || key === 'parentField'
13237
+ || key === 'textRange'
13238
+ || key === 'range'
13239
+ || key === 'span'
13240
+ || key === 'location'
13241
+ || key === 'name'
13242
+ || key === 'nameIdentifier'
13243
+ || key === 'identifier'
13244
+ || key === 'fqName'
13245
+ || key === 'packageFqName'
13246
+ || key === 'analysisSymbol'
13247
+ || key === 'symbol'
13248
+ || key === 'typeInfo'
13249
+ || key === 'bindingContext'
13250
+ || key === 'fir'
13251
+ || key === 'ir';
13252
+ }
13253
+
13254
+ function visitKotlinPsiNode(node, context, propertyPath) {
13255
+ if (!isKotlinPsiNode(node) || context.truncated) return undefined;
13256
+ if (context.objectIds.has(node)) return context.objectIds.get(node);
13257
+ if (context.counter >= context.maxNodes) {
13258
+ context.truncated = true;
13259
+ return undefined;
13260
+ }
13261
+ const kind = kotlinPsiKind(node);
13262
+ const span = spanFromKotlinPsiNode(node, context.input, context.options);
13263
+ const id = nativeNodeId(context, kind, { start: { line: span?.startLine, column: span?.startColumn } }, propertyPath);
13264
+ context.objectIds.set(node, id);
13265
+ if (!context.rootId) context.rootId = id;
13266
+ const children = [];
13267
+ for (const [field, value] of kotlinPsiChildEntries(node, kind)) {
13268
+ if (Array.isArray(value)) {
13269
+ value.forEach((entry, index) => {
13270
+ const childId = visitKotlinPsiNode(entry, context, `${propertyPath}.${field}[${index}]`);
13271
+ if (childId) children.push(childId);
13272
+ });
13273
+ } else {
13274
+ const childId = visitKotlinPsiNode(value, context, `${propertyPath}.${field}`);
13275
+ if (childId) children.push(childId);
13276
+ }
13277
+ }
13278
+ const declarations = kotlinPsiDeclarations(node, kind, id, context.input);
13279
+ const declaration = declarations[0];
13280
+ const nativeNode = {
13281
+ id,
13282
+ kind,
13283
+ languageKind: `${context.input.language}.${kind}`,
13284
+ span,
13285
+ value: declaration?.name ?? kotlinPsiNodeValue(node),
13286
+ fields: primitiveKotlinPsiFields(node, kind),
13287
+ children,
13288
+ metadata: {
13289
+ astFormat: context.options.astFormat,
13290
+ propertyPath,
13291
+ positionKind: kotlinPsiPositionKind(node),
13292
+ parser: context.options.parser
13293
+ }
13294
+ };
13295
+ context.nodes[id] = nativeNode;
13296
+ for (const entry of declarations) {
13297
+ context.declarations.push({ ...entry, nativeNode });
13298
+ }
13299
+ if (kotlinPsiRecoveredKind(kind) || kotlinPsiProblemNode(node, kind)) {
13300
+ context.losses.push({
13301
+ id: `loss_${idFragment(id)}_kotlin_psi_recovered_node`,
13302
+ severity: 'error',
13303
+ phase: 'parse',
13304
+ sourceFormat: context.input.language,
13305
+ kind: 'unsupportedSyntax',
13306
+ message: 'Kotlin PSI reported an error or recovered syntax node; semantic import is partial until syntax errors are resolved.',
13307
+ span,
13308
+ nodeId: id,
13309
+ metadata: {
13310
+ parser: context.options.parser,
13311
+ astFormat: context.options.astFormat,
13312
+ nodeKind: kind
13313
+ }
13314
+ });
13315
+ }
13316
+ if (kotlinExpectActualNode(node, kind)) {
13317
+ context.losses.push(kotlinUnsupportedSemanticLoss(context.input, id, span, context.options, {
13318
+ nodeKind: kind,
13319
+ feature: 'expect-actual',
13320
+ message: 'Kotlin expect/actual syntax was imported; matching platform declarations requires multiplatform build evidence.'
13321
+ }));
13322
+ }
13323
+ if (kotlinCoroutineNode(node, kind)) {
13324
+ context.losses.push(kotlinUnsupportedSemanticLoss(context.input, id, span, context.options, {
13325
+ nodeKind: kind,
13326
+ feature: 'coroutine',
13327
+ message: 'Kotlin coroutine syntax was imported; suspend lowering, scheduling, and effect semantics require host compiler/runtime evidence.'
13328
+ }));
13329
+ }
13330
+ if (kotlinContractNode(kind)) {
13331
+ context.losses.push(kotlinUnsupportedSemanticLoss(context.input, id, span, context.options, {
13332
+ nodeKind: kind,
13333
+ feature: 'contract',
13334
+ message: 'Kotlin contract syntax was imported; data-flow effects require Analysis API or compiler evidence.'
13335
+ }));
13336
+ }
13337
+ if (kotlinCompilerPluginAnnotationNode(node, kind) && !context.options.compilerPluginEvidence) {
13338
+ context.losses.push({
13339
+ id: `loss_${idFragment(id)}_kotlin_compiler_plugin_semantics`,
13340
+ severity: 'warning',
13341
+ phase: 'parse',
13342
+ sourceFormat: context.input.language,
13343
+ kind: 'metaprogramming',
13344
+ message: 'Kotlin compiler-plugin-style annotation was imported; generated declarations and transformed semantics require compiler plugin evidence.',
13345
+ span,
13346
+ nodeId: id,
13347
+ metadata: {
13348
+ parser: context.options.parser,
13349
+ astFormat: context.options.astFormat,
13350
+ nodeKind: kind,
13351
+ annotations: kotlinPsiAnnotationNames(node)
13352
+ }
13353
+ });
13354
+ }
13355
+ if (kotlinGeneratedCodeMarker(node, kind)) {
13356
+ context.losses.push(kotlinGeneratedCodeLoss(context.input, id, span, context.options, { nodeKind: kind }));
13357
+ }
13358
+ return id;
13359
+ }
13360
+
13361
+ function primitiveKotlinPsiFields(node, kind) {
13362
+ const fields = { kind };
13363
+ const name = kotlinPsiDeclarationName(node, kind);
13364
+ if (name) fields.name = name;
13365
+ const importPath = kotlinPsiImportPath(node);
13366
+ if (importPath) fields.importPath = importPath;
13367
+ const packageName = kotlinPsiPackageName(node);
13368
+ if (packageName) fields.packageName = packageName;
13369
+ const type = kotlinPsiTypeName(node.typeReference ?? node.returnTypeRef ?? node.returnType ?? node.type);
13370
+ if (type) fields.type = type;
13371
+ const receiver = kotlinPsiTypeName(node.receiverTypeReference ?? node.receiverTypeRef);
13372
+ if (receiver) fields.receiverType = receiver;
13373
+ const modifiers = kotlinPsiModifiers(node);
13374
+ if (modifiers.length) fields.modifiers = modifiers.join(',');
13375
+ const annotations = kotlinPsiAnnotationNames(node);
13376
+ if (annotations.length) fields.annotations = annotations.join(',');
13377
+ if (node.generated === true || node.isGenerated === true) fields.generated = true;
13378
+ if (node.isScript === true || kind === 'KtScript') fields.script = true;
13379
+ if (Array.isArray(node.typeParameters ?? node.typeParameterList?.parameters)) fields.typeParameterCount = (node.typeParameters ?? node.typeParameterList?.parameters).length;
13380
+ if (Array.isArray(node.valueParameters ?? node.valueParameterList?.parameters ?? node.parameters)) fields.parameterCount = (node.valueParameters ?? node.valueParameterList?.parameters ?? node.parameters).length;
13381
+ if (Array.isArray(node.superTypeListEntries ?? node.superTypes)) fields.superTypeCount = (node.superTypeListEntries ?? node.superTypes).length;
13382
+ return fields;
13383
+ }
13384
+
13385
+ function spanFromKotlinPsiNode(node, input, options = {}) {
13386
+ const direct = spanFromKotlinLineFields(node, input);
13387
+ if (direct) return direct;
13388
+ const range = node.sourceRange ?? node.range ?? node.span ?? node.textRange;
13389
+ const fromRange = spanFromKotlinRange(range, input);
13390
+ if (fromRange) return fromRange;
13391
+ const start = kotlinPsiPosition(node.start ?? node.startOffset ?? range?.startOffset ?? range?.start, options);
13392
+ const end = kotlinPsiPosition(node.end ?? node.endOffset ?? range?.endOffset ?? range?.end, options);
13393
+ if (!start) return undefined;
13394
+ return {
13395
+ sourceId: input.sourceHash,
13396
+ path: start.path ?? end?.path ?? input.sourcePath,
13397
+ startLine: start.line,
13398
+ startColumn: start.column,
13399
+ endLine: end?.line,
13400
+ endColumn: end?.column
13401
+ };
13402
+ }
13403
+
13404
+ function spanFromKotlinLineFields(node, input) {
13405
+ const startLine = node.startLine ?? node.line ?? node.beginLine;
13406
+ if (typeof startLine !== 'number') return undefined;
13407
+ return {
13408
+ sourceId: input.sourceHash,
13409
+ path: node.path ?? node.filePath ?? node.file ?? input.sourcePath,
13410
+ startLine,
13411
+ startColumn: node.startColumn ?? node.column ?? node.beginColumn,
13412
+ endLine: node.endLine,
13413
+ endColumn: node.endColumn
13414
+ };
13415
+ }
13416
+
13417
+ function spanFromKotlinRange(range, input) {
13418
+ if (!range || typeof range !== 'object') return undefined;
13419
+ const start = range.start ?? range.startPosition ?? range.lowerBound ?? range.begin;
13420
+ const end = range.end ?? range.endPosition ?? range.upperBound;
13421
+ const line = start?.line ?? start?.Line;
13422
+ if (typeof line !== 'number') return undefined;
13423
+ const column = start.column ?? start.character ?? start.offset ?? start.Column;
13424
+ const endLine = end?.line ?? end?.Line;
13425
+ const endColumn = end?.column ?? end?.character ?? end?.offset ?? end?.Column;
13426
+ return {
13427
+ sourceId: input.sourceHash,
13428
+ path: range.path ?? range.filePath ?? range.file ?? input.sourcePath,
13429
+ startLine: line,
13430
+ startColumn: typeof column === 'number' ? column : undefined,
13431
+ endLine: typeof endLine === 'number' ? endLine : undefined,
13432
+ endColumn: typeof endColumn === 'number' ? endColumn : undefined
13433
+ };
13434
+ }
13435
+
13436
+ function kotlinPsiPosition(value, options = {}) {
13437
+ if (value === undefined || value === null) return undefined;
13438
+ if (typeof value === 'object') {
13439
+ const position = value.position ?? value.location ?? value;
13440
+ const line = position.line ?? position.Line;
13441
+ const column = position.column ?? position.character ?? position.Column;
13442
+ if (typeof line === 'number') {
13443
+ return {
13444
+ path: position.path ?? position.filePath ?? position.file,
13445
+ line,
13446
+ column: typeof column === 'number' ? column : undefined
13447
+ };
13448
+ }
13449
+ }
13450
+ const resolver = typeof options.positionResolver === 'function'
13451
+ ? options.positionResolver
13452
+ : typeof options.lineMap?.position === 'function'
13453
+ ? options.lineMap.position.bind(options.lineMap)
13454
+ : typeof options.lineMap?.getLineAndColumn === 'function'
13455
+ ? options.lineMap.getLineAndColumn.bind(options.lineMap)
13456
+ : undefined;
13457
+ if (resolver) {
13458
+ const resolved = resolver(value);
13459
+ if (resolved !== value) return kotlinPsiPosition(resolved, options);
13460
+ }
13461
+ return undefined;
13462
+ }
13463
+
13464
+ function kotlinPsiPositionKind(node) {
13465
+ if (node.textRange || node.range) return 'text-range';
13466
+ if (typeof node.startOffset === 'number') return 'offset';
13467
+ if (typeof node.startLine === 'number' || typeof node.line === 'number') return 'line-column-fields';
13468
+ return undefined;
13469
+ }
13470
+
13471
+ function kotlinPsiDeclarations(node, kind, nativeNodeId, input) {
13472
+ if (kind === 'KtPackageDirective') {
13473
+ const name = kotlinPsiPackageName(node);
13474
+ return name ? [declarationRecord(input, nativeNodeId, name, 'namespace', 'definition')] : [];
13475
+ }
13476
+ if (kind === 'KtImportDirective') {
13477
+ const name = kotlinPsiImportPath(node);
13478
+ return name ? [declarationRecord(input, nativeNodeId, name, 'module', 'import')] : [];
13479
+ }
13480
+ if (kotlinPsiTypeDeclarationKind(kind)) {
13481
+ const name = kotlinPsiDeclarationName(node, kind);
13482
+ return name ? [declarationRecord(input, nativeNodeId, name, kotlinPsiTypeDeclarationSymbolKind(node, kind), 'definition')] : [];
13483
+ }
13484
+ if (kind === 'KtTypeAlias') {
13485
+ const name = kotlinPsiDeclarationName(node, kind);
13486
+ return name ? [declarationRecord(input, nativeNodeId, name, 'type', 'definition')] : [];
13487
+ }
13488
+ if (kind === 'KtNamedFunction') {
13489
+ const name = kotlinPsiDeclarationName(node, kind);
13490
+ return name ? [declarationRecord(input, nativeNodeId, name, node.parentKind && kotlinPsiTypeDeclarationKind(node.parentKind) ? 'method' : 'function', kotlinPsiHasBody(node) ? 'definition' : 'declaration')] : [];
13491
+ }
13492
+ if (kind === 'KtProperty') {
13493
+ return kotlinPsiVariableNames(node).map((name) => declarationRecord(input, nativeNodeId, name, 'property', 'definition'));
13494
+ }
13495
+ if (kind === 'KtParameter' && node.parentKind === 'KtPrimaryConstructor') {
13496
+ return kotlinPsiVariableNames(node).map((name) => declarationRecord(input, nativeNodeId, name, 'property', 'definition'));
13497
+ }
13498
+ if (kind === 'KtPrimaryConstructor' || kind === 'KtSecondaryConstructor') {
13499
+ return [declarationRecord(input, nativeNodeId, kotlinPsiDeclarationName(node, kind) ?? 'constructor', 'method', 'definition')];
13500
+ }
13501
+ if (kind === 'KtEnumEntry') {
13502
+ const name = kotlinPsiDeclarationName(node, kind);
13503
+ return name ? [declarationRecord(input, nativeNodeId, name, 'enumMember', 'definition')] : [];
13504
+ }
13505
+ return [];
13506
+ }
13507
+
13508
+ function kotlinPsiChildEntries(node, kind = kotlinPsiKind(node)) {
13509
+ const fieldNames = Object.keys(node).filter((key) => !ignoredKotlinPsiField(key));
13510
+ const entries = [];
13511
+ for (const field of fieldNames) {
13512
+ const value = node[field];
13513
+ if (Array.isArray(value)) {
13514
+ entries.push([field, value.map((entry) => kotlinPsiChildWithParent(entry, kind, field))]);
13515
+ continue;
13516
+ }
13517
+ if (value && typeof value === 'object') {
13518
+ entries.push([field, kotlinPsiChildWithParent(value, kind, field)]);
13519
+ }
13520
+ }
13521
+ return entries.filter(([, value]) => Array.isArray(value)
13522
+ ? value.some(isKotlinPsiNode)
13523
+ : isKotlinPsiNode(value));
13524
+ }
13525
+
13526
+ function kotlinPsiChildWithParent(entry, parentKind, parentField) {
13527
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return entry;
13528
+ if (!isKotlinPsiNode(entry)) return entry;
13529
+ return { parentKind, parentField, ...entry };
13530
+ }
13531
+
13532
+ function kotlinPsiNodeValue(node) {
13533
+ return kotlinPsiDeclarationName(node, kotlinPsiKind(node))
13534
+ ?? kotlinPsiImportPath(node)
13535
+ ?? kotlinPsiPackageName(node)
13536
+ ?? kotlinPsiTypeName(node.typeReference ?? node.returnTypeRef ?? node.type);
13537
+ }
13538
+
13539
+ function kotlinPsiDeclarationName(node, kind = kotlinPsiKind(node)) {
13540
+ if (!node || typeof node !== 'object') return undefined;
13541
+ if (kind === 'KtPrimaryConstructor' || kind === 'KtSecondaryConstructor') return 'constructor';
13542
+ if (kind === 'KtClassInitializer') return 'init';
13543
+ if (kind === 'KtObjectDeclaration' && node.isCompanion === true && !node.name && !node.nameIdentifier) return 'companion object';
13544
+ for (const key of ['nameIdentifier', 'identifier', 'name', 'simpleName', 'classId', 'fqName', 'id']) {
13545
+ const name = kotlinPsiName(node[key]);
13546
+ if (name) return name;
13547
+ }
13548
+ const variable = kotlinPsiVariableNames(node)[0];
13549
+ if (variable) return variable;
13550
+ return undefined;
13551
+ }
13552
+
13553
+ function kotlinPsiName(value) {
13554
+ if (!value) return undefined;
13555
+ if (typeof value === 'string') return value;
13556
+ if (typeof value.asString === 'string') return value.asString;
13557
+ if (typeof value.identifier === 'string') return value.identifier;
13558
+ if (typeof value.name === 'string') return value.name;
13559
+ if (typeof value.text === 'string') return value.text;
13560
+ if (typeof value.value === 'string') return value.value;
13561
+ if (typeof value.fqName === 'string') return value.fqName;
13562
+ if (value.shortName && value.shortName !== value) return kotlinPsiName(value.shortName);
13563
+ if (value.name && value.name !== value) return kotlinPsiName(value.name);
13564
+ if (value.identifier && value.identifier !== value) return kotlinPsiName(value.identifier);
13565
+ return undefined;
13566
+ }
13567
+
13568
+ function kotlinPsiImportPath(node) {
13569
+ if (!node || typeof node !== 'object') return undefined;
13570
+ const path = node.importedFqName ?? node.importedReference ?? node.importPath ?? node.path ?? node.name;
13571
+ if (typeof path === 'string') return path;
13572
+ if (path && typeof path === 'object') return kotlinPsiName(path.fqName ?? path.name ?? path);
13573
+ return undefined;
13574
+ }
13575
+
13576
+ function kotlinPsiPackageName(node) {
13577
+ if (!node || typeof node !== 'object') return undefined;
13578
+ const value = node.packageFqName ?? node.fqName ?? node.qualifiedName ?? node.packageName;
13579
+ if (typeof value === 'string') return value;
13580
+ if (value && typeof value === 'object') return kotlinPsiName(value);
13581
+ return undefined;
13582
+ }
13583
+
13584
+ function kotlinPsiVariableNames(node) {
13585
+ const variables = node.variables ?? node.entries ?? node.declarations;
13586
+ if (Array.isArray(variables)) return variables.map(kotlinPsiDeclarationName).filter(Boolean);
13587
+ const name = kotlinPsiName(node.nameIdentifier ?? node.identifier ?? node.name);
13588
+ return name ? [name] : [];
13589
+ }
13590
+
13591
+ function kotlinPsiTypeName(value) {
13592
+ if (!value) return undefined;
13593
+ if (typeof value === 'string') return value;
13594
+ if (typeof value.text === 'string') return value.text.trim();
13595
+ if (typeof value.name === 'string') return value.name;
13596
+ if (typeof value.fqName === 'string') return value.fqName;
13597
+ if (value.typeElement) return kotlinPsiTypeName(value.typeElement);
13598
+ if (value.typeReference) return kotlinPsiTypeName(value.typeReference);
13599
+ if (value.referencedName) return kotlinPsiName(value.referencedName);
13600
+ if (value.constructorReferenceExpression) return kotlinPsiTypeName(value.constructorReferenceExpression);
13601
+ if (Array.isArray(value.typeArguments) || Array.isArray(value.arguments)) {
13602
+ const base = kotlinPsiName(value.name ?? value.referencedName ?? value.constructorReferenceExpression);
13603
+ const args = (value.typeArguments ?? value.arguments).map((entry) => kotlinPsiTypeName(entry.typeReference ?? entry)).filter(Boolean);
13604
+ return base ? `${base}<${args.join(', ')}>` : undefined;
13605
+ }
13606
+ return kotlinPsiName(value);
13607
+ }
13608
+
13609
+ function kotlinPsiModifiers(node) {
13610
+ const raw = node.modifiers ?? node.modifierList?.modifiers ?? node.modifierList?.children;
13611
+ const names = [];
13612
+ if (Array.isArray(raw)) {
13613
+ for (const entry of raw) {
13614
+ const name = typeof entry === 'string' ? entry : kotlinPsiName(entry) ?? entry?.keyword ?? entry?.tokenType ?? entry?.text;
13615
+ if (name) names.push(String(name).toLowerCase());
13616
+ }
13617
+ } else if (typeof raw === 'string') {
13618
+ names.push(...raw.split(/\s+/).filter(Boolean).map((entry) => entry.toLowerCase()));
13619
+ } else if (raw && typeof raw === 'object') {
13620
+ for (const [key, enabled] of Object.entries(raw)) {
13621
+ if (enabled === true) names.push(key.toLowerCase());
13622
+ }
13623
+ }
13624
+ for (const key of ['suspend', 'expect', 'actual', 'inline', 'operator', 'infix', 'tailrec', 'external', 'override', 'data', 'sealed', 'value']) {
13625
+ if (node[key] === true || node[`is${upperFirst(key)}`] === true) names.push(key);
13626
+ }
13627
+ return uniqueStrings(names.map((entry) => entry.replace(/keyword$/i, '').replace(/_keyword$/i, '').replace(/[_\s-]+/g, '').toLowerCase()).filter(Boolean));
13628
+ }
13629
+
13630
+ function kotlinPsiAnnotationNames(node) {
13631
+ const entries = node.annotationEntries ?? node.annotations ?? node.modifierList?.annotationEntries;
13632
+ if (!entries) return [];
13633
+ if (Array.isArray(entries)) {
13634
+ return uniqueStrings(entries.map((entry) => typeof entry === 'string' ? entry : kotlinPsiName(entry.shortName ?? entry.calleeExpression ?? entry.typeReference ?? entry.name ?? entry)).filter(Boolean));
13635
+ }
13636
+ return [];
13637
+ }
13638
+
13639
+ function kotlinPsiTypeDeclarationKind(kind) {
13640
+ return kind === 'KtClass'
13641
+ || kind === 'KtClassOrObject'
13642
+ || kind === 'KtObjectDeclaration';
13643
+ }
13644
+
13645
+ function kotlinPsiTypeDeclarationSymbolKind(node, kind) {
13646
+ const classKind = String(node.classKind ?? node.kindKeyword ?? '').toLowerCase();
13647
+ if (kind === 'KtObjectDeclaration') return 'class';
13648
+ if (classKind.includes('interface')) return 'interface';
13649
+ if (classKind.includes('enum')) return 'enum';
13650
+ if (classKind.includes('annotation')) return 'type';
13651
+ return 'class';
13652
+ }
13653
+
13654
+ function kotlinPsiHasBody(node) {
13655
+ return Boolean(node.bodyExpression || node.bodyBlockExpression || node.body || Array.isArray(node.statements));
13656
+ }
13657
+
13658
+ function kotlinPsiRecoveredKind(kind) {
13659
+ return kind === 'PsiErrorElement'
13660
+ || kind === 'KtErrorElement'
13661
+ || /Error|Recovery|Incomplete|Missing|Skipped/.test(String(kind));
13662
+ }
13663
+
13664
+ function kotlinPsiProblemNode(node, kind) {
13665
+ return Boolean(
13666
+ node.hasError
13667
+ || node.containsDiagnostics
13668
+ || node.containsSkippedText
13669
+ || node.isMissing
13670
+ || kind === 'PsiErrorElement'
13671
+ || kind === 'KtErrorElement'
13672
+ );
13673
+ }
13674
+
13675
+ function kotlinExpectActualNode(node) {
13676
+ const modifiers = kotlinPsiModifiers(node);
13677
+ return modifiers.includes('expect') || modifiers.includes('actual');
13678
+ }
13679
+
13680
+ function kotlinCoroutineNode(node, kind) {
13681
+ const modifiers = kotlinPsiModifiers(node);
13682
+ return modifiers.includes('suspend')
13683
+ || /Coroutine|Suspend/i.test(String(kind))
13684
+ || node.isSuspend === true
13685
+ || node.suspend === true;
13686
+ }
13687
+
13688
+ function kotlinContractNode(kind) {
13689
+ return /Contract/i.test(String(kind));
13690
+ }
13691
+
13692
+ function kotlinCompilerPluginAnnotationNode(node) {
13693
+ const names = kotlinPsiAnnotationNames(node);
13694
+ return names.some((name) => /^(Composable|Serializable|Parcelize|Entity|Immutable|Stable|AutoService|AssistedInject|Hilt|Inject|Room|KSerializable)$/i.test(name.replace(/^.*\./, '')));
13695
+ }
13696
+
13697
+ function kotlinGeneratedCodeMarker(node) {
13698
+ if (node.generated || node.isGenerated) return true;
13699
+ const path = String(node.filePath ?? node.path ?? node.sourcePath ?? '');
13700
+ return kotlinGeneratedSourcePath(path);
13701
+ }
13702
+
13703
+ function kotlinGeneratedSourcePath(path) {
13704
+ return typeof path === 'string' && (/(\.g|\.generated)\.kts?$/i.test(path) || /[\/\\](build|generated|ksp|kapt)[\/\\]/i.test(path));
13705
+ }
13706
+
13707
+ function kotlinGeneratedCodeLoss(input, nodeId, span, options = {}, metadata = {}) {
13708
+ return {
13709
+ id: `loss_${idFragment(nodeId ?? input.sourcePath ?? 'kotlin')}_kotlin_generated_code`,
13710
+ severity: 'warning',
13711
+ phase: 'parse',
13712
+ sourceFormat: input.language,
13713
+ kind: 'generatedCode',
13714
+ message: 'Kotlin generated-source marker was imported; generated member provenance and source ownership require host evidence.',
13715
+ span,
13716
+ nodeId,
13717
+ metadata: {
13718
+ parser: options.parser,
13719
+ astFormat: options.astFormat,
13720
+ kspEvidence: kotlinEvidenceSummary(options.kspEvidence),
13721
+ kaptEvidence: kotlinEvidenceSummary(options.kaptEvidence),
13722
+ compilerPluginEvidence: kotlinEvidenceSummary(options.compilerPluginEvidence),
13723
+ ...metadata
13724
+ }
13725
+ };
13726
+ }
13727
+
13728
+ function kotlinScriptLoss(input, nodeId, span, options = {}) {
13729
+ return {
13730
+ id: `loss_${idFragment(nodeId ?? input.sourcePath ?? 'kotlin')}_kotlin_script_semantics`,
13731
+ severity: 'warning',
13732
+ phase: 'parse',
13733
+ sourceFormat: input.language,
13734
+ kind: 'unsupportedSemantic',
13735
+ message: 'Kotlin script source was imported; script templates, implicit receivers, dependencies, and host execution environment require build/runtime evidence.',
13736
+ span,
13737
+ nodeId,
13738
+ metadata: {
13739
+ parser: options.parser,
13740
+ astFormat: options.astFormat,
13741
+ feature: 'script',
13742
+ unsupportedSemanticKind: 'kotlin.scriptContext',
13743
+ script: true,
13744
+ buildVariantEvidence: kotlinEvidenceSummary(options.buildVariantEvidence)
13745
+ }
13746
+ };
13747
+ }
13748
+
13749
+ function kotlinUnsupportedSemanticLoss(input, nodeId, span, options = {}, metadata = {}) {
13750
+ return {
13751
+ id: `loss_${idFragment(nodeId ?? input.sourcePath ?? 'kotlin')}_kotlin_${idFragment(metadata.feature ?? 'semantic')}`,
13752
+ severity: 'warning',
13753
+ phase: 'parse',
13754
+ sourceFormat: input.language,
13755
+ kind: 'unsupportedSemantic',
13756
+ message: metadata.message ?? 'Kotlin semantic feature requires host compiler evidence.',
13757
+ span,
13758
+ nodeId,
13759
+ metadata: {
13760
+ parser: options.parser,
13761
+ astFormat: options.astFormat,
13762
+ unsupportedSemanticKind: metadata.unsupportedSemanticKind ?? `kotlin.${idFragment(metadata.feature ?? 'semantic')}`,
13763
+ analysisApiEvidence: kotlinEvidenceSummary(options.analysisApiEvidence),
13764
+ firEvidence: kotlinEvidenceSummary(options.firEvidence),
13765
+ multiplatformEvidence: kotlinEvidenceSummary(options.multiplatformEvidence),
13766
+ ...metadata
13767
+ }
13768
+ };
13769
+ }
13770
+
13771
+ function kotlinEvidenceSummary(value) {
13772
+ if (!value) return undefined;
13773
+ if (Array.isArray(value)) return { entryCount: value.length };
13774
+ if (typeof value === 'string') return { value };
13775
+ if (typeof value === 'object') {
13776
+ const summary = {};
13777
+ if (typeof value.hash === 'string') summary.hash = value.hash;
13778
+ if (typeof value.solver === 'string') summary.solver = value.solver;
13779
+ if (Array.isArray(value.entries)) summary.entryCount = value.entries.length;
13780
+ if (Array.isArray(value.symbols)) summary.symbolCount = value.symbols.length;
13781
+ if (Array.isArray(value.references)) summary.referenceCount = value.references.length;
13782
+ if (Array.isArray(value.types)) summary.typeCount = value.types.length;
13783
+ if (Array.isArray(value.diagnostics)) summary.diagnosticCount = value.diagnostics.length;
13784
+ if (Array.isArray(value.plugins)) summary.pluginCount = value.plugins.length;
13785
+ if (Array.isArray(value.generatedSources)) summary.generatedSourceCount = value.generatedSources.length;
13786
+ if (Array.isArray(value.platforms)) summary.platformCount = value.platforms.length;
13787
+ if (Array.isArray(value.variants)) summary.variantCount = value.variants.length;
13788
+ return Object.keys(summary).length ? summary : { present: true };
13789
+ }
13790
+ return { present: true };
13791
+ }
13792
+
12351
13793
  function csharpRoslynRoot(value) {
12352
13794
  if (!value || typeof value !== 'object') return undefined;
12353
13795
  if (isCSharpRoslynNode(value)) return value;