@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.
- package/out/checker.d.ts.map +1 -1
- package/out/checker.js +32 -16
- package/out/checker.js.map +1 -1
- package/out/typer/collectGlobalDefs.d.ts.map +1 -1
- package/out/typer/collectGlobalDefs.js +73 -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-call.d.ts.map +1 -1
- package/out/typer/dispatch-call.js +13 -1
- package/out/typer/dispatch-call.js.map +1 -1
- package/out/typer/dispatch-view.d.ts.map +1 -1
- package/out/typer/dispatch-view.js +342 -141
- package/out/typer/dispatch-view.js.map +1 -1
- package/out/typer/overload-helper.d.ts.map +1 -1
- package/out/typer/overload-helper.js +0 -3
- package/out/typer/overload-helper.js.map +1 -1
- package/out/typer/sem-diag.js +1 -1
- package/out/typer/sem-diag.js.map +1 -1
- package/out/typer/solver.d.ts.map +1 -1
- package/out/typer/solver.js +2 -2
- package/out/typer/solver.js.map +1 -1
- package/out/typer/subster.d.ts.map +1 -1
- package/out/typer/subster.js +54 -35
- package/out/typer/subster.js.map +1 -1
- package/out/typer/type-manager.d.ts.map +1 -1
- package/out/typer/type-manager.js +4 -0
- package/out/typer/type-manager.js.map +1 -1
- package/out/typer/type-predicate.d.ts +19 -1
- package/out/typer/type-predicate.d.ts.map +1 -1
- package/out/typer/type-predicate.js +325 -18
- package/out/typer/type-predicate.js.map +1 -1
- package/out/typer/typer.d.ts +9 -0
- package/out/typer/typer.d.ts.map +1 -1
- package/out/typer/typer.js +8 -0
- 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 +34 -2
- package/out/typer/unifier.js.map +1 -1
- package/out/utils/file-node-cache.d.ts.map +1 -1
- package/out/utils/file-node-cache.js +5 -4
- package/out/utils/file-node-cache.js.map +1 -1
- package/out/utils/parseTsClassType.d.ts.map +1 -1
- package/out/utils/parseTsClassType.js +6 -0
- package/out/utils/parseTsClassType.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");
|
|
@@ -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
|
|
193
|
-
|
|
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
|
-
|
|
333
|
-
|
|
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
|
-
|
|
443
|
+
/**
|
|
444
|
+
* 在祖先组件链中,找到拥有自己数据源的 dataItem 类型参数(基于 TypeParamMeta.role,而非名字)。
|
|
445
|
+
*/
|
|
446
|
+
function findAncestorDataItemTypeParam(env, nd) {
|
|
405
447
|
let cur = nd.parentNode;
|
|
406
448
|
while (cur) {
|
|
407
|
-
const
|
|
408
|
-
if (
|
|
409
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
601
|
+
// 预先解析 default slot 的返回类型,供非 template 直接子组件使用
|
|
602
|
+
const defaultSlotProp = componentDef.getSlotPropBySlotTarget('default');
|
|
603
|
+
const defaultSlotComponentType = extractComponentTypeFromSlotReturn(defaultSlotProp);
|
|
506
604
|
return {
|
|
507
605
|
create: (options) => {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
-
|
|
563
|
-
|
|
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.
|
|
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
|
-
*
|
|
679
|
-
*
|
|
820
|
+
* 如果当前组件有 valueResult 类型变元,且没有自己的额外数据源(role = 'dataItem' 且 hasOwnDataSource 的第二个),
|
|
821
|
+
* 则子组件同角色的 valueResult 应当贡献到当前组件的 valueResult 上。
|
|
822
|
+
* 这里通过 role 匹配,而不是看名字是否是 'V' / 'T1'。
|
|
680
823
|
*/
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
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
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
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,
|
|
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
|
-
|
|
707
|
-
|
|
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
|
-
|
|
718
|
-
|
|
719
|
-
if (
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
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
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
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
|
-
|
|
738
|
-
|
|
739
|
-
|
|
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
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
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
|
-
|
|
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'));
|
|
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
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
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
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
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
|
-
|
|
799
|
-
|
|
800
|
-
|
|
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
|
-
|
|
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:
|
|
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 (
|
|
1401
|
+
if (['u-radios', 'van-option'].includes(options.tag)) {
|
|
1201
1402
|
// HACK: 仅允许它贡献到父组件上
|
|
1202
1403
|
return false;
|
|
1203
1404
|
}
|