@lcap/nasl-language-server-core 4.3.0-rc.7 → 4.4.0-beta.10

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 (56) hide show
  1. package/out/checker.d.ts +3 -1
  2. package/out/checker.d.ts.map +1 -1
  3. package/out/checker.js +293 -3
  4. package/out/checker.js.map +1 -1
  5. package/out/reference-manager/reference-manager.d.ts +84 -14
  6. package/out/reference-manager/reference-manager.d.ts.map +1 -1
  7. package/out/reference-manager/reference-manager.js +217 -66
  8. package/out/reference-manager/reference-manager.js.map +1 -1
  9. package/out/reference-manager/update-nasl-fragment.d.ts.map +1 -1
  10. package/out/reference-manager/update-nasl-fragment.js +16 -10
  11. package/out/reference-manager/update-nasl-fragment.js.map +1 -1
  12. package/out/reference-manager/view-elem-logic.d.ts +3 -3
  13. package/out/reference-manager/view-elem-logic.d.ts.map +1 -1
  14. package/out/reference-manager/view-elem-logic.js +17 -0
  15. package/out/reference-manager/view-elem-logic.js.map +1 -1
  16. package/out/symbol/traverse/concepts/index.d.ts +1 -1
  17. package/out/symbol/traverse/concepts/index.d.ts.map +1 -1
  18. package/out/typer/collectGlobalDefs.d.ts.map +1 -1
  19. package/out/typer/collectGlobalDefs.js +25 -2
  20. package/out/typer/collectGlobalDefs.js.map +1 -1
  21. package/out/typer/dispatch-def.d.ts.map +1 -1
  22. package/out/typer/dispatch-def.js +4 -7
  23. package/out/typer/dispatch-def.js.map +1 -1
  24. package/out/typer/dispatch-expr.d.ts +2 -1
  25. package/out/typer/dispatch-expr.d.ts.map +1 -1
  26. package/out/typer/dispatch-expr.js +139 -97
  27. package/out/typer/dispatch-expr.js.map +1 -1
  28. package/out/typer/dispatch-stmt.d.ts.map +1 -1
  29. package/out/typer/dispatch-stmt.js +34 -23
  30. package/out/typer/dispatch-stmt.js.map +1 -1
  31. package/out/typer/dispatch-view.d.ts.map +1 -1
  32. package/out/typer/dispatch-view.js +67 -25
  33. package/out/typer/dispatch-view.js.map +1 -1
  34. package/out/typer/get-oql-files.d.ts.map +1 -1
  35. package/out/typer/get-oql-files.js +2 -8
  36. package/out/typer/get-oql-files.js.map +1 -1
  37. package/out/typer/incremental-update.d.ts +12 -5
  38. package/out/typer/incremental-update.d.ts.map +1 -1
  39. package/out/typer/incremental-update.js +187 -18
  40. package/out/typer/incremental-update.js.map +1 -1
  41. package/out/typer/subster.d.ts.map +1 -1
  42. package/out/typer/subster.js +103 -28
  43. package/out/typer/subster.js.map +1 -1
  44. package/out/typer/type-predicate.d.ts +2 -0
  45. package/out/typer/type-predicate.d.ts.map +1 -1
  46. package/out/typer/type-predicate.js +7 -2
  47. package/out/typer/type-predicate.js.map +1 -1
  48. package/out/typer/typer.d.ts +18 -1
  49. package/out/typer/typer.d.ts.map +1 -1
  50. package/out/typer/typer.js +89 -6
  51. package/out/typer/typer.js.map +1 -1
  52. package/out/typer/unifier.d.ts +10 -1
  53. package/out/typer/unifier.d.ts.map +1 -1
  54. package/out/typer/unifier.js +54 -4
  55. package/out/typer/unifier.js.map +1 -1
  56. package/package.json +5 -5
