@lcap/nasl-language-server-core 4.4.0-beta.33 → 4.4.0-beta.35
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/out/checker.d.ts.map +1 -1
- package/out/checker.js +27 -13
- package/out/checker.js.map +1 -1
- package/out/typer/collectGlobalDefs.d.ts.map +1 -1
- package/out/typer/collectGlobalDefs.js +55 -7
- package/out/typer/collectGlobalDefs.js.map +1 -1
- package/out/typer/component-def-manager/component-def-manager.d.ts +65 -7
- package/out/typer/component-def-manager/component-def-manager.d.ts.map +1 -1
- package/out/typer/component-def-manager/component-def-manager.js +400 -8
- package/out/typer/component-def-manager/component-def-manager.js.map +1 -1
- package/out/typer/dispatch-view.d.ts.map +1 -1
- package/out/typer/dispatch-view.js +326 -142
- package/out/typer/dispatch-view.js.map +1 -1
- package/out/typer/subster.d.ts.map +1 -1
- package/out/typer/subster.js +80 -29
- package/out/typer/subster.js.map +1 -1
- package/out/typer/type-predicate.d.ts +14 -1
- package/out/typer/type-predicate.d.ts.map +1 -1
- package/out/typer/type-predicate.js +303 -3
- package/out/typer/type-predicate.js.map +1 -1
- package/out/typer/typer.d.ts +1 -0
- package/out/typer/typer.d.ts.map +1 -1
- package/out/typer/typer.js.map +1 -1
- package/out/typer/unifier.d.ts +8 -0
- package/out/typer/unifier.d.ts.map +1 -1
- package/out/typer/unifier.js +32 -0
- package/out/typer/unifier.js.map +1 -1
- 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");
|
|
@@ -189,8 +190,21 @@ function* tpBusinessComponent(env, nd) {
|
|
|
189
190
|
});
|
|
190
191
|
nd.elements.forEach((e) => {
|
|
191
192
|
(0, traverse_util_1.traverseViewElements)(e, (ve) => {
|
|
192
|
-
const
|
|
193
|
-
|
|
193
|
+
const compResult = env.refMgr?.compDefMgr?.queryByViewElement(ve);
|
|
194
|
+
if (compResult) {
|
|
195
|
+
const componentDef = compResult.instantiate(env);
|
|
196
|
+
const instanceProps = componentDef.getInstanceProps();
|
|
197
|
+
const veTy = nasl_concepts_1.TypeAnnotation.createTypeAnonymousStructure(instanceProps.map((x) => {
|
|
198
|
+
x.id = 'placeholder';
|
|
199
|
+
return nasl_concepts_1.StructureProperty.createProtoTypeOnly(x);
|
|
200
|
+
}), { id: 'placeholder' });
|
|
201
|
+
env.setType(ve, veTy);
|
|
202
|
+
ve.__preInstantiatedHelper = componentDef;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
const ty = env.unifier.nextFreshTyAnn('free');
|
|
206
|
+
env.setType(ve, ty);
|
|
207
|
+
}
|
|
194
208
|
const qName = (0, def_key_helpers_1.getGlobalDefKey)(ve);
|
|
195
209
|
(0, assertion_1.assertTrusy)(qName);
|
|
196
210
|
env.allocatedVEQNames.push(qName);
|
|
@@ -329,8 +343,26 @@ function* tpView(env, nd, skipSubView = false) {
|
|
|
329
343
|
});
|
|
330
344
|
nd.elements.forEach((e) => {
|
|
331
345
|
(0, traverse_util_1.traverseViewElements)(e, (ve) => {
|
|
332
|
-
|
|
333
|
-
|
|
346
|
+
// 在 logics 之前提前实例化组件定义,将 VE 的类型设为带有已重命名类型参数的
|
|
347
|
+
// 实际结构类型(如 { data: List<T'>, page: Integer, ... })。
|
|
348
|
+
// 这样 logics 中 elements.xxx.property.data 能获得完整的结构信息,
|
|
349
|
+
// 并通过 T'(fresh type var)在约束系统中与 ListTransform 等函数的类型参数关联。
|
|
350
|
+
// tpViewElementFirstPass 会复用此 helper,处理 bindAttrs 以解出 T' 的具体类型。
|
|
351
|
+
const compResult = env.refMgr?.compDefMgr?.queryByViewElement(ve);
|
|
352
|
+
if (compResult) {
|
|
353
|
+
const componentDef = compResult.instantiate(env);
|
|
354
|
+
const instanceProps = componentDef.getInstanceProps();
|
|
355
|
+
const veTy = nasl_concepts_1.TypeAnnotation.createTypeAnonymousStructure(instanceProps.map((x) => {
|
|
356
|
+
x.id = 'placeholder';
|
|
357
|
+
return nasl_concepts_1.StructureProperty.createProtoTypeOnly(x);
|
|
358
|
+
}), { id: 'placeholder' });
|
|
359
|
+
env.setType(ve, veTy);
|
|
360
|
+
ve.__preInstantiatedHelper = componentDef;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
const ty = env.unifier.nextFreshTyAnn('free');
|
|
364
|
+
env.setType(ve, ty);
|
|
365
|
+
}
|
|
334
366
|
const qName = (0, def_key_helpers_1.getGlobalDefKey)(ve);
|
|
335
367
|
(0, assertion_1.assertTrusy)(qName);
|
|
336
368
|
env.allocatedVEQNames.push(qName);
|
|
@@ -401,12 +433,21 @@ const excludedViewElementPropNames = new Set([
|
|
|
401
433
|
'formDesignerBuildInDefaults',
|
|
402
434
|
'formDesignerCalcDefaultsTargetField',
|
|
403
435
|
]);
|
|
404
|
-
|
|
436
|
+
/**
|
|
437
|
+
* 在祖先组件链中,找到拥有自己数据源的 dataItem 类型参数(基于 TypeParamMeta.role,而非名字)。
|
|
438
|
+
*/
|
|
439
|
+
function findAncestorDataItemTypeParam(env, nd) {
|
|
405
440
|
let cur = nd.parentNode;
|
|
406
441
|
while (cur) {
|
|
407
|
-
const
|
|
408
|
-
if (
|
|
409
|
-
|
|
442
|
+
const wrappers = env.getDataSourceTyParam(cur);
|
|
443
|
+
if (wrappers) {
|
|
444
|
+
const match = wrappers.find((w) => {
|
|
445
|
+
const ancestorComponentDef = env.refMgr?.compDefMgr.queryByViewElement(cur)?.instantiate(env);
|
|
446
|
+
const meta = ancestorComponentDef?.getTypeParamMeta(w.rawName);
|
|
447
|
+
return meta?.role === 'dataItem' && meta.hasOwnDataSource;
|
|
448
|
+
});
|
|
449
|
+
if (match)
|
|
450
|
+
return match;
|
|
410
451
|
}
|
|
411
452
|
if ((0, asserts_1.isViewElement)(cur.parentNode)) {
|
|
412
453
|
cur = cur.parentNode;
|
|
@@ -442,7 +483,8 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
442
483
|
if ((0, file_node_cache_1.checkNodeIsFileModuleInIdeWithCache)(nd)) {
|
|
443
484
|
env.fileNodes.push(nd);
|
|
444
485
|
}
|
|
445
|
-
const componentDef =
|
|
486
|
+
const componentDef = nd.__preInstantiatedHelper
|
|
487
|
+
?? env.refMgr.compDefMgr.queryByViewElement(nd)?.instantiate(env);
|
|
446
488
|
yield;
|
|
447
489
|
if (!componentDef) {
|
|
448
490
|
env.addDiagnosticsFor(`${nd.tag} 组件的定义缺失`, nd, 'warning');
|
|
@@ -451,9 +493,55 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
451
493
|
env.addError(`[DEBUG] ${nd.tag} 组件的定义缺失`);
|
|
452
494
|
}
|
|
453
495
|
}
|
|
454
|
-
|
|
496
|
+
const typeParamWrappers = componentDef?.getRenamedTypeParamWrappers() ?? [];
|
|
497
|
+
env.setDataSourceTyParam(nd, typeParamWrappers);
|
|
498
|
+
// 从父组件的 slot 返回类型统一泛型参数(按位置,不按名字)
|
|
499
|
+
// componentType 可能是 union,resolveComponentTypeForTag 会找到匹配的分支
|
|
500
|
+
const resolvedParentType = env.componentType?.typeKind === 'union'
|
|
501
|
+
? resolveComponentTypeForTag(env.componentType, nd.tag)
|
|
502
|
+
: (env.componentType?.typeName === component_def_manager_1.ComponentTagUtils.normalizeTag(nd.tag)
|
|
503
|
+
? env.componentType
|
|
504
|
+
: undefined);
|
|
505
|
+
if (resolvedParentType) {
|
|
506
|
+
typeParamWrappers.forEach((typeParam, index) => {
|
|
507
|
+
const pa = resolvedParentType.typeArguments?.[index];
|
|
508
|
+
if (typeParam?.renamed && pa) {
|
|
509
|
+
env.unifier.unify(typeParam.renamed, pa);
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
// 对于没有自己数据源但角色是 dataItem 的类型参数,从祖先组件继承
|
|
514
|
+
if (componentDef) {
|
|
515
|
+
typeParamWrappers.forEach((w) => {
|
|
516
|
+
const meta = componentDef.getTypeParamMeta(w.rawName);
|
|
517
|
+
if (meta?.role === 'dataItem' && !meta.hasOwnDataSource) {
|
|
518
|
+
const ancestorParam = findAncestorDataItemTypeParam(env, nd);
|
|
519
|
+
if (ancestorParam) {
|
|
520
|
+
env.unifier.unify(w.renamed, ancestorParam.renamed);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
}
|
|
455
525
|
const initProp = componentDef?.getInitPropsTypeAnnotation();
|
|
456
526
|
const instanceProps = componentDef?.getInstanceProps();
|
|
527
|
+
// 判断某个类型变元是否在初始化属性里以
|
|
528
|
+
// typeParam | String | Boolean | Integer
|
|
529
|
+
// 这类 union 形式出现,用于在完全无约束的情况下兜底为 String。
|
|
530
|
+
const isStringishUnionParam = (wrapper) => {
|
|
531
|
+
const props = initProp?.properties ?? [];
|
|
532
|
+
return props.some((p) => {
|
|
533
|
+
const ty = p.typeAnnotation;
|
|
534
|
+
if (!ty || ty.typeKind !== 'union' || !ty.typeArguments?.length)
|
|
535
|
+
return false;
|
|
536
|
+
// 通过类型形状而不是引用相等来识别同一个类型变元
|
|
537
|
+
const hasThisParam = ty.typeArguments.some((arg) => arg?.typeKind === 'typeParam' && arg.typeName === wrapper.renamed.typeName);
|
|
538
|
+
if (!hasThisParam)
|
|
539
|
+
return false;
|
|
540
|
+
const hasStringLike = ty.typeArguments.some((arg) => arg?.typeNamespace === 'nasl.core' &&
|
|
541
|
+
(arg.typeName === 'String' || arg.typeName === 'Boolean' || arg.typeName === 'Integer'));
|
|
542
|
+
return hasThisParam && hasStringLike;
|
|
543
|
+
});
|
|
544
|
+
};
|
|
457
545
|
/**
|
|
458
546
|
* 线性复杂度根据name寻找给定的property
|
|
459
547
|
* @param name
|
|
@@ -477,49 +565,56 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
477
565
|
}
|
|
478
566
|
return prop?.typeAnnotation ?? type_manager_1.naslAnyTy;
|
|
479
567
|
};
|
|
480
|
-
|
|
568
|
+
// 从 slot 返回类型中提取子组件类型(可能是 Array/List 包裹的 union)
|
|
569
|
+
function extractComponentTypeFromSlotReturn(slotProp) {
|
|
570
|
+
let ty = slotProp?.typeAnnotation?.returnType?.[0];
|
|
571
|
+
if (ty?.typeName === 'List' || ty?.typeName === 'Array') {
|
|
572
|
+
ty = ty?.typeArguments?.[0];
|
|
573
|
+
}
|
|
574
|
+
return ty;
|
|
575
|
+
}
|
|
576
|
+
// 从一个可能是 union 的类型中,找到与 childTag 匹配的那个分支
|
|
577
|
+
function resolveComponentTypeForTag(ty, childTag) {
|
|
578
|
+
if (!ty)
|
|
579
|
+
return undefined;
|
|
580
|
+
const normalizedChildTag = component_def_manager_1.ComponentTagUtils.normalizeTag(childTag);
|
|
581
|
+
// 直接匹配
|
|
582
|
+
if (ty.typeName === normalizedChildTag)
|
|
583
|
+
return ty;
|
|
584
|
+
// union: 遍历 typeArguments 找匹配的分支
|
|
585
|
+
if (ty.typeKind === 'union' && ty.typeArguments?.length) {
|
|
586
|
+
return ty.typeArguments.find((alt) => alt && component_def_manager_1.ComponentTagUtils.normalizeTag(alt.typeName ?? '') === normalizedChildTag);
|
|
587
|
+
}
|
|
588
|
+
return undefined;
|
|
589
|
+
}
|
|
481
590
|
function makeCurrentManager(nd) {
|
|
482
|
-
// currentManager机制分为两部分:
|
|
483
|
-
// 1. 维护类型变元的约束
|
|
484
|
-
// 2. current变量的创建和销毁
|
|
485
591
|
if (!componentDef) {
|
|
486
592
|
return;
|
|
487
593
|
}
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
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
|
-
}
|
|
594
|
+
// 预先解析 default slot 的返回类型,供非 template 直接子组件使用
|
|
595
|
+
const defaultSlotProp = componentDef.getSlotPropBySlotTarget('default');
|
|
596
|
+
const defaultSlotComponentType = extractComponentTypeFromSlotReturn(defaultSlotProp);
|
|
506
597
|
return {
|
|
507
598
|
create: (options) => {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
599
|
+
const child = options.templateElement;
|
|
600
|
+
if (child?.tag === 'template') {
|
|
601
|
+
// template slot: 创建 current 变量 + 提供 slot 级别的 componentType
|
|
602
|
+
const slotTarget = child.slotTarget ?? 'default';
|
|
603
|
+
const slotProp = componentDef?.getSlotPropBySlotTarget(slotTarget);
|
|
604
|
+
const slotScopeType = slotProp ? componentDef?.getSlotScopeType(slotProp) : null;
|
|
605
|
+
const slotComponentType = extractComponentTypeFromSlotReturn(slotProp);
|
|
606
|
+
let curTy = slotScopeType?.[0]?.type;
|
|
607
|
+
let curName;
|
|
608
|
+
let curVar;
|
|
609
|
+
if (curTy) {
|
|
610
|
+
curName = env.unifier.nextFreshCurrent();
|
|
611
|
+
curVar = (0, collectGlobalDefs_1.createVarAndSetParentNode)(curName, curTy, nd);
|
|
612
|
+
env.setType(curVar, curTy);
|
|
613
|
+
env.addLexicalSym(curVar);
|
|
614
|
+
}
|
|
521
615
|
return {
|
|
522
616
|
createdVar: curVar,
|
|
617
|
+
componentType: slotComponentType,
|
|
523
618
|
revoke: () => {
|
|
524
619
|
if (curName) {
|
|
525
620
|
env.unifier.decreaseCurrentCounter();
|
|
@@ -528,6 +623,17 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
528
623
|
},
|
|
529
624
|
};
|
|
530
625
|
}
|
|
626
|
+
// 非 template 直接子组件:使用 default slot 的返回类型
|
|
627
|
+
// 从 union 中按 tag 找到对应的分支,从而实现按位置统一泛型
|
|
628
|
+
const matched = resolveComponentTypeForTag(defaultSlotComponentType, child?.tag ?? '');
|
|
629
|
+
if (matched) {
|
|
630
|
+
return {
|
|
631
|
+
createdVar: undefined,
|
|
632
|
+
componentType: matched,
|
|
633
|
+
revoke: () => { },
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
return undefined;
|
|
531
637
|
},
|
|
532
638
|
};
|
|
533
639
|
}
|
|
@@ -559,8 +665,34 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
559
665
|
}
|
|
560
666
|
});
|
|
561
667
|
}
|
|
562
|
-
|
|
563
|
-
|
|
668
|
+
// 如果 VE 已经在 tpView 中被预实例化(类型已经是 anonymousStructure),
|
|
669
|
+
// 跳过 unify 以避免将同一结构的属性类型与自身统一导致自引用约束。
|
|
670
|
+
// 否则(VE 类型是 freshTyVar),通过 unify 连接 freshTyVar 与实际类型。
|
|
671
|
+
const existingTy = env.getRawType(nd);
|
|
672
|
+
if (!existingTy || existingTy.typeKind !== 'anonymousStructure') {
|
|
673
|
+
env.unifier.unify(existingTy, veTy);
|
|
674
|
+
}
|
|
675
|
+
env.setType(nd, veTy);
|
|
676
|
+
// 在完全求解后,对仍未解出的类型变元做默认兜底:
|
|
677
|
+
// 1. dataItem 角色(无论是否有自己的数据源):当数据源未设置且无其他约束能推出具体类型时,兜底为 String
|
|
678
|
+
// 2. 形状类似 T | String | Boolean | Integer 的 union 参数:兜底为 String
|
|
679
|
+
if (typeParamWrappers.length) {
|
|
680
|
+
env.addPostCheckTask(nd, (env, pSubst) => {
|
|
681
|
+
typeParamWrappers.forEach((w) => {
|
|
682
|
+
const solved = pSubst(w.renamed);
|
|
683
|
+
if (!(0, type_predicate_1.isUnResolvedTy)(solved))
|
|
684
|
+
return;
|
|
685
|
+
const meta = componentDef?.getTypeParamMeta(w.rawName);
|
|
686
|
+
if (meta?.role === 'dataItem') {
|
|
687
|
+
env.unifier.unify(w.renamed, meta.defaultWhenUnresolved ?? type_manager_1.naslStringTy);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if (isStringishUnionParam(w)) {
|
|
691
|
+
env.unifier.unify(w.renamed, type_manager_1.naslStringTy);
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
}
|
|
564
696
|
const ceTy = nasl_concepts_1.TypeAnnotation.createProtoTypeOnly({
|
|
565
697
|
typeName: 'IElements',
|
|
566
698
|
typeKind: 'anonymousStructure',
|
|
@@ -577,7 +709,7 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
577
709
|
const elements = (0, collectGlobalDefs_1.createVarAndSetParentNode)('elements', ceTy, nd);
|
|
578
710
|
// env.setType(elements, ceTy);
|
|
579
711
|
// 省点内存
|
|
580
|
-
env.
|
|
712
|
+
env.setType(elements, ceTy);
|
|
581
713
|
env.addLexicalSym(elements);
|
|
582
714
|
const dropList = [];
|
|
583
715
|
dropList.push(elements);
|
|
@@ -633,8 +765,11 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
633
765
|
dropList.push(popper.createdVar);
|
|
634
766
|
(0, helper_1.createOnPush)(env.allocatedVirtualNodes, env.getCurFileNode(), popper.createdVar);
|
|
635
767
|
}
|
|
768
|
+
// 存在组件类型
|
|
769
|
+
env.componentType = popper?.componentType;
|
|
636
770
|
yield* tpViewElementFirstPass(env, e);
|
|
637
771
|
popper?.revoke();
|
|
772
|
+
env.componentType = undefined;
|
|
638
773
|
});
|
|
639
774
|
/**
|
|
640
775
|
* 特性:部分元素的 V 类型可以由子元素的 V 推导得到。同时又尊重此元素本身引入的 V 约束
|
|
@@ -675,22 +810,32 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
675
810
|
* 当前ViewElement的children中第一个非template的 BindAttribute[name='value'](若无,则为其他类型为V的字段)的类型
|
|
676
811
|
*/
|
|
677
812
|
/**
|
|
678
|
-
*
|
|
679
|
-
*
|
|
813
|
+
* 如果当前组件有 valueResult 类型变元,且没有自己的额外数据源(role = 'dataItem' 且 hasOwnDataSource 的第二个),
|
|
814
|
+
* 则子组件同角色的 valueResult 应当贡献到当前组件的 valueResult 上。
|
|
815
|
+
* 这里通过 role 匹配,而不是看名字是否是 'V' / 'T1'。
|
|
680
816
|
*/
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
817
|
+
const valueResultWrapper = typeParamWrappers.find((w) => {
|
|
818
|
+
const m = componentDef?.getTypeParamMeta(w.rawName);
|
|
819
|
+
return m?.role === 'valueResult';
|
|
820
|
+
});
|
|
821
|
+
const hasMultipleDataItems = typeParamWrappers.filter((w) => {
|
|
822
|
+
const m = componentDef?.getTypeParamMeta(w.rawName);
|
|
823
|
+
return m?.role === 'dataItem' && m.hasOwnDataSource;
|
|
824
|
+
}).length > 1;
|
|
825
|
+
const shouldValueBeInferred = !hasMultipleDataItems &&
|
|
826
|
+
!!valueResultWrapper &&
|
|
827
|
+
env.unifier.containsEmptyBounds(valueResultWrapper.renamed);
|
|
828
|
+
if (shouldValueBeInferred) {
|
|
687
829
|
yield* (0, nasl_utils_1.wrapForEachToGenerator)(nd.children, function* (e) {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
830
|
+
if (e.tag.startsWith(nd.tag)) {
|
|
831
|
+
const childWrappers = env.getDataSourceTyParam(e);
|
|
832
|
+
const vChild = childWrappers?.find((cw) => {
|
|
833
|
+
const childDef = env.refMgr?.compDefMgr.queryByViewElement(e)?.instantiate(env);
|
|
834
|
+
const cm = childDef?.getTypeParamMeta(cw.rawName);
|
|
835
|
+
return cm?.role === 'valueResult';
|
|
836
|
+
})?.renamed;
|
|
692
837
|
if (vChild) {
|
|
693
|
-
env.unifier.unify(vChild,
|
|
838
|
+
env.unifier.unify(vChild, valueResultWrapper.renamed);
|
|
694
839
|
}
|
|
695
840
|
}
|
|
696
841
|
});
|
|
@@ -703,8 +848,10 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
703
848
|
dropList.forEach((x) => {
|
|
704
849
|
env.removeLexicalVarByName(x.name);
|
|
705
850
|
});
|
|
706
|
-
|
|
707
|
-
|
|
851
|
+
/**
|
|
852
|
+
* 通用的类型变元 fallback 过程:
|
|
853
|
+
* 完全基于 TypeParamMeta.role 驱动,不依赖任何 rawName 硬编码。
|
|
854
|
+
*/
|
|
708
855
|
const isBindAttrEmptyExpression = (name) => {
|
|
709
856
|
const attr = nd.bindAttrs?.find((attr) => attr.name === name);
|
|
710
857
|
if (!attr) {
|
|
@@ -714,98 +861,98 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
714
861
|
return attr.type === 'dynamic' && !attr.expression;
|
|
715
862
|
}
|
|
716
863
|
};
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
if (
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
// ColumnDynamic 场景下,T 类型变元从 table 中推导,不需要 fallback 规则
|
|
728
|
-
if (isColumnDynamic) {
|
|
729
|
-
return;
|
|
864
|
+
const inferTypeFromPropMeta = (cfg, nd) => {
|
|
865
|
+
const attr = nd.bindAttrs?.find((a) => a.name === cfg.propName);
|
|
866
|
+
if (!attr) {
|
|
867
|
+
if (cfg.mode === 'booleanFlag')
|
|
868
|
+
return nasl_concepts_1.TypeAnnotation.createLiteral(false);
|
|
869
|
+
return undefined;
|
|
870
|
+
}
|
|
871
|
+
if (cfg.mode === 'booleanFlag') {
|
|
872
|
+
if (['static', 'string'].includes(attr.type ?? '')) {
|
|
873
|
+
return nasl_concepts_1.TypeAnnotation.createLiteral(!!attr.value);
|
|
730
874
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
875
|
+
return type_manager_1.naslBooleanTy;
|
|
876
|
+
}
|
|
877
|
+
if (cfg.mode === 'converterLiteral') {
|
|
878
|
+
if (['static', 'string'].includes(attr.type ?? '')) {
|
|
879
|
+
if ([true, 'true'].includes(attr.value)) {
|
|
880
|
+
return nasl_concepts_1.TypeAnnotation.createLiteral(true);
|
|
881
|
+
}
|
|
882
|
+
if ([false, 'false'].includes(attr.value)) {
|
|
883
|
+
return nasl_concepts_1.TypeAnnotation.createLiteral(false);
|
|
884
|
+
}
|
|
885
|
+
return nasl_concepts_1.TypeAnnotation.createLiteral(String(attr.value));
|
|
735
886
|
}
|
|
887
|
+
return nasl_concepts_1.TypeAnnotation.createLiteral('');
|
|
736
888
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
if (isColumnDynamic && isBindAttrEmptyExpression('dataSource')) {
|
|
741
|
-
env.unifier.unify(x.renamed, type_manager_1.naslStringTy);
|
|
889
|
+
if (cfg.mode === 'stringLiteral') {
|
|
890
|
+
if (['static', 'string'].includes(attr.type ?? '')) {
|
|
891
|
+
return nasl_concepts_1.TypeAnnotation.createLiteral(String(attr.value ?? ''));
|
|
742
892
|
}
|
|
893
|
+
return type_manager_1.naslStringTy;
|
|
743
894
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
895
|
+
return undefined;
|
|
896
|
+
};
|
|
897
|
+
typeParamWrappers.forEach((wrapper) => {
|
|
898
|
+
const meta = componentDef?.getTypeParamMeta(wrapper.rawName);
|
|
899
|
+
if (!meta) {
|
|
900
|
+
// 没有 meta 的类型参数(如 u-transfer 的 V,仅出现在事件处理器参数结构体内)
|
|
901
|
+
// 也需要注册到约束系统中,否则 solver 无法将其兜底为 any,
|
|
902
|
+
// 导致包含该类型参数的函数类型在 postCheck 中被判为 unresolved 而误报。
|
|
903
|
+
if (wrapper.renamed?.typeName && env.unifier.containsEmptyBounds(wrapper.renamed)) {
|
|
904
|
+
env.unifier.unify(wrapper.renamed, type_manager_1.naslAnyTy);
|
|
748
905
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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'));
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
// dataItem 且有自己的数据源:仅在数据源为空且无具体类型约束时回落到默认类型。
|
|
909
|
+
// 预实例化后逻辑可能已为 T 建立了纯类型参数约束(如 T ≈ T_lt),
|
|
910
|
+
// 这些不携带具体信息,不应阻止兜底;但 model 等属性提供的具体类型约束应阻止兜底。
|
|
911
|
+
if (meta.role === 'dataItem' && meta.hasOwnDataSource && meta.dataSourcePropName) {
|
|
912
|
+
if (isBindAttrEmptyExpression(meta.dataSourcePropName) && meta.defaultWhenUnresolved
|
|
913
|
+
&& env.unifier.hasNoConcreteBounds(wrapper.renamed)) {
|
|
914
|
+
env.unifier.unify(wrapper.renamed, meta.defaultWhenUnresolved);
|
|
770
915
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
return type_manager_1.naslBooleanTy;
|
|
779
|
-
};
|
|
780
|
-
// 兼容旧规则: 如果组件的M类型变元没有约束,那么它就是...
|
|
781
|
-
env.unifier.unify(x.renamed, getPropInitialValue('multiple'));
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
// dataItem 但没有自己的数据源(从祖先继承):祖先的类型参数 bound 是有意义的约束,
|
|
919
|
+
// 必须用 containsEmptyBounds(而非 hasNoConcreteBounds)来防止覆盖祖先传播的类型。
|
|
920
|
+
if (meta.role === 'dataItem' && !meta.hasOwnDataSource) {
|
|
921
|
+
if (meta.defaultWhenUnresolved && env.unifier.containsEmptyBounds(wrapper.renamed)) {
|
|
922
|
+
env.unifier.unify(wrapper.renamed, meta.defaultWhenUnresolved);
|
|
782
923
|
}
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
return type_manager_1.naslBooleanTy;
|
|
791
|
-
};
|
|
792
|
-
// 兼容旧规则: 如果组件的P类型变元没有约束,那么它就是...
|
|
793
|
-
env.unifier.unify(x.renamed, getPropInitialValue('pageable'));
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
// booleanFlag / converterLiteral 等:优先根据属性值推导类型
|
|
927
|
+
if (meta.defaultFromProp && env.unifier.hasNoConcreteBounds(wrapper.renamed)) {
|
|
928
|
+
const ty = inferTypeFromPropMeta(meta.defaultFromProp, nd);
|
|
929
|
+
if (ty) {
|
|
930
|
+
env.unifier.unify(wrapper.renamed, ty);
|
|
794
931
|
}
|
|
795
932
|
}
|
|
933
|
+
// 非 dataItem 的参数:若仍无具体类型约束,使用默认类型
|
|
934
|
+
if (meta.defaultWhenUnresolved && env.unifier.hasNoConcreteBounds(wrapper.renamed)) {
|
|
935
|
+
env.unifier.unify(wrapper.renamed, meta.defaultWhenUnresolved);
|
|
936
|
+
}
|
|
796
937
|
});
|
|
797
938
|
env.addPostCheckTask(nd, (_, pSubst) => {
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
939
|
+
const meta = (rawName) => componentDef?.getTypeParamMeta(rawName);
|
|
940
|
+
nd.__ViewElementTV = typeParamWrappers.map((w) => {
|
|
941
|
+
const m = meta(w.rawName);
|
|
942
|
+
return {
|
|
943
|
+
rawName: w.rawName,
|
|
944
|
+
renamed: pSubst(w.renamed),
|
|
945
|
+
role: m?.role,
|
|
946
|
+
hasOwnDataSource: m?.hasOwnDataSource,
|
|
947
|
+
};
|
|
948
|
+
});
|
|
802
949
|
});
|
|
803
950
|
// 在 postCheck 中设置事件类型,使用求解完成后的类型信息
|
|
804
951
|
if (componentDef) {
|
|
805
952
|
env.addPostCheckTask(nd, (env, pSubst) => {
|
|
806
953
|
const initProp = componentDef.getInitPropsTypeAnnotation();
|
|
807
954
|
if (initProp?.properties) {
|
|
808
|
-
initProp.properties.forEach(prop => {
|
|
955
|
+
initProp.properties.forEach((prop) => {
|
|
809
956
|
if (prop.name.startsWith('on') && prop.typeAnnotation?.typeKind === 'function') {
|
|
810
957
|
// eventName 形如 "onBlurValid"
|
|
811
958
|
const eventName = prop.name;
|
|
@@ -926,7 +1073,16 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
926
1073
|
}
|
|
927
1074
|
}
|
|
928
1075
|
else if (tgtTy.typeKind !== 'function') {
|
|
929
|
-
|
|
1076
|
+
// 类型参数带 extends(如 DT extends 'date' | 'datetime')时,应用字面量约束而非 String,
|
|
1077
|
+
// 否则 unify(naslStringTy, DT) 会把 DT 的界弄成宽 String,丢失字面量判别。
|
|
1078
|
+
let subTy = type_manager_1.naslStringTy;
|
|
1079
|
+
if ((0, type_predicate_1.isTyAnnTyParam)(tgtTy) && tgtTy.constraints) {
|
|
1080
|
+
const litTy = (0, bind_expression_hack_1.inferStaticValueTypeAsLiteral)(prop.value);
|
|
1081
|
+
subTy = (0, type_predicate_1.isAnyTy)(litTy)
|
|
1082
|
+
? nasl_concepts_1.TypeAnnotation.createLiteral(String(prop.value ?? ''))
|
|
1083
|
+
: litTy;
|
|
1084
|
+
}
|
|
1085
|
+
env.unifier.unify(subTy, tgtTy);
|
|
930
1086
|
}
|
|
931
1087
|
}
|
|
932
1088
|
function collectTypeConstraints(prop) {
|
|
@@ -1024,20 +1180,48 @@ function* tpViewElementFirstPass(env, nd) {
|
|
|
1024
1180
|
}
|
|
1025
1181
|
const args = (0, nasl_type_manipulation_1.extractArgPairsFromFnTy)(propTy);
|
|
1026
1182
|
l.virtualParams = [];
|
|
1183
|
+
// 兜底替换:将所有组件类型参数映射为 any,用于 solver 无法完整求解时的回退。
|
|
1184
|
+
// 例如 u-transfer 的 onChange 事件参数类型为 { source: List<T>, transferValues: List<V> },
|
|
1185
|
+
// 若 V 无约束且从未进入 cs,solver 会将 ty 整体坍缩为 any。
|
|
1186
|
+
// 此时利用 solver deref 已原地修改的 arg.type(T 已替换)+ fallbackSubst(V→any)
|
|
1187
|
+
// 可以恢复出 { source: List<{...}>, transferValues: List<any> } 这个更精确的类型。
|
|
1188
|
+
const typeParamFallbackSubst = new Map();
|
|
1189
|
+
typeParamWrappers.forEach((w) => {
|
|
1190
|
+
if (w.renamed?.typeName) {
|
|
1191
|
+
typeParamFallbackSubst.set(w.renamed.typeName, type_manager_1.naslAnyTy);
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1027
1194
|
args?.forEach((arg) => {
|
|
1028
1195
|
// pointToSymbol机制要求类型必须是类型变元才会被替换
|
|
1029
1196
|
const ty = env.unifier.nextFreshTyAnn('bound');
|
|
1030
1197
|
env.unifier.unify(ty, arg.type);
|
|
1198
|
+
// 直接使用 arg.type(如匿名结构 { value: V })作为虚参的类型标注,
|
|
1199
|
+
// 而非使用 fresh type var ty。这样事件体内对 event.value 的成员访问
|
|
1200
|
+
// 能直接在结构上找到 V,从而让 event.value = true 把 Boolean 约束回 V。
|
|
1201
|
+
const paramTyAnn = arg.type ?? ty;
|
|
1031
1202
|
const newParam = nasl_concepts_1.Param.createProtoTypeOnly({
|
|
1032
1203
|
name: arg.name,
|
|
1033
|
-
typeAnnotation:
|
|
1204
|
+
typeAnnotation: paramTyAnn
|
|
1034
1205
|
});
|
|
1035
1206
|
// 必须设置好,不然 IDE 展示有问题
|
|
1036
1207
|
newParam.parentNode = l;
|
|
1037
1208
|
newParam.parentKey = 'virtualParams';
|
|
1038
1209
|
l.virtualParams.push(newParam);
|
|
1039
|
-
env.queryManager.asyncQuery(l).typeLevelEvaluationOf(ty).then(x => {
|
|
1040
|
-
if ((0, type_predicate_1.isUnResolvedTy)(x)) {
|
|
1210
|
+
env.queryManager.asyncQuery(l).typeLevelEvaluationOf(ty).then((x) => {
|
|
1211
|
+
if ((0, type_predicate_1.isUnResolvedTy)(x) || (0, type_predicate_1.isAnyTy)(x)) {
|
|
1212
|
+
// ty 被 solver 坍缩为 any(常见原因:UB 中的匿名结构含有未进入 cs 的类型参数)。
|
|
1213
|
+
// arg.type 可能已被 solver 的 deref 原地修改(已求解的类型参数已替换)。
|
|
1214
|
+
// 通过 substTyAnn + typeParamFallbackSubst 将剩余类型参数替换为 any,
|
|
1215
|
+
// 保留已求解部分,得到更精确的兜底类型。
|
|
1216
|
+
const inPlaceType = newParam.typeAnnotation;
|
|
1217
|
+
if (inPlaceType && !(0, type_predicate_1.isAnyTy)(inPlaceType) && inPlaceType !== ty) {
|
|
1218
|
+
const resolved = (0, subster_1.substTyAnn)(env, typeParamFallbackSubst)(inPlaceType);
|
|
1219
|
+
if (!(0, type_predicate_1.isAnyTy)(resolved) && !(0, type_predicate_1.isUnResolvedTy)(resolved)) {
|
|
1220
|
+
env.setType(newParam, resolved);
|
|
1221
|
+
newParam.typeAnnotation = resolved;
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1041
1225
|
env.setType(newParam, undefined);
|
|
1042
1226
|
newParam.typeAnnotation = undefined;
|
|
1043
1227
|
}
|
|
@@ -1197,7 +1381,7 @@ const skipCheckDataBase = {
|
|
|
1197
1381
|
};
|
|
1198
1382
|
function isAttrIgnored(options) {
|
|
1199
1383
|
if (options.attrName === 'value') {
|
|
1200
|
-
if (
|
|
1384
|
+
if (['u-radios', 'van-option'].includes(options.tag)) {
|
|
1201
1385
|
// HACK: 仅允许它贡献到父组件上
|
|
1202
1386
|
return false;
|
|
1203
1387
|
}
|