@lcap/nasl-language-server-core 4.4.2-rc.4 → 4.4.3-rc.1

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.
Files changed (49) hide show
  1. package/out/checker.d.ts.map +1 -1
  2. package/out/checker.js +32 -16
  3. package/out/checker.js.map +1 -1
  4. package/out/typer/collectGlobalDefs.d.ts.map +1 -1
  5. package/out/typer/collectGlobalDefs.js +73 -7
  6. package/out/typer/collectGlobalDefs.js.map +1 -1
  7. package/out/typer/component-def-manager/component-def-manager.d.ts +65 -7
  8. package/out/typer/component-def-manager/component-def-manager.d.ts.map +1 -1
  9. package/out/typer/component-def-manager/component-def-manager.js +400 -8
  10. package/out/typer/component-def-manager/component-def-manager.js.map +1 -1
  11. package/out/typer/dispatch-call.d.ts.map +1 -1
  12. package/out/typer/dispatch-call.js +13 -1
  13. package/out/typer/dispatch-call.js.map +1 -1
  14. package/out/typer/dispatch-view.d.ts.map +1 -1
  15. package/out/typer/dispatch-view.js +342 -141
  16. package/out/typer/dispatch-view.js.map +1 -1
  17. package/out/typer/overload-helper.d.ts.map +1 -1
  18. package/out/typer/overload-helper.js +0 -3
  19. package/out/typer/overload-helper.js.map +1 -1
  20. package/out/typer/sem-diag.js +1 -1
  21. package/out/typer/sem-diag.js.map +1 -1
  22. package/out/typer/solver.d.ts.map +1 -1
  23. package/out/typer/solver.js +2 -2
  24. package/out/typer/solver.js.map +1 -1
  25. package/out/typer/subster.d.ts.map +1 -1
  26. package/out/typer/subster.js +54 -35
  27. package/out/typer/subster.js.map +1 -1
  28. package/out/typer/type-manager.d.ts.map +1 -1
  29. package/out/typer/type-manager.js +4 -0
  30. package/out/typer/type-manager.js.map +1 -1
  31. package/out/typer/type-predicate.d.ts +19 -1
  32. package/out/typer/type-predicate.d.ts.map +1 -1
  33. package/out/typer/type-predicate.js +325 -18
  34. package/out/typer/type-predicate.js.map +1 -1
  35. package/out/typer/typer.d.ts +9 -0
  36. package/out/typer/typer.d.ts.map +1 -1
  37. package/out/typer/typer.js +8 -0
  38. package/out/typer/typer.js.map +1 -1
  39. package/out/typer/unifier.d.ts +8 -0
  40. package/out/typer/unifier.d.ts.map +1 -1
  41. package/out/typer/unifier.js +34 -2
  42. package/out/typer/unifier.js.map +1 -1
  43. package/out/utils/file-node-cache.d.ts.map +1 -1
  44. package/out/utils/file-node-cache.js +5 -4
  45. package/out/utils/file-node-cache.js.map +1 -1
  46. package/out/utils/parseTsClassType.d.ts.map +1 -1
  47. package/out/utils/parseTsClassType.js +6 -0
  48. package/out/utils/parseTsClassType.js.map +1 -1
  49. package/package.json +5 -5
@@ -4,6 +4,7 @@ exports.isTagIgnored = exports.isAttrIgnored = exports.singletonValue = exports.
4
4
  const nasl_concepts_1 = require("@lcap/nasl-concepts");
5
5
  const type_manager_1 = require("./type-manager");
6
6
  const type_predicate_1 = require("./type-predicate");
7
+ const component_def_manager_1 = require("./component-def-manager/component-def-manager");
7
8
  const assertion_1 = require("../utils/assertion");
8
9
  const def_key_helpers_1 = require("../reference-manager/def-key-helpers");
9
10
  const reference_manager_1 = require("../reference-manager/reference-manager");