package/out/checker.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { SyntaxNode, App, BaseNode } from '@lcap/nasl-concepts';
1
+ import type { SyntaxNode, Frontend, View, App, BaseNode } from '@lcap/nasl-concepts';
2
2
  import { TypeAnnotation } from '@lcap/nasl-concepts';
3
3
  import { type SemEnv } from './typer/typer';
4
4
  import { Diagnostic as NaslTypeDiagnostic } from '@lcap/nasl-types';
@@ -49,6 +49,8 @@ export declare function createErrorDiagnoser(context: DiagnoseContext): {
49
49
  nodeConcept: string;
50
50
  }[];
51
51
  error: (node: SyntaxNode, message: string, context?: ErrorContext) => void;
52
+ checkViewIndexPageSetting: (node: View | Frontend) => void;
53
+ revalidateAncestorNode: (ancestorNode: SyntaxNode, validationFn: (ancestorNode: SyntaxNode, env: SemEnv) => void, errorFilter?: (err: Diagnostic) => boolean) => NodeDiagnosticsMap | null;
52
54
  };
53
55
  export interface DiagnosticRecord {
54
56
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EAU2G,GAAG,EAMxH,QAAQ,EAET,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACgE,cAAc,EAGpF,MAAM,qBAAqB,CAAC;AAS7B,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAK5C,OAAO,EAAE,UAAU,IAAI,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAMpE,oBAAY,QAAQ;IAClB,IAAI,YAAY;IAChB,KAAK,UAAU;CAChB;AAED,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAEnE,KAAK,YAAY,GAAG;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AAMF,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;AAMrE,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAqBF,KAAK,eAAe,GAAG;IACrB,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,yBAAyB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/C,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AA4OF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe;oCA2zIpB,OAAO;sDAPW,gBAAgB,EAAE;;;;sBAhBjD,UAAU,GAAG,SAAS;;;8BA4Df,UAAU;2BAuBb,UAAU;;;iBAW3B,MAAM;kBACL,MAAM;kBACN,MAAM;qBACH,MAAM;;kBA50IF,UAAU,WAAW,MAAM,YAAY,YAAY;EA+2IzE;AAGD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IAEX,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACrC,mBAAmB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACvC,qBAAqB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC1C;AAMD,eAAO,MAAM,6BAA6B,aAC9B,UAAU,+DAGnB,MAAM,gBAAgB,CAyCxB,CAAC;AAMF,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
1
+ {"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EAC4C,QAAQ,EAC5B,IAAI,EAQ+E,GAAG,EAMxH,QAAQ,EAET,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACgE,cAAc,EAGpF,MAAM,qBAAqB,CAAC;AAS7B,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAK5C,OAAO,EAAE,UAAU,IAAI,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAWpE,oBAAY,QAAQ;IAClB,IAAI,YAAY;IAChB,KAAK,UAAU;CAChB;AAED,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAEnE,KAAK,YAAY,GAAG;IAClB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AAMF,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG,GAAG,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAC;AAMrE,KAAK,gBAAgB,GAAG;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAqBF,KAAK,eAAe,GAAG;IACrB,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf,yBAAyB,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/C,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB,CAAC;AA6OF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe;oCA4lJpB,OAAO;sDAPW,gBAAgB,EAAE;;;;sBAhBjD,UAAU,GAAG,SAAS;;;8BA4Df,UAAU;2BAyBb,UAAU;;;iBAW3B,MAAM;kBACL,MAAM;kBACN,MAAM;qBACH,MAAM;;kBA9mJF,UAAU,WAAW,MAAM,YAAY,YAAY;sCA+qD/B,IAAI,GAAG,QAAQ;2CAg+FxC,UAAU,gBACV,CAAC,YAAY,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,gBAC/C,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,KACzC,kBAAkB,GAAG,IAAI;EAgD7B;AAGD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IAEX,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACrC,mBAAmB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACvC,qBAAqB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;CAC1C;AAMD,eAAO,MAAM,6BAA6B,aAC9B,UAAU,+DAGnB,MAAM,gBAAgB,CAyCxB,CAAC;AAMF,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
package/out/checker.js CHANGED
@@ -11,8 +11,11 @@ const type_predicate_1 = require("./typer/type-predicate");
11
11
  const type_manager_1 = require("./typer/type-manager");
12
12
  const service_2 = require("@lcap/nasl-concepts/service");
13
13
  const lodash_1 = require("lodash");
14
+ const asserts_1 = require("@lcap/nasl-concepts/asserts");
14
15
  const helper_1 = require("./typer/helper");
15
16
  const semantic_rules_1 = require("./semantic-rules");
17
+ const regeExp = new nasl_utils_1.ExpressionLexer();
18
+ regeExp.profile = nasl_utils_1.profiles.js;
16
19
  var Severity;
17
20
  (function (Severity) {
18
21
  Severity["WARN"] = "warning";
@@ -320,7 +323,30 @@ function createErrorDiagnoser(context) {
320
323
  }
321
324
  const finalNode = memberExpression ?? node;
322
325
  (0, helper_1.createOnPush)(diagnostics, finalNode, diagnostic);
323
- finalNode.tsErrorDetail = diagnostic;
326
+ const aggregationFields = {
327
+ assignmentLine: 'assignmentLines',
328
+ };
329
+ const nodeDiagnostics = diagnostics.get(finalNode);
330
+ if (nodeDiagnostics && nodeDiagnostics.length > 0) {
331
+ // 构建最终的 tsErrorDetail,合并所有上下文信息
332
+ const aggregatedContext = {};
333
+ // 对每个需要聚合的字段进行收集
334
+ for (const [singleField, arrayField] of Object.entries(aggregationFields)) {
335
+ const values = nodeDiagnostics
336
+ .map(d => d[singleField])
337
+ .filter((val) => val !== undefined);
338
+ if (values.length > 0) {
339
+ aggregatedContext[arrayField] = values;
340
+ }
341
+ }
342
+ finalNode.tsErrorDetail = {
343
+ ...diagnostic,
344
+ ...aggregatedContext,
345
+ };
346
+ }
347
+ else {
348
+ finalNode.tsErrorDetail = diagnostic;
349
+ }
324
350
  }
325
351
  else { }
326
352
  }
@@ -342,7 +368,7 @@ function createErrorDiagnoser(context) {
342
368
  * @returns
343
369
  */
344
370
  function getError(node) {
345
- const curFileNode = getCurFileNode();
371
+ const curFileNode = getCurFileNode() ?? node;
346
372
  const diagnostics = diagnosticMap.get(curFileNode);
347
373
  if (diagnostics) {
348
374
  return diagnostics.get(node);
@@ -1911,9 +1937,39 @@ function createErrorDiagnoser(context) {
1911
1937
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.views, function* (node) {
1912
1938
  yield* checkNode(node);
1913
1939
  });
1940
+ checkViewIndexPageSetting(node);
1914
1941
  }
1915
1942
  env.exitScope();
1916
1943
  }
1944
+ /**
1945
+ * 检查页面的首页/默认跳转页设置
1946
+ * 根据父级类型(Frontend 或 View)自动判断检查场景
1947
+ * @param node 当前页面节点
1948
+ */
1949
+ function checkViewIndexPageSetting(node) {
1950
+ const isNodeFrontend = (0, asserts_1.isFrontend)(node);
1951
+ const children = isNodeFrontend ? node.views : node.children;
1952
+ // 防御性检查:确保 children 存在且是数组
1953
+ if (!children || !Array.isArray(children)) {
1954
+ return;
1955
+ }
1956
+ // 检查默认跳转页数量,只允许有一个默认跳转页
1957
+ const defaultViews = children.filter((view) => view.isIndex);
1958
+ if (defaultViews.length > 1) {
1959
+ // 收集所有默认跳转页的名称
1960
+ const defaultViewNames = defaultViews.map((view) => view.name).join('、');
1961
+ const typeMap = {
1962
+ Frontend: '首页',
1963
+ View: '默认跳转页',
1964
+ };
1965
+ const pageType = typeMap[node.concept] || '默认跳转页';
1966
+ const errorMessage = `页面 ${node.name} 只能有一个 ${pageType},目前存在 ${defaultViewNames}`;
1967
+ error(node, errorMessage, {
1968
+ fileNode: node,
1969
+ isIndex: true,
1970
+ });
1971
+ }
1972
+ }
1917
1973
  /**
1918
1974
  * 检查 页面
1919
1975
  * @param node
@@ -1961,6 +2017,7 @@ function createErrorDiagnoser(context) {
1961
2017
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.children, function* (node) {
1962
2018
  yield* checkNode(node);
1963
2019
  });
2020
+ checkViewIndexPageSetting(node);
1964
2021
  }
1965
2022
  env.exitScope();
1966
2023
  }
@@ -3042,6 +3099,7 @@ function createErrorDiagnoser(context) {
3042
3099
  */
3043
3100
  function* checkIfStatement(node) {
3044
3101
  if (ensureNodeKeyExists(node, 'test')) {
3102
+ yield* rejectNullLiteral(node.test);
3045
3103
  yield* checkNode(node.test);
3046
3104
  }
3047
3105
  // then
@@ -3057,12 +3115,32 @@ function createErrorDiagnoser(context) {
3057
3115
  });
3058
3116
  }
3059
3117
  }
3118
+ /**
3119
+ * 检查 条件表达式
3120
+ * @param node
3121
+ */
3122
+ function* checkIfExpression(node) {
3123
+ if (ensureNodeKeyExists(node, 'condition')) {
3124
+ yield* rejectNullLiteral(node.condition);
3125
+ yield* checkNode(node.condition);
3126
+ }
3127
+ // then
3128
+ if (ensureNodeKeyExists(node, 'consequent')) {
3129
+ yield* checkNode(node.consequent);
3130
+ }
3131
+ // else
3132
+ if (ensureNodeKeyExists(node, 'alternate')) {
3133
+ yield* checkNode(node.alternate);
3134
+ }
3135
+ return env.getType(node);
3136
+ }
3060
3137
  /**
3061
3138
  * 检查 匹配
3062
3139
  * @param node
3063
3140
  */
3064
3141
  function* checkMatch(node) {
3065
3142
  if (ensureNodeKeyExists(node, 'expression')) {
3143
+ yield* rejectNullLiteral(node.expression);
3066
3144
  yield* checkNode(node.expression);
3067
3145
  }
3068
3146
  if (node.cases?.length) {
@@ -3317,6 +3395,7 @@ function createErrorDiagnoser(context) {
3317
3395
  function* checkSwitchCase(node) {
3318
3396
  if (!(0, nasl_concepts_1.isLastNode)(node)) {
3319
3397
  if (ensureNodeKeyExists(node, 'test')) {
3398
+ yield* rejectNullLiteral(node.test);
3320
3399
  yield* checkNode(node.test);
3321
3400
  }
3322
3401
  }
@@ -3361,6 +3440,7 @@ function createErrorDiagnoser(context) {
3361
3440
  */
3362
3441
  function* checkWhileStatement(node) {
3363
3442
  if (ensureNodeKeyExists(node, 'test')) {
3443
+ yield* rejectNullLiteral(node.test);
3364
3444
  yield* checkNode(node.test);
3365
3445
  }
3366
3446
  if (node.body?.length) {
@@ -3480,6 +3560,11 @@ function createErrorDiagnoser(context) {
3480
3560
  if (typeof node.value !== 'string') {
3481
3561
  error(node, '字符串');
3482
3562
  }
3563
+ const MAX_LENGTH = 2 ** 16;
3564
+ if (node.value.length > MAX_LENGTH) {
3565
+ // 插值内部的字符串过长时要报错在插值节点上有红框
3566
+ error(node.parentNode?.concept === 'StringInterpolation' ? node.parentNode : node, `存在过长文本,当前长度:${node.value.length},最大长度:${MAX_LENGTH}。建议使用变量拆分文本后再拼接。`);
3567
+ }
3483
3568
  return env.getType(node);
3484
3569
  }
3485
3570
  /**
@@ -3541,7 +3626,26 @@ function createErrorDiagnoser(context) {
3541
3626
  });
3542
3627
  }
3543
3628
  }
3544
- return env.getType(node);
3629
+ const ty = env.getType(node);
3630
+ function isConcreteTy(ty) {
3631
+ if (ty.typeKind === 'generic') {
3632
+ return !!ty.typeArguments?.every(x => isConcreteTy(x));
3633
+ }
3634
+ else if (ty.typeKind === 'primitive') {
3635
+ return ty !== type_manager_1.naslAnyTy;
3636
+ }
3637
+ return true;
3638
+ }
3639
+ if (!ty || !isConcreteTy(ty)) {
3640
+ const p = node.parentNode;
3641
+ if (p && p.name === 'dataSourceWatch' && (0, asserts_1.isBindAttribute)(p)) {
3642
+ // 🐮
3643
+ }
3644
+ else {
3645
+ error(node, `NewList 无法推导类型,请添加手动类型标注或添加元素`);
3646
+ }
3647
+ }
3648
+ return ty;
3545
3649
  }
3546
3650
  /**
3547
3651
  * 检查 Map 构造器
@@ -3616,9 +3720,121 @@ function createErrorDiagnoser(context) {
3616
3720
  }
3617
3721
  return type;
3618
3722
  }
3723
+ /**
3724
+ * 获取正则表达式错误的中文消息
3725
+ */
3726
+ function getRegexErrorMessage(errorId) {
3727
+ const errors = nasl_utils_1.referenceContent?.errors;
3728
+ return errors?.[errorId] || `正则表达式错误: ${errorId}`;
3729
+ }
3730
+ /**
3731
+ * 检查正则表达式标志符
3732
+ */
3733
+ function checkRegexFlags(flags, targetNode) {
3734
+ const VALID_FLAGS = new Set(['g', 'i', 'm', 's', 'u', 'y']);
3735
+ const flagCounts = new Map();
3736
+ for (const flag of flags) {
3737
+ if (!VALID_FLAGS.has(flag)) {
3738
+ error(targetNode, `正则表达式标志符 "${flag}" 无效`);
3739
+ continue;
3740
+ }
3741
+ const count = (flagCounts.get(flag) || 0) + 1;
3742
+ flagCounts.set(flag, count);
3743
+ if (count > 1) {
3744
+ error(targetNode, `正则表达式标志符 "${flag}" 重复`);
3745
+ }
3746
+ }
3747
+ }
3748
+ /**
3749
+ * 检查正则表达式语法和标志符
3750
+ */
3619
3751
  function checkNewRegex(node) {
3752
+ // 检查正则表达式模式语法
3753
+ if (nasl_concepts_1.asserts.isStringLiteral(node.pattern)) {
3754
+ const targetNode = node.kind === 'static' ? node : node.pattern;
3755
+ try {
3756
+ regeExp.parse(`/${node.pattern.value}/`);
3757
+ const parseErrors = regeExp.errors;
3758
+ if (Array.isArray(parseErrors) && parseErrors.length > 0) {
3759
+ parseErrors.forEach((errToken) => {
3760
+ const errorId = errToken?.error?.id;
3761
+ if (errorId) {
3762
+ error(targetNode, getRegexErrorMessage(errorId));
3763
+ }
3764
+ });
3765
+ }
3766
+ }
3767
+ catch (e) {
3768
+ error(targetNode, '正则表达式解析失败');
3769
+ }
3770
+ }
3771
+ // 检查正则表达式标志符
3772
+ if (nasl_concepts_1.asserts.isStringLiteral(node.flags)) {
3773
+ const targetNode = node.kind === 'static' ? node : node.flags;
3774
+ checkRegexFlags(node.flags.value, targetNode);
3775
+ }
3620
3776
  return env.getType(node);
3621
3777
  }
3778
+ /**
3779
+ * 递归检查赋值类型不兼容问题
3780
+ * 检查左侧类型(Decimal/Long)是否与右侧联合类型(Union<Decimal, Long>)不兼容
3781
+ * @param leftType 左侧类型
3782
+ * @param rightType 右侧类型
3783
+ * @param context 上下文信息,用于生成错误消息
3784
+ * @returns 所有类型不兼容的错误信息数组
3785
+ */
3786
+ function checkAssignmentTypeIncompatibility(leftType, rightType, context = {}) {
3787
+ if (!leftType || !rightType)
3788
+ return [];
3789
+ const errors = [];
3790
+ // 1. 检查普通类型:Decimal/Long vs Union<Decimal, Long>
3791
+ const isLeftDecimalOrLong = (leftType.typeKind === 'primitive' && leftType.typeName === 'Decimal') ||
3792
+ (leftType.typeKind === 'primitive' && leftType.typeName === 'Long');
3793
+ if (isLeftDecimalOrLong && (0, type_predicate_1.isDecimalUnionLongTy)(rightType)) {
3794
+ const prefix = context.prefix || '赋值';
3795
+ const fieldInfo = context.fieldName ? `字段 ${context.fieldName} ` : '';
3796
+ errors.push(`${prefix}:${fieldInfo}类型不匹配,不能将 ${(0, type_manager_1.showUserLevelType)(rightType)} 赋值给 ${(0, type_manager_1.showUserLevelType)(leftType)} 类型。`);
3797
+ return errors; // 找到基本类型错误后直接返回,不再检查嵌套
3798
+ }
3799
+ // 2. 检查 List 类型(包括嵌套):List<T> vs List<Union<...>>
3800
+ if (leftType.typeKind === 'generic' &&
3801
+ leftType.typeName === 'List' &&
3802
+ rightType.typeKind === 'generic' &&
3803
+ rightType.typeName === 'List' &&
3804
+ leftType.typeArguments?.[0] &&
3805
+ rightType.typeArguments?.[0]) {
3806
+ // 递归检查 List 的元素类型
3807
+ return checkAssignmentTypeIncompatibility(leftType.typeArguments[0], rightType.typeArguments[0], { ...context, prefix: context.prefix || '赋值' });
3808
+ }
3809
+ // 3. 检查 Map 类型(包括嵌套):Map<K, V> vs Map<K, Union<...>>
3810
+ if (leftType.typeKind === 'generic' &&
3811
+ leftType.typeName === 'Map' &&
3812
+ rightType.typeKind === 'generic' &&
3813
+ rightType.typeName === 'Map' &&
3814
+ leftType.typeArguments?.length === 2 &&
3815
+ rightType.typeArguments?.length === 2) {
3816
+ // 递归检查 Map 的值类型(第二个类型参数)
3817
+ const valueTypeErrors = checkAssignmentTypeIncompatibility(leftType.typeArguments[1], rightType.typeArguments[1], { ...context, prefix: context.prefix || '赋值' });
3818
+ errors.push(...valueTypeErrors);
3819
+ // 也检查键类型(第一个类型参数)
3820
+ const keyTypeErrors = checkAssignmentTypeIncompatibility(leftType.typeArguments[0], rightType.typeArguments[0], { ...context, prefix: context.prefix || '赋值' });
3821
+ errors.push(...keyTypeErrors);
3822
+ return errors;
3823
+ }
3824
+ // 4. 检查匿名结构体:递归检查每个字段,收集所有字段的错误
3825
+ if ((0, type_predicate_1.isAnonymousTy)(leftType) && (0, type_predicate_1.isAnonymousTy)(rightType)) {
3826
+ const leftProps = leftType.properties ?? [];
3827
+ const rightProps = rightType.properties ?? [];
3828
+ for (const leftProp of leftProps) {
3829
+ const rightProp = rightProps.find(p => p.name === leftProp.name);
3830
+ if (rightProp && leftProp.typeAnnotation && rightProp.typeAnnotation) {
3831
+ const fieldErrors = checkAssignmentTypeIncompatibility(leftProp.typeAnnotation, rightProp.typeAnnotation, { prefix: context.prefix || '赋值', fieldName: leftProp.name });
3832
+ errors.push(...fieldErrors); // 收集所有字段的错误,不提前返回
3833
+ }
3834
+ }
3835
+ }
3836
+ return errors;
3837
+ }
3622
3838
  /**
3623
3839
  * 检查 赋值语句
3624
3840
  * @param node
@@ -3630,6 +3846,14 @@ function createErrorDiagnoser(context) {
3630
3846
  if (ensureNodeKeyExists(node, 'right')) {
3631
3847
  yield* checkNode(node.right);
3632
3848
  }
3849
+ // 使用统一的类型兼容性检查
3850
+ const leftType = env.getType(node.left);
3851
+ const rightType = env.getType(node.right);
3852
+ const errorMessages = checkAssignmentTypeIncompatibility(leftType, rightType);
3853
+ // 报告所有错误
3854
+ errorMessages.forEach(errorMessage => {
3855
+ error(node, errorMessage);
3856
+ });
3633
3857
  }
3634
3858
  /**
3635
3859
  * 检查 批量赋值语句
@@ -3964,10 +4188,17 @@ function createErrorDiagnoser(context) {
3964
4188
  */
3965
4189
  function* checkBinaryExpression(node) {
3966
4190
  yield* semantic_rules_1.ExtraSemanticRules.rejectFileTypes(node, env, error);
4191
+ const isCandidate = ['+', '-', '*', '/', '%', '>', '<', '>=', '<='].includes(node.operator);
3967
4192
  if (ensureNodeKeyExists(node, 'left')) {
4193
+ if (isCandidate) {
4194
+ yield* rejectNullLiteral(node.left);
4195
+ }
3968
4196
  yield* checkNode(node.left);
3969
4197
  }
3970
4198
  if (ensureNodeKeyExists(node, 'right')) {
4199
+ if (isCandidate) {
4200
+ yield* rejectNullLiteral(node.right);
4201
+ }
3971
4202
  yield* checkNode(node.right);
3972
4203
  }
3973
4204
  return env.getType(node);
@@ -3980,6 +4211,7 @@ function createErrorDiagnoser(context) {
3980
4211
  error(node, '表达式不能为空!');
3981
4212
  }
3982
4213
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.expressions, function* (node) {
4214
+ yield* rejectNullLiteral(node);
3983
4215
  yield* checkNode(node);
3984
4216
  });
3985
4217
  return env.getType(node);
@@ -3990,6 +4222,10 @@ function createErrorDiagnoser(context) {
3990
4222
  */
3991
4223
  function* checkUnaryExpression(node) {
3992
4224
  if (ensureNodeKeyExists(node, 'argument')) {
4225
+ const isCandidate = ['!'].includes(node.operator);
4226
+ if (isCandidate) {
4227
+ yield* rejectNullLiteral(node.argument);
4228
+ }
3993
4229
  yield* checkNode(node.argument);
3994
4230
  }
3995
4231
  return env.getType(node);
@@ -4169,6 +4405,15 @@ function createErrorDiagnoser(context) {
4169
4405
  error(node, `无返回值的${node?.constructor?.nodeTitle}${node.calleeName} 不能在${parentNode?.constructor?.nodeTitle}中作为表达式使用。`);
4170
4406
  }
4171
4407
  }
4408
+ /**
4409
+ * 检查 不允许使用 null 字面量
4410
+ * @param node
4411
+ */
4412
+ function* rejectNullLiteral(node) {
4413
+ if (node.concept === 'NullLiteral') {
4414
+ error(node, `此处不允许使用 null`);
4415
+ }
4416
+ }
4172
4417
  function* dispatchNode(node) {
4173
4418
  try {
4174
4419
  // 调用表达式
@@ -4315,6 +4560,8 @@ function createErrorDiagnoser(context) {
4315
4560
  return yield* checkBlock(node);
4316
4561
  case 'IfStatement':
4317
4562
  return yield* checkIfStatement(node);
4563
+ case 'IfExpression':
4564
+ return yield* checkIfExpression(node);
4318
4565
  case 'Match':
4319
4566
  return yield* checkMatch(node);
4320
4567
  case 'MatchCase':
@@ -4516,6 +4763,8 @@ function createErrorDiagnoser(context) {
4516
4763
  }
4517
4764
  });
4518
4765
  diagnostics?.clear();
4766
+ // 删除 diagnosticMap 中的 key,避免内存泄漏和冗余报错
4767
+ diagnosticMap.delete(node);
4519
4768
  }
4520
4769
  else {
4521
4770
  diagnosticMap.clear();
@@ -4552,6 +4801,45 @@ function createErrorDiagnoser(context) {
4552
4801
  });
4553
4802
  return flattenedDiags;
4554
4803
  }
4804
+ /**
4805
+ * 重新验证祖先节点
4806
+ * 用于增量更新时,当子节点变更需要重新验证祖先节点的场景
4807
+ * @param ancestorNode 需要重新验证的祖先节点
4808
+ * @param validationFn 验证函数
4809
+ * @param errorFilter 错误过滤函数(可选),用于保留特定的错误
4810
+ * @returns 新的诊断信息 Map,如果没有则返回 null
4811
+ */
4812
+ function revalidateAncestorNode(ancestorNode, validationFn, errorFilter) {
4813
+ // 保存并过滤相关的错误诊断
4814
+ const errs = getError(ancestorNode) || [];
4815
+ const filteredErrs = errorFilter
4816
+ ? errs.filter(errorFilter)
4817
+ : [];
4818
+ // 清除祖先节点的诊断信息
4819
+ clearDiagnostics(ancestorNode);
4820
+ // 恢复过滤后的错误诊断
4821
+ if (filteredErrs.length > 0) {
4822
+ filteredErrs.forEach((err) => {
4823
+ const { message, severity, ...context } = err;
4824
+ if (message) {
4825
+ error(ancestorNode, message, {
4826
+ severity: severity,
4827
+ ...context,
4828
+ });
4829
+ }
4830
+ });
4831
+ }
4832
+ // 设置文件节点上下文并执行验证
4833
+ const cleanup = handleFileNode(ancestorNode);
4834
+ try {
4835
+ validationFn(ancestorNode, env);
4836
+ }
4837
+ finally {
4838
+ cleanup?.();
4839
+ }
4840
+ // 返回新的诊断信息
4841
+ return getDiagnostics(ancestorNode) || null;
4842
+ }
4555
4843
  return {
4556
4844
  setMetadataTypeEnable,
4557
4845
  setLatestVersionsOfSharedApp,
@@ -4562,6 +4850,8 @@ function createErrorDiagnoser(context) {
4562
4850
  getAllDiagnostics,
4563
4851
  getDebugDiagnostics,
4564
4852
  error,
4853
+ checkViewIndexPageSetting,
4854
+ revalidateAncestorNode,
4565
4855
  };
4566
4856
  }
4567
4857
  exports.createErrorDiagnoser = createErrorDiagnoser;