@@ -48,6 +49,13 @@ function* dispatchViewGenerator(env, nd, skipSubView = false) {
48
49
  if (isFileNode) {
49
50
  env.fileNodes.push(nd);
50
51
  }
52
+ // 统一在此处设置 frameworkKind,无论是全量还是增量入口(Frontend/View 直接进来时也能正确取到)
53
+ const frontendTypeAncestor = nd.concept === 'FrontendType'
54
+ ? nd
55
+ : nd.getAncestor?.('FrontendType');
56
+ if (frontendTypeAncestor) {
57
+ env.frameworkKind = frontendTypeAncestor.frameworkKind || 'vue2';
58
+ }
51
59
  try {
52
60
  env.isInMemberExpr = false; // 恢复到默认值,防止意外错误
53
61
  switch (nd.concept) {
@@ -189,8 +197,21 @@ function* tpBusinessComponent(env, nd) {
189
197
  });
190
198
  nd.elements.forEach((e) => {
191
199
  (0, traverse_util_1.traverseViewElements)(e, (ve) => {
192
- const ty = env.unifier.nextFreshTyAnn('free');
193
- env.setType(ve, ty);
200
+ const compResult = env.refMgr?.compDefMgr?.queryByViewElement(ve);
201
+ if (compResult) {
202
+ const componentDef = compResult.instantiate(env);
203
+ const instanceProps = componentDef.getInstanceProps();
204
+ const veTy = nasl_concepts_1.TypeAnnotation.createTypeAnonymousStructure(instanceProps.map((x) => {
205
+ x.id = 'placeholder';
206
+ return nasl_concepts_1.StructureProperty.createProtoTypeOnly(x);
207
+ }), { id: 'placeholder' });
208
+ env.setType(ve, veTy);
209
+ ve.__preInstantiatedHelper = componentDef;
210
+ }
211
+ else {
212
+ const ty = env.unifier.nextFreshTyAnn('free');
213
+ env.setType(ve, ty);
214
+ }
194
215
  const qName = (0, def_key_helpers_1.getGlobalDefKey)(ve);
195
216
  (0, assertion_1.assertTrusy)(qName);
196
217
  env.allocatedVEQNames.push(qName);
@@ -329,8 +350,26 @@ function* tpView(env, nd, skipSubView = false) {
329
350
  });
330
351
  nd.elements.forEach((e) => {
331
352
  (0, traverse_util_1.traverseViewElements)(e, (ve) => {
332
- const ty = env.unifier.nextFreshTyAnn('free');
333
- env.setType(ve, ty);
353
+ // logics 之前提前实例化组件定义,将 VE 的类型设为带有已重命名类型参数的
354
+ // 实际结构类型(如 { data: List<T'>, page: Integer, ... })。
355
+ // 这样 logics 中 elements.xxx.property.data 能获得完整的结构信息,
356
+ // 并通过 T'(fresh type var)在约束系统中与 ListTransform 等函数的类型参数关联。
357
+ // tpViewElementFirstPass 会复用此 helper,处理 bindAttrs 以解出 T' 的具体类型。
358
+ const compResult = env.refMgr?.compDefMgr?.queryByViewElement(ve);
359
+ if (compResult) {
360
+ const componentDef = compResult.instantiate(env);
361
+ const instanceProps = componentDef.getInstanceProps();
362
+ const veTy = nasl_concepts_1.TypeAnnotation.createTypeAnonymousStructure(instanceProps.map((x) => {
363
+ x.id = 'placeholder';
364
+ return nasl_concepts_1.StructureProperty.createProtoTypeOnly(x);
365
+ }), { id: 'placeholder' });
366
+ env.setType(ve, veTy);
367
+ ve.__preInstantiatedHelper = componentDef;
368
+ }
369
+ else {
370
+ const ty = env.unifier.nextFreshTyAnn('free');
371
+ env.setType(ve, ty);
372
+ }
334
373
  const qName = (0, def_key_helpers_1.getGlobalDefKey)(ve);
335
374
  (0, assertion_1.assertTrusy)(qName);
336
375
  env.allocatedVEQNames.push(qName);
@@ -401,12 +440,21 @@ const excludedViewElementPropNames = new Set([
401
440
  'formDesignerBuildInDefaults',
402
441
  'formDesignerCalcDefaultsTargetField',
403
442
  ]);
404
- function findAncestorWhichHasTypeParamT(env, nd) {
443
+ /**
444
+ * 在祖先组件链中,找到拥有自己数据源的 dataItem 类型参数(基于 TypeParamMeta.role,而非名字)。
445
+ */
446
+ function findAncestorDataItemTypeParam(env, nd) {
405
447
  let cur = nd.parentNode;
406
448
  while (cur) {
407
- const t = env.getDataSourceTyParam(cur)?.find((x) => x.rawName === 'T');
408
- if (t) {
409
- return t;
449
+ const wrappers = env.getDataSourceTyParam(cur);
450
+ if (wrappers) {
451
+ const match = wrappers.find((w) => {
452
+ const ancestorComponentDef = env.refMgr?.compDefMgr.queryByViewElement(cur)?.instantiate(env);
453
+ const meta = ancestorComponentDef?.getTypeParamMeta(w.rawName);
454
+ return meta?.role === 'dataItem' && meta.hasOwnDataSource;
455
+ });
456
+ if (match)
457
+ return match;
410
458
  }
411
459
  if ((0, asserts_1.isViewElement)(cur.parentNode)) {
412
460
  cur = cur.parentNode;
@@ -442,7 +490,8 @@ function* tpViewElementFirstPass(env, nd) {
442
490
  if ((0, file_node_cache_1.checkNodeIsFileModuleInIdeWithCache)(nd)) {
443
491
  env.fileNodes.push(nd);
444
492
  }
445
- const componentDef = env.refMgr.compDefMgr.queryByViewElement(nd)?.instantiate(env);
493
+ const componentDef = nd.__preInstantiatedHelper
494
+ ?? env.refMgr.compDefMgr.queryByViewElement(nd)?.instantiate(env);
446
495
  yield;
447
496
  if (!componentDef) {
448
497
  env.addDiagnosticsFor(`${nd.tag} 组件的定义缺失`, nd, 'warning');
@@ -451,9 +500,55 @@ function* tpViewElementFirstPass(env, nd) {
451
500
  env.addError(`[DEBUG] ${nd.tag} 组件的定义缺失`);
452
501
  }
453
502
  }
454
- env.setDataSourceTyParam(nd, componentDef?.getRenamedTypeParamWrappers() ?? []);
503
+ const typeParamWrappers = componentDef?.getRenamedTypeParamWrappers() ?? [];
504
+ env.setDataSourceTyParam(nd, typeParamWrappers);
505
+ // 从父组件的 slot 返回类型统一泛型参数(按位置,不按名字)
506
+ // componentType 可能是 union,resolveComponentTypeForTag 会找到匹配的分支
507
+ const resolvedParentType = env.componentType?.typeKind === 'union'
508
+ ? resolveComponentTypeForTag(env.componentType, nd.tag)
509
+ : (env.componentType?.typeName === component_def_manager_1.ComponentTagUtils.normalizeTag(nd.tag)
510
+ ? env.componentType
511
+ : undefined);
512
+ if (resolvedParentType) {
513
+ typeParamWrappers.forEach((typeParam, index) => {
514
+ const pa = resolvedParentType.typeArguments?.[index];
515
+ if (typeParam?.renamed && pa) {
516
+ env.unifier.unify(typeParam.renamed, pa);
517
+ }
518
+ });
519
+ }
520
+ // 对于没有自己数据源但角色是 dataItem 的类型参数,从祖先组件继承
521
+ if (componentDef) {
522
+ typeParamWrappers.forEach((w) => {
523
+ const meta = componentDef.getTypeParamMeta(w.rawName);
524
+ if (meta?.role === 'dataItem' && !meta.hasOwnDataSource) {
525
+ const ancestorParam = findAncestorDataItemTypeParam(env, nd);
526
+ if (ancestorParam) {
527
+ env.unifier.unify(w.renamed, ancestorParam.renamed);
528
+ }
529
+ }
530
+ });
531
+ }
455
532
  const initProp = componentDef?.getInitPropsTypeAnnotation();
456
533
  const instanceProps = componentDef?.getInstanceProps();
534
+ // 判断某个类型变元是否在初始化属性里以
535
+ // typeParam | String | Boolean | Integer
536
+ // 这类 union 形式出现,用于在完全无约束的情况下兜底为 String。
537
+ const isStringishUnionParam = (wrapper) => {
538
+ const props = initProp?.properties ?? [];
539
+ return props.some((p) => {
540
+ const ty = p.typeAnnotation;
541
+ if (!ty || ty.typeKind !== 'union' || !ty.typeArguments?.length)
542
+ return false;
543
+ // 通过类型形状而不是引用相等来识别同一个类型变元
544
+ const hasThisParam = ty.typeArguments.some((arg) => arg?.typeKind === 'typeParam' && arg.typeName === wrapper.renamed.typeName);
545
+ if (!hasThisParam)
546
+ return false;
547
+ const hasStringLike = ty.typeArguments.some((arg) => arg?.typeNamespace === 'nasl.core' &&
548
+ (arg.typeName === 'String' || arg.typeName === 'Boolean' || arg.typeName === 'Integer'));
549
+ return hasThisParam && hasStringLike;
550
+ });
551
+ };
457
552
  /**
458
553
  * 线性复杂度根据name寻找给定的property
459
554
  * @param name
@@ -477,49 +572,56 @@ function* tpViewElementFirstPass(env, nd) {
477
572
  }
478
573
  return prop?.typeAnnotation ?? type_manager_1.naslAnyTy;
479
574
  };
480
- const typeParamWrappers = componentDef?.getRenamedTypeParamWrappers() ?? [];
575
+ // slot 返回类型中提取子组件类型(可能是 Array/List 包裹的 union)
576
+ function extractComponentTypeFromSlotReturn(slotProp) {
577
+ let ty = slotProp?.typeAnnotation?.returnType?.[0];
578
+ if (ty?.typeName === 'List' || ty?.typeName === 'Array') {
579
+ ty = ty?.typeArguments?.[0];
580
+ }
581
+ return ty;
582
+ }
583
+ // 从一个可能是 union 的类型中,找到与 childTag 匹配的那个分支
584
+ function resolveComponentTypeForTag(ty, childTag) {
585
+ if (!ty)
586
+ return undefined;
587
+ const normalizedChildTag = component_def_manager_1.ComponentTagUtils.normalizeTag(childTag);
588
+ // 直接匹配
589
+ if (ty.typeName === normalizedChildTag)
590
+ return ty;
591
+ // union: 遍历 typeArguments 找匹配的分支
592
+ if (ty.typeKind === 'union' && ty.typeArguments?.length) {
593
+ return ty.typeArguments.find((alt) => alt && component_def_manager_1.ComponentTagUtils.normalizeTag(alt.typeName ?? '') === normalizedChildTag);
594
+ }
595
+ return undefined;
596
+ }
481
597
  function makeCurrentManager(nd) {
482
- // currentManager机制分为两部分:
483
- // 1. 维护类型变元的约束
484
- // 2. current变量的创建和销毁
485
598
  if (!componentDef) {
486
599
  return;
487
600
  }
488
- const T1 = typeParamWrappers.find((x) => x.rawName === 'T1');
489
- const T = typeParamWrappers.find((x) => x.rawName === 'T');
490
- // 以下两个情形,从祖先组件中获取 T
491
- if (T1 && T) {
492
- // 如果当前组件定义了 T1 和 T,那么就必定从祖先组件中获取T
493
- const ancestorTableTypeParam = findAncestorWhichHasTypeParamT(env, nd)?.renamed ?? type_manager_1.naslAnyTy;
494
- env.unifier.unify(T.renamed, ancestorTableTypeParam);
495
- }
496
- // 如果当前组件没有定义 dataSource 且有 T,就需要从祖先组件中获取 T
497
- // u-transfer 使用 source 而非 dataSource 作为数据源
498
- const hasDataSourceInInitProp = !!componentDef
499
- .getInitPropsTypeAnnotation()
500
- ?.properties?.some((x) => x.name === 'dataSource' || (nd.tag === 'u-transfer' && x.name === 'source'));
501
- const shouldStealAncestorT = !hasDataSourceInInitProp;
502
- if (shouldStealAncestorT && T) {
503
- const ancestorTableTypeParam = findAncestorWhichHasTypeParamT(env, nd)?.renamed ?? type_manager_1.naslAnyTy;
504
- env.unifier.unify(T.renamed, ancestorTableTypeParam);
505
- }
601
+ // 预先解析 default slot 的返回类型,供非 template 直接子组件使用
602
+ const defaultSlotProp = componentDef.getSlotPropBySlotTarget('default');
603
+ const defaultSlotComponentType = extractComponentTypeFromSlotReturn(defaultSlotProp);
506
604
  return {
507
605
  create: (options) => {
508
- if (options.templateElement?.tag !== 'template') {
509
- return undefined;
510
- }
511
- const slotTarget = options.templateElement?.slotTarget ?? 'default';
512
- const slotScopeType = componentDef?.getSlotScopeTypeBySlotTarget(slotTarget);
513
- // 如果curTy非空,那么才需要引入current变量。否则完全不需要引入。
514
- let curTy = slotScopeType?.[0]?.type;
515
- if (curTy) {
516
- // 引入变量 current_i: CurTy
517
- const curName = env.unifier.nextFreshCurrent();
518
- const curVar = (0, collectGlobalDefs_1.createVarAndSetParentNode)(curName, curTy, nd);
519
- env.setType(curVar, curTy);
520
- env.addLexicalSym(curVar);
606
+ const child = options.templateElement;
607
+ if (child?.tag === 'template') {
608
+ // template slot: 创建 current 变量 + 提供 slot 级别的 componentType
609
+ const slotTarget = child.slotTarget ?? 'default';
610
+ const slotProp = componentDef?.getSlotPropBySlotTarget(slotTarget);
611
+ const slotScopeType = slotProp ? componentDef?.getSlotScopeType(slotProp) : null;
612
+ const slotComponentType = extractComponentTypeFromSlotReturn(slotProp);
613
+ let curTy = slotScopeType?.[0]?.type;
614
+ let curName;
615
+ let curVar;
616
+ if (curTy) {
617
+ curName = env.unifier.nextFreshCurrent();
618
+ curVar = (0, collectGlobalDefs_1.createVarAndSetParentNode)(curName, curTy, nd);
619
+ env.setType(curVar, curTy);
620
+ env.addLexicalSym(curVar);
621
+ }
521
622
  return {
522
623
  createdVar: curVar,
624
+ componentType: slotComponentType,
523
625
  revoke: () => {
524
626
  if (curName) {
525
627
  env.unifier.decreaseCurrentCounter();
@@ -528,6 +630,17 @@ function* tpViewElementFirstPass(env, nd) {
528
630
  },
529
631
  };
530
632
  }
633
+ // 非 template 直接子组件:使用 default slot 的返回类型
634
+ // 从 union 中按 tag 找到对应的分支,从而实现按位置统一泛型
635
+ const matched = resolveComponentTypeForTag(defaultSlotComponentType, child?.tag ?? '');
636
+ if (matched) {
637
+ return {
638
+ createdVar: undefined,
639
+ componentType: matched,
640
+ revoke: () => { },
641
+ };
642
+ }
643
+ return undefined;
531
644
  },
532
645
  };
533
646
  }
@@ -559,8 +672,34 @@ function* tpViewElementFirstPass(env, nd) {
559
672
  }
560
673
  });
561
674
  }
562
- env.unifier.unify(env.getRawType(nd), veTy);
563
- env.typeBindings.set(nd, veTy);
675
+ // 如果 VE 已经在 tpView 中被预实例化(类型已经是 anonymousStructure),
676
+ // 跳过 unify 以避免将同一结构的属性类型与自身统一导致自引用约束。
677
+ // 否则(VE 类型是 freshTyVar),通过 unify 连接 freshTyVar 与实际类型。
678
+ const existingTy = env.getRawType(nd);
679
+ if (!existingTy || existingTy.typeKind !== 'anonymousStructure') {
680
+ env.unifier.unify(existingTy, veTy);
681
+ }
682
+ env.setType(nd, veTy);
683
+ // 在完全求解后,对仍未解出的类型变元做默认兜底:
684
+ // 1. dataItem 角色(无论是否有自己的数据源):当数据源未设置且无其他约束能推出具体类型时,兜底为 String
685
+ // 2. 形状类似 T | String | Boolean | Integer 的 union 参数:兜底为 String
686
+ if (typeParamWrappers.length) {
687
+ env.addPostCheckTask(nd, (env, pSubst) => {
688
+ typeParamWrappers.forEach((w) => {
689
+ const solved = pSubst(w.renamed);
690
+ if (!(0, type_predicate_1.isUnResolvedTy)(solved))
691
+ return;
692
+ const meta = componentDef?.getTypeParamMeta(w.rawName);
693
+ if (meta?.role === 'dataItem') {
694
+ env.unifier.unify(w.renamed, meta.defaultWhenUnresolved ?? type_manager_1.naslStringTy);
695
+ return;
696
+ }
697
+ if (isStringishUnionParam(w)) {
698
+ env.unifier.unify(w.renamed, type_manager_1.naslStringTy);
699
+ }
700
+ });
701
+ });
702
+ }
564
703
  const ceTy = nasl_concepts_1.TypeAnnotation.createProtoTypeOnly({
565
704
  typeName: 'IElements',
566
705
  typeKind: 'anonymousStructure',
@@ -577,7 +716,7 @@ function* tpViewElementFirstPass(env, nd) {
577
716
  const elements = (0, collectGlobalDefs_1.createVarAndSetParentNode)('elements', ceTy, nd);
578
717
  // env.setType(elements, ceTy);
579
718
  // 省点内存
580
- env.typeBindings.set(elements, ceTy);
719
+ env.setType(elements, ceTy);
581
720
  env.addLexicalSym(elements);
582
721
  const dropList = [];
583
722
  dropList.push(elements);
@@ -633,8 +772,11 @@ function* tpViewElementFirstPass(env, nd) {
633
772
  dropList.push(popper.createdVar);
634
773
  (0, helper_1.createOnPush)(env.allocatedVirtualNodes, env.getCurFileNode(), popper.createdVar);
635
774
  }
775
+ // 存在组件类型
776
+ env.componentType = popper?.componentType;
636
777
  yield* tpViewElementFirstPass(env, e);
637
778
  popper?.revoke();
779
+ env.componentType = undefined;
638
780
  });
639
781
  /**
640
782
  * 特性:部分元素的 V 类型可以由子元素的 V 推导得到。同时又尊重此元素本身引入的 V 约束
@@ -675,22 +817,32 @@ function* tpViewElementFirstPass(env, nd) {
675
817
  * 当前ViewElement的children中第一个非template的 BindAttribute[name='value'](若无,则为其他类型为V的字段)的类型
676
818
  */
677
819
  /**
678
- * 如果当前元素没有T1,且当前元素的定义中有类型变元 V,而子级组件也有类似的变元 V',那么需要unify(V', V)。
679
- * 以此将子元素的 V' 约束贡献给当前元素的 V
820
+ * 如果当前组件有 valueResult 类型变元,且没有自己的额外数据源(role = 'dataItem' 且 hasOwnDataSource 的第二个),
821
+ * 则子组件同角色的 valueResult 应当贡献到当前组件的 valueResult 上。
822
+ * 这里通过 role 匹配,而不是看名字是否是 'V' / 'T1'。
680
823
  */
681
- const vThisLevel = typeParamWrappers.find((x) => x.rawName === 'V')?.renamed;
682
- const shouldVThisLevelBeInferred = !typeParamWrappers.find((x) => x.rawName === 'T1') &&
683
- // 若当前 V 无任何约束
684
- !!vThisLevel &&
685
- env.unifier.containsEmptyBounds(vThisLevel);
686
- if (shouldVThisLevelBeInferred) {
824
+ const valueResultWrapper = typeParamWrappers.find((w) => {
825
+ const m = componentDef?.getTypeParamMeta(w.rawName);
826
+ return m?.role === 'valueResult';
827
+ });
828
+ const hasMultipleDataItems = typeParamWrappers.filter((w) => {
829
+ const m = componentDef?.getTypeParamMeta(w.rawName);
830
+ return m?.role === 'dataItem' && m.hasOwnDataSource;
831
+ }).length > 1;
832
+ const shouldValueBeInferred = !hasMultipleDataItems &&
833
+ !!valueResultWrapper &&
834
+ env.unifier.containsEmptyBounds(valueResultWrapper.renamed);
835
+ if (shouldValueBeInferred) {
687
836
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(nd.children, function* (e) {
688
- // HACK: 简单的关联组件判断,后续可以考虑更复杂的判断
689
- if (e.tag.startsWith(nd.tag) ||
690
- (e.tag === 'el-option' && ['el-select', 'el-form-select'].includes(nd.tag))) {
691
- const vChild = env.getDataSourceTyParam(e)?.find((x) => x.rawName === 'V')?.renamed;
837
+ if (e.tag.startsWith(nd.tag)) {
838
+ const childWrappers = env.getDataSourceTyParam(e);
839
+ const vChild = childWrappers?.find((cw) => {
840
+ const childDef = env.refMgr?.compDefMgr.queryByViewElement(e)?.instantiate(env);
841
+ const cm = childDef?.getTypeParamMeta(cw.rawName);
842
+ return cm?.role === 'valueResult';
843
+ })?.renamed;
692
844
  if (vChild) {
693
- env.unifier.unify(vChild, vThisLevel);
845
+ env.unifier.unify(vChild, valueResultWrapper.renamed);
694
846
  }
695
847
  }
696
848
  });
@@ -703,8 +855,10 @@ function* tpViewElementFirstPass(env, nd) {
703
855
  dropList.forEach((x) => {
704
856
  env.removeLexicalVarByName(x.name);
705
857
  });
706
- // el-table-column-dynamic,u-table-view-column-dynamic,T/T1 特殊处理
707
- const isColumnDynamic = ['el-table-column-dynamic', 'u-table-view-column-dynamic'].includes(nd?.tag ?? '');
858
+ /**
859
+ * 通用的类型变元 fallback 过程:
860
+ * 完全基于 TypeParamMeta.role 驱动,不依赖任何 rawName 硬编码。
861
+ */
708
862
  const isBindAttrEmptyExpression = (name) => {
709
863
  const attr = nd.bindAttrs?.find((attr) => attr.name === name);
710
864
  if (!attr) {
@@ -714,98 +868,108 @@ function* tpViewElementFirstPass(env, nd) {
714
868
  return attr.type === 'dynamic' && !attr.expression;
715
869
  }
716
870
  };
717
- // 组件语义化的类型变元 fallback 规则
718
- typeParamWrappers.forEach((x) => {
719
- if (x.rawName === 'T') {
720
- // el-list-components 开启表单模式 formMode,会清除 dataSource,T 类型变元会根据表单值 model 推导,因此不需要 fallback 规则
721
- if (nd?.tag === 'el-list-components') {
722
- const formModeAttr = nd.bindAttrs?.find((attr) => attr.name === 'formMode');
723
- if (formModeAttr?.type === 'static' && formModeAttr?.value === true) {
724
- return;
725
- }
871
+ const inferTypeFromPropMeta = (cfg, nd) => {
872
+ const attr = nd.bindAttrs?.find((a) => a.name === cfg.propName);
873
+ if (!attr) {
874
+ if (cfg.mode === 'booleanFlag')
875
+ return nasl_concepts_1.TypeAnnotation.createLiteral(false);
876
+ // 日期类组件 converter 未配置时,按组件默认语义视为 'auto'
877
+ if (cfg.mode === 'converterLiteral' && cfg.propName === 'converter') {
878
+ return nasl_concepts_1.TypeAnnotation.createLiteral('auto');
726
879
  }
727
- // ColumnDynamic 场景下,T 类型变元从 table 中推导,不需要 fallback 规则
728
- if (isColumnDynamic) {
729
- return;
880
+ return undefined;
881
+ }
882
+ if (cfg.mode === 'booleanFlag') {
883
+ if (['static', 'string'].includes(attr.type ?? '')) {
884
+ return nasl_concepts_1.TypeAnnotation.createLiteral(!!attr.value);
730
885
  }
731
- // 如果数据源未定义或为空,则不可能有任何合适的解,回落到 String 类型
732
- const isEmpty = isBindAttrEmptyExpression('dataSource');
733
- if (isEmpty) {
734
- env.unifier.unify(x.renamed, type_manager_1.naslStringTy);
886
+ return type_manager_1.naslBooleanTy;
887
+ }
888
+ if (cfg.mode === 'converterLiteral') {
889
+ if (['static', 'string'].includes(attr.type ?? '')) {
890
+ if ([true, 'true'].includes(attr.value)) {
891
+ return nasl_concepts_1.TypeAnnotation.createLiteral(true);
892
+ }
893
+ if ([false, 'false'].includes(attr.value)) {
894
+ return nasl_concepts_1.TypeAnnotation.createLiteral(false);
895
+ }
896
+ return nasl_concepts_1.TypeAnnotation.createLiteral(String(attr.value));
735
897
  }
898
+ return nasl_concepts_1.TypeAnnotation.createLiteral('');
736
899
  }
737
- else if (x.rawName === 'T1') {
738
- // ColumnDynamic 场景下,T1 类型变元需要 fallback 规则
739
- // 如果数据源未定义或为空,则不可能有任何合适的解,回落到 String 类型
740
- if (isColumnDynamic && isBindAttrEmptyExpression('dataSource')) {
741
- env.unifier.unify(x.renamed, type_manager_1.naslStringTy);
900
+ if (cfg.mode === 'stringLiteral') {
901
+ if (['static', 'string'].includes(attr.type ?? '')) {
902
+ return nasl_concepts_1.TypeAnnotation.createLiteral(String(attr.value ?? ''));
742
903
  }
904
+ return type_manager_1.naslStringTy;
743
905
  }
744
- if (env.unifier.containsEmptyBounds(x.renamed)) {
745
- if (x.rawName === 'T' || x.rawName === 'V') {
746
- // 兼容旧规则: 如果组件的T V类型变元没有约束,那么它就是 String
747
- env.unifier.unify(x.renamed, type_manager_1.naslStringTy);
906
+ return undefined;
907
+ };
908
+ typeParamWrappers.forEach((wrapper) => {
909
+ const meta = componentDef?.getTypeParamMeta(wrapper.rawName);
910
+ if (!meta) {
911
+ // 没有 meta 的类型参数(如 u-transfer 的 V,仅出现在事件处理器参数结构体内)
912
+ // 也需要注册到约束系统中,否则 solver 无法将其兜底为 any,
913
+ // 导致包含该类型参数的函数类型在 postCheck 中被判为 unresolved 而误报。
914
+ if (wrapper.renamed?.typeName && env.unifier.containsEmptyBounds(wrapper.renamed)) {
915
+ env.unifier.unify(wrapper.renamed, type_manager_1.naslAnyTy);
748
916
  }
749
- else if (x.rawName === 'C') {
750
- // 规则引自 ViewElement的toEmbeddedTSRefTree
751
- const getConverterDefaultValue = (name) => {
752
- const attr = nd.bindAttrs?.find((attr) => attr.name === name);
753
- if (attr) {
754
- if (['static', 'string'].includes(attr?.type ?? "")) {
755
- if ([true, 'true'].includes(attr.value)) {
756
- return nasl_concepts_1.TypeAnnotation.createLiteral(true);
757
- }
758
- else if ([false, 'false'].includes(attr.value)) {
759
- return nasl_concepts_1.TypeAnnotation.createLiteral(false);
760
- }
761
- else {
762
- return nasl_concepts_1.TypeAnnotation.createLiteral(String(attr.value));
763
- }
764
- }
765
- }
766
- return nasl_concepts_1.TypeAnnotation.createLiteral("");
767
- };
768
- // 兼容旧规则: 如果组件的C类型变元没有约束,那么它就是...
769
- env.unifier.unify(x.renamed, getConverterDefaultValue('converter'));
917
+ return;
918
+ }
919
+ // dataItem 且有自己的数据源:仅在数据源为空且无具体类型约束时回落到默认类型。
920
+ // 预实例化后逻辑可能已为 T 建立了纯类型参数约束(如 T T_lt),
921
+ // 这些不携带具体信息,不应阻止兜底;但 model 等属性提供的具体类型约束应阻止兜底。
922
+ if (meta.role === 'dataItem' && meta.hasOwnDataSource && meta.dataSourcePropName) {
923
+ if (isBindAttrEmptyExpression(meta.dataSourcePropName) && meta.defaultWhenUnresolved
924
+ && env.unifier.hasNoConcreteBounds(wrapper.renamed)) {
925
+ env.unifier.unify(wrapper.renamed, meta.defaultWhenUnresolved);
770
926
  }
771
- else if (x.rawName === 'M') {
772
- // 规则引自 ViewElement的toEmbeddedTSRefTree
773
- const getPropInitialValue = (name) => {
774
- const attr = nd.bindAttrs?.find((attr) => attr.name === name);
775
- if (attr && ['static', 'string'].includes(attr?.type ?? "")) {
776
- return nasl_concepts_1.TypeAnnotation.createLiteral(!!attr.value);
777
- }
778
- return type_manager_1.naslBooleanTy;
779
- };
780
- // 兼容旧规则: 如果组件的M类型变元没有约束,那么它就是...
781
- env.unifier.unify(x.renamed, getPropInitialValue('multiple'));
927
+ return;
928
+ }
929
+ // dataItem 但没有自己的数据源(从祖先继承):祖先的类型参数 bound 是有意义的约束,
930
+ // 必须用 containsEmptyBounds(而非 hasNoConcreteBounds)来防止覆盖祖先传播的类型。
931
+ if (meta.role === 'dataItem' && !meta.hasOwnDataSource) {
932
+ if (meta.defaultWhenUnresolved && env.unifier.containsEmptyBounds(wrapper.renamed)) {
933
+ env.unifier.unify(wrapper.renamed, meta.defaultWhenUnresolved);
782
934
  }
783
- else if (x.rawName === 'P') {
784
- // 规则引自 ViewElement的toEmbeddedTSRefTree
785
- const getPropInitialValue = (name) => {
786
- const attr = nd.bindAttrs?.find((attr) => attr.name === name);
787
- if (attr && ['static', 'string'].includes(attr?.type ?? "")) {
788
- return nasl_concepts_1.TypeAnnotation.createLiteral(!!attr.value);
789
- }
790
- return type_manager_1.naslBooleanTy;
791
- };
792
- // 兼容旧规则: 如果组件的P类型变元没有约束,那么它就是...
793
- env.unifier.unify(x.renamed, getPropInitialValue('pageable'));
935
+ return;
936
+ }
937
+ // booleanFlag / converterLiteral 等:优先根据属性值推导类型
938
+ if (meta.defaultFromProp) {
939
+ const propMissing = !nd.bindAttrs?.some((a) => a.name === meta.defaultFromProp.propName);
940
+ const shouldApplyFromProp = env.unifier.hasNoConcreteBounds(wrapper.renamed) ||
941
+ // converter 未传时,使用组件缺省值 'auto',避免后续被条件类型误收窄到其他分支
942
+ (meta.defaultFromProp.mode === 'converterLiteral' && meta.defaultFromProp.propName === 'converter' && propMissing);
943
+ if (shouldApplyFromProp) {
944
+ const ty = inferTypeFromPropMeta(meta.defaultFromProp, nd);
945
+ if (ty) {
946
+ env.unifier.unify(wrapper.renamed, ty);
947
+ }
794
948
  }
795
949
  }
950
+ // 非 dataItem 的参数:若仍无具体类型约束,使用默认类型
951
+ if (meta.defaultWhenUnresolved && env.unifier.hasNoConcreteBounds(wrapper.renamed)) {
952
+ env.unifier.unify(wrapper.renamed, meta.defaultWhenUnresolved);
953
+ }
796
954
  });
797
955
  env.addPostCheckTask(nd, (_, pSubst) => {
798
- nd.__ViewElementTV = typeParamWrappers.map((w) => ({
799
- rawName: w.rawName,
800
- renamed: pSubst(w.renamed),
801
- }));
956
+ const meta = (rawName) => componentDef?.getTypeParamMeta(rawName);
957
+ nd.__ViewElementTV = typeParamWrappers.map((w) => {
958
+ const m = meta(w.rawName);
959
+ return {
960
+ rawName: w.rawName,
961
+ renamed: pSubst(w.renamed),
962
+ role: m?.role,
963
+ hasOwnDataSource: m?.hasOwnDataSource,
964
+ };
965
+ });
802
966
  });
803
967
  // 在 postCheck 中设置事件类型,使用求解完成后的类型信息
804
968
  if (componentDef) {
805
969
  env.addPostCheckTask(nd, (env, pSubst) => {
806
970
  const initProp = componentDef.getInitPropsTypeAnnotation();
807
971
  if (initProp?.properties) {
808
- initProp.properties.forEach(prop => {
972
+ initProp.properties.forEach((prop) => {
809
973
  if (prop.name.startsWith('on') && prop.typeAnnotation?.typeKind === 'function') {
810
974
  // eventName 形如 "onBlurValid"
811
975
  const eventName = prop.name;
@@ -926,7 +1090,16 @@ function* tpViewElementFirstPass(env, nd) {
926
1090
  }
927
1091
  }
928
1092
  else if (tgtTy.typeKind !== 'function') {
929
- env.unifier.unify(type_manager_1.naslStringTy, tgtTy);
1093
+ // 类型参数带 extends(如 DT extends 'date' | 'datetime')时,应用字面量约束而非 String,
1094
+ // 否则 unify(naslStringTy, DT) 会把 DT 的界弄成宽 String,丢失字面量判别。
1095
+ let subTy = type_manager_1.naslStringTy;
1096
+ if ((0, type_predicate_1.isTyAnnTyParam)(tgtTy) && tgtTy.constraints) {
1097
+ const litTy = (0, bind_expression_hack_1.inferStaticValueTypeAsLiteral)(prop.value);
1098
+ subTy = (0, type_predicate_1.isAnyTy)(litTy)
1099
+ ? nasl_concepts_1.TypeAnnotation.createLiteral(String(prop.value ?? ''))
1100
+ : litTy;
1101
+ }
1102
+ env.unifier.unify(subTy, tgtTy);
930
1103
  }
931
1104
  }
932
1105
  function collectTypeConstraints(prop) {
@@ -1024,20 +1197,48 @@ function* tpViewElementFirstPass(env, nd) {
1024
1197
  }
1025
1198
  const args = (0, nasl_type_manipulation_1.extractArgPairsFromFnTy)(propTy);
1026
1199
  l.virtualParams = [];
1200
+ // 兜底替换:将所有组件类型参数映射为 any,用于 solver 无法完整求解时的回退。
1201
+ // 例如 u-transfer 的 onChange 事件参数类型为 { source: List<T>, transferValues: List<V> },
1202
+ // 若 V 无约束且从未进入 cs,solver 会将 ty 整体坍缩为 any。
1203
+ // 此时利用 solver deref 已原地修改的 arg.type(T 已替换)+ fallbackSubst(V→any)
1204
+ // 可以恢复出 { source: List<{...}>, transferValues: List<any> } 这个更精确的类型。
1205
+ const typeParamFallbackSubst = new Map();
1206
+ typeParamWrappers.forEach((w) => {
1207
+ if (w.renamed?.typeName) {
1208
+ typeParamFallbackSubst.set(w.renamed.typeName, type_manager_1.naslAnyTy);
1209
+ }
1210
+ });
1027
1211
  args?.forEach((arg) => {
1028
1212
  // pointToSymbol机制要求类型必须是类型变元才会被替换
1029
1213
  const ty = env.unifier.nextFreshTyAnn('bound');
1030
1214
  env.unifier.unify(ty, arg.type);
1215
+ // 直接使用 arg.type(如匿名结构 { value: V })作为虚参的类型标注,
1216
+ // 而非使用 fresh type var ty。这样事件体内对 event.value 的成员访问
1217
+ // 能直接在结构上找到 V,从而让 event.value = true 把 Boolean 约束回 V。
1218
+ const paramTyAnn = arg.type ?? ty;
1031
1219
  const newParam = nasl_concepts_1.Param.createProtoTypeOnly({
1032
1220
  name: arg.name,
1033
- typeAnnotation: ty
1221
+ typeAnnotation: paramTyAnn
1034
1222
  });
1035
1223
  // 必须设置好,不然 IDE 展示有问题
1036
1224
  newParam.parentNode = l;
1037
1225
  newParam.parentKey = 'virtualParams';
1038
1226
  l.virtualParams.push(newParam);
1039
- env.queryManager.asyncQuery(l).typeLevelEvaluationOf(ty).then(x => {
1040
- if ((0, type_predicate_1.isUnResolvedTy)(x)) {
1227
+ env.queryManager.asyncQuery(l).typeLevelEvaluationOf(ty).then((x) => {
1228
+ if ((0, type_predicate_1.isUnResolvedTy)(x) || (0, type_predicate_1.isAnyTy)(x)) {
1229
+ // ty 被 solver 坍缩为 any(常见原因:UB 中的匿名结构含有未进入 cs 的类型参数)。
1230
+ // arg.type 可能已被 solver 的 deref 原地修改(已求解的类型参数已替换)。
1231
+ // 通过 substTyAnn + typeParamFallbackSubst 将剩余类型参数替换为 any,
1232
+ // 保留已求解部分,得到更精确的兜底类型。
1233
+ const inPlaceType = newParam.typeAnnotation;
1234
+ if (inPlaceType && !(0, type_predicate_1.isAnyTy)(inPlaceType) && inPlaceType !== ty) {
1235
+ const resolved = (0, subster_1.substTyAnn)(env, typeParamFallbackSubst)(inPlaceType);
1236
+ if (!(0, type_predicate_1.isAnyTy)(resolved) && !(0, type_predicate_1.isUnResolvedTy)(resolved)) {
1237
+ env.setType(newParam, resolved);
1238
+ newParam.typeAnnotation = resolved;
1239
+ return;
1240
+ }
1241
+ }
1041
1242
  env.setType(newParam, undefined);
1042
1243
  newParam.typeAnnotation = undefined;
1043
1244
  }
@@ -1197,7 +1398,7 @@ const skipCheckDataBase = {
1197
1398
  };
1198
1399
  function isAttrIgnored(options) {
1199
1400
  if (options.attrName === 'value') {
1200
- if (options.tag === 'el-option') {
1401
+ if (['u-radios', 'van-option'].includes(options.tag)) {
1201
1402
  // HACK: 仅允许它贡献到父组件上
1202
1403
  return false;
1203
1404
  }