@lcap/nasl-language-server-core 4.4.0-beta.1 → 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 (74) hide show
  1. package/out/checker.d.ts +3 -1
  2. package/out/checker.d.ts.map +1 -1
  3. package/out/checker.js +294 -32
  4. package/out/checker.js.map +1 -1
  5. package/out/reference-manager/collect-q-name.d.ts.map +1 -1
  6. package/out/reference-manager/collect-q-name.js +6 -3
  7. package/out/reference-manager/collect-q-name.js.map +1 -1
  8. package/out/reference-manager/reference-manager.d.ts +84 -14
  9. package/out/reference-manager/reference-manager.d.ts.map +1 -1
  10. package/out/reference-manager/reference-manager.js +217 -75
  11. package/out/reference-manager/reference-manager.js.map +1 -1
  12. package/out/reference-manager/update-nasl-fragment.d.ts.map +1 -1
  13. package/out/reference-manager/update-nasl-fragment.js +16 -10
  14. package/out/reference-manager/update-nasl-fragment.js.map +1 -1
  15. package/out/reference-manager/view-elem-logic.d.ts +3 -3
  16. package/out/reference-manager/view-elem-logic.d.ts.map +1 -1
  17. package/out/reference-manager/view-elem-logic.js +17 -0
  18. package/out/reference-manager/view-elem-logic.js.map +1 -1
  19. package/out/symbol/traverse/concepts/index.d.ts +1 -1
  20. package/out/symbol/traverse/concepts/index.d.ts.map +1 -1
  21. package/out/typer/collectGlobalDefs.d.ts.map +1 -1
  22. package/out/typer/collectGlobalDefs.js +25 -2
  23. package/out/typer/collectGlobalDefs.js.map +1 -1
  24. package/out/typer/component-def-manager/component-def-manager.js +1 -1
  25. package/out/typer/component-def-manager/component-def-manager.js.map +1 -1
  26. package/out/typer/dispatch-call.d.ts.map +1 -1
  27. package/out/typer/dispatch-call.js +6 -0
  28. package/out/typer/dispatch-call.js.map +1 -1
  29. package/out/typer/dispatch-def.d.ts.map +1 -1
  30. package/out/typer/dispatch-def.js +4 -7
  31. package/out/typer/dispatch-def.js.map +1 -1
  32. package/out/typer/dispatch-expr.d.ts +2 -1
  33. package/out/typer/dispatch-expr.d.ts.map +1 -1
  34. package/out/typer/dispatch-expr.js +177 -80
  35. package/out/typer/dispatch-expr.js.map +1 -1
  36. package/out/typer/dispatch-stmt.d.ts.map +1 -1
  37. package/out/typer/dispatch-stmt.js +36 -20
  38. package/out/typer/dispatch-stmt.js.map +1 -1
  39. package/out/typer/dispatch-view.d.ts.map +1 -1
  40. package/out/typer/dispatch-view.js +23 -14
  41. package/out/typer/dispatch-view.js.map +1 -1
  42. package/out/typer/get-oql-files.d.ts.map +1 -1
  43. package/out/typer/get-oql-files.js +2 -8
  44. package/out/typer/get-oql-files.js.map +1 -1
  45. package/out/typer/incremental-update.d.ts +12 -5
  46. package/out/typer/incremental-update.d.ts.map +1 -1
  47. package/out/typer/incremental-update.js +187 -18
  48. package/out/typer/incremental-update.js.map +1 -1
  49. package/out/typer/oql-checker/chain-call-transformer.d.ts.map +1 -1
  50. package/out/typer/oql-checker/chain-call-transformer.js +17 -4
  51. package/out/typer/oql-checker/chain-call-transformer.js.map +1 -1
  52. package/out/typer/oql-checker/ts-parser.d.ts.map +1 -1
  53. package/out/typer/oql-checker/ts-parser.js +157 -21
  54. package/out/typer/oql-checker/ts-parser.js.map +1 -1
  55. package/out/typer/overload-helper.d.ts.map +1 -1
  56. package/out/typer/overload-helper.js +9 -2
  57. package/out/typer/overload-helper.js.map +1 -1
  58. package/out/typer/subster.d.ts +1 -1
  59. package/out/typer/subster.d.ts.map +1 -1
  60. package/out/typer/subster.js +110 -31
  61. package/out/typer/subster.js.map +1 -1
  62. package/out/typer/type-predicate.d.ts +1 -0
  63. package/out/typer/type-predicate.d.ts.map +1 -1
  64. package/out/typer/type-predicate.js +43 -4
  65. package/out/typer/type-predicate.js.map +1 -1
  66. package/out/typer/typer.d.ts +31 -2
  67. package/out/typer/typer.d.ts.map +1 -1
  68. package/out/typer/typer.js +176 -18
  69. package/out/typer/typer.js.map +1 -1
  70. package/out/typer/unifier.d.ts +10 -1
  71. package/out/typer/unifier.d.ts.map +1 -1
  72. package/out/typer/unifier.js +52 -6
  73. package/out/typer/unifier.js.map +1 -1
  74. package/package.json +6 -6
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;AAOpE,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;oCA01IpB,OAAO;sDAPW,gBAAgB,EAAE;;;;sBAhBjD,UAAU,GAAG,SAAS;;;8BA4Df,UAAU;2BAyBb,UAAU;;;iBAW3B,MAAM;kBACL,MAAM;kBACN,MAAM;qBACH,MAAM;;kBA72IF,UAAU,WAAW,MAAM,YAAY,YAAY;EAg5IzE;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
@@ -10,10 +10,12 @@ const sem_diag_1 = require("./typer/sem-diag");
10
10
  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
- const asserts_1 = require("@lcap/nasl-concepts/asserts");
14
13
  const lodash_1 = require("lodash");
14
+ const asserts_1 = require("@lcap/nasl-concepts/asserts");
15
15
  const helper_1 = require("./typer/helper");
16
16
  const semantic_rules_1 = require("./semantic-rules");
17
+ const regeExp = new nasl_utils_1.ExpressionLexer();
18
+ regeExp.profile = nasl_utils_1.profiles.js;
17
19
  var Severity;
18
20
  (function (Severity) {
19
21
  Severity["WARN"] = "warning";
@@ -321,7 +323,30 @@ function createErrorDiagnoser(context) {
321
323
  }
322
324
  const finalNode = memberExpression ?? node;
323
325
  (0, helper_1.createOnPush)(diagnostics, finalNode, diagnostic);
324
- 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
+ }
325
350
  }
326
351
  else { }
327
352
  }
@@ -343,7 +368,7 @@ function createErrorDiagnoser(context) {
343
368
  * @returns
344
369
  */
345
370
  function getError(node) {
346
- const curFileNode = getCurFileNode();
371
+ const curFileNode = getCurFileNode() ?? node;
347
372
  const diagnostics = diagnosticMap.get(curFileNode);
348
373
  if (diagnostics) {
349
374
  return diagnostics.get(node);
@@ -1912,9 +1937,39 @@ function createErrorDiagnoser(context) {
1912
1937
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.views, function* (node) {
1913
1938
  yield* checkNode(node);
1914
1939
  });
1940
+ checkViewIndexPageSetting(node);
1915
1941
  }
1916
1942
  env.exitScope();
1917
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
+ }
1918
1973
  /**
1919
1974
  * 检查 页面
1920
1975
  * @param node
@@ -1962,6 +2017,7 @@ function createErrorDiagnoser(context) {
1962
2017
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.children, function* (node) {
1963
2018
  yield* checkNode(node);
1964
2019
  });
2020
+ checkViewIndexPageSetting(node);
1965
2021
  }
1966
2022
  env.exitScope();
1967
2023
  }
@@ -1971,6 +2027,17 @@ function createErrorDiagnoser(context) {
1971
2027
  */
1972
2028
  function* checkBusinessComponent(node) {
1973
2029
  env.enterScope(node);
2030
+ if (node.params?.length) {
2031
+ yield* checkParams(node.params);
2032
+ yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.params, function* (node) {
2033
+ yield* checkNode(node);
2034
+ });
2035
+ }
2036
+ if (node.variables?.length) {
2037
+ yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.variables, function* (node) {
2038
+ yield* checkNode(node);
2039
+ });
2040
+ }
1974
2041
  // 组件元素
1975
2042
  if (node.elements?.length) {
1976
2043
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.elements, function* (node) {
@@ -2930,15 +2997,27 @@ function createErrorDiagnoser(context) {
2930
2997
  }
2931
2998
  return type;
2932
2999
  function checkListSort(nd) {
3000
+ const parTy = env.getType(nd.arguments?.[0]);
3001
+ let paramType = '未知类型';
3002
+ if (parTy?.typeName === 'List') {
3003
+ paramType = (0, type_manager_1.showUserLevelType)(parTy.typeArguments?.[0] || type_manager_1.naslAnyTy);
3004
+ }
3005
+ const expectTy = `{ by: (item: ${paramType}) => 未知类型, asc: 布尔值 }`;
2933
3006
  for (let i = 1; i < nd.arguments.length; i++) {
2934
3007
  const expr = nd.arguments[i].expression;
2935
3008
  let resTy;
2936
3009
  if (expr?.concept === 'AnonymousFunction') {
2937
3010
  resTy = env.getType(expr.body);
3011
+ if (!nasl_concepts_1.asserts.isNewComposite(expr.body)) {
3012
+ error(nd.arguments[i], (0, sem_diag_1.mkIncompatibleTyErr)(env, env.getType(expr) || type_manager_1.naslAnyTy, expectTy));
3013
+ }
2938
3014
  }
2939
3015
  else if (expr?.concept === 'SubLogic') {
2940
3016
  resTy = env.getType(expr.returns?.[0]);
2941
3017
  }
3018
+ else {
3019
+ error(nd.arguments[i], (0, sem_diag_1.mkIncompatibleTyErr)(env, env.getType(expr) || type_manager_1.naslAnyTy, expectTy));
3020
+ }
2942
3021
  const byTy = resTy?.properties?.find((prop) => prop.name === 'by')?.typeAnnotation;
2943
3022
  if (byTy?.typeKind === 'union') {
2944
3023
  // 这里使用 node.arguments[i] 则无红框,猜测是 ListSort 的特殊交互导致的问题
@@ -3020,6 +3099,7 @@ function createErrorDiagnoser(context) {
3020
3099
  */
3021
3100
  function* checkIfStatement(node) {
3022
3101
  if (ensureNodeKeyExists(node, 'test')) {
3102
+ yield* rejectNullLiteral(node.test);
3023
3103
  yield* checkNode(node.test);
3024
3104
  }
3025
3105
  // then
@@ -3035,12 +3115,32 @@ function createErrorDiagnoser(context) {
3035
3115
  });
3036
3116
  }
3037
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
+ }
3038
3137
  /**
3039
3138
  * 检查 匹配
3040
3139
  * @param node
3041
3140
  */
3042
3141
  function* checkMatch(node) {
3043
3142
  if (ensureNodeKeyExists(node, 'expression')) {
3143
+ yield* rejectNullLiteral(node.expression);
3044
3144
  yield* checkNode(node.expression);
3045
3145
  }
3046
3146
  if (node.cases?.length) {
@@ -3295,6 +3395,7 @@ function createErrorDiagnoser(context) {
3295
3395
  function* checkSwitchCase(node) {
3296
3396
  if (!(0, nasl_concepts_1.isLastNode)(node)) {
3297
3397
  if (ensureNodeKeyExists(node, 'test')) {
3398
+ yield* rejectNullLiteral(node.test);
3298
3399
  yield* checkNode(node.test);
3299
3400
  }
3300
3401
  }
@@ -3339,6 +3440,7 @@ function createErrorDiagnoser(context) {
3339
3440
  */
3340
3441
  function* checkWhileStatement(node) {
3341
3442
  if (ensureNodeKeyExists(node, 'test')) {
3443
+ yield* rejectNullLiteral(node.test);
3342
3444
  yield* checkNode(node.test);
3343
3445
  }
3344
3446
  if (node.body?.length) {
@@ -3458,6 +3560,11 @@ function createErrorDiagnoser(context) {
3458
3560
  if (typeof node.value !== 'string') {
3459
3561
  error(node, '字符串');
3460
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
+ }
3461
3568
  return env.getType(node);
3462
3569
  }
3463
3570
  /**
@@ -3613,9 +3720,121 @@ function createErrorDiagnoser(context) {
3613
3720
  }
3614
3721
  return type;
3615
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
+ */
3616
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
+ }
3617
3776
  return env.getType(node);
3618
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
+ }
3619
3838
  /**
3620
3839
  * 检查 赋值语句
3621
3840
  * @param node
@@ -3627,37 +3846,14 @@ function createErrorDiagnoser(context) {
3627
3846
  if (ensureNodeKeyExists(node, 'right')) {
3628
3847
  yield* checkNode(node.right);
3629
3848
  }
3630
- // 检查左侧为 Decimal,右侧为 Union<Decimal, Integer> 的情况
3849
+ // 使用统一的类型兼容性检查
3631
3850
  const leftType = env.getType(node.left);
3632
3851
  const rightType = env.getType(node.right);
3633
- // 检查左右两侧同为匿名结构体时,对每个字段,都增加 Decimal 和 Union<Decimal, Long> 的检查
3634
- if (leftType && rightType && (0, type_predicate_1.isAnonymousTy)(leftType) && (0, type_predicate_1.isAnonymousTy)(rightType)) {
3635
- const leftProps = leftType.properties ?? [];
3636
- const rightProps = rightType.properties ?? [];
3637
- for (const leftProp of leftProps) {
3638
- const rightProp = rightProps.find(p => p.name === leftProp.name);
3639
- if (rightProp) {
3640
- if (leftProp.typeAnnotation && rightProp.typeAnnotation) {
3641
- if ((0, type_predicate_1.isDecimalTy)(leftProp.typeAnnotation) && (0, type_predicate_1.isDecimalUnionLongTy)(rightProp.typeAnnotation)) {
3642
- error(node, `赋值:字段 ${leftProp.name} 类型不匹配,不能将 ${(0, type_manager_1.showUserLevelType)(rightProp.typeAnnotation)} 赋值给 ${(0, type_manager_1.showUserLevelType)(leftProp.typeAnnotation)} 类型。`);
3643
- }
3644
- }
3645
- }
3646
- }
3647
- }
3648
- if (leftType && rightType) {
3649
- // 检查左侧是否为 Decimal
3650
- const isLeftDecimal = leftType.typeKind === 'primitive' && leftType.typeName === 'Decimal';
3651
- // 检查右侧是否为 Union<Decimal, Long> 或 Union<Long, Decimal>
3652
- if (isLeftDecimal && rightType.typeKind === 'union' && rightType.typeArguments) {
3653
- const typeArgs = rightType.typeArguments;
3654
- const hasDecimal = typeArgs.some((arg) => arg.typeKind === 'primitive' && arg.typeName === 'Decimal');
3655
- const hasLong = typeArgs.some((arg) => arg.typeKind === 'primitive' && arg.typeName === 'Long');
3656
- if (hasDecimal && hasLong && typeArgs.length === 2) {
3657
- error(node, `赋值:类型不匹配,不能将联合类型 ${(0, type_manager_1.showUserLevelType)(rightType)} 赋值给 ${(0, type_manager_1.showUserLevelType)(leftType)} 类型。`);
3658
- }
3659
- }
3660
- }
3852
+ const errorMessages = checkAssignmentTypeIncompatibility(leftType, rightType);
3853
+ // 报告所有错误
3854
+ errorMessages.forEach(errorMessage => {
3855
+ error(node, errorMessage);
3856
+ });
3661
3857
  }
3662
3858
  /**
3663
3859
  * 检查 批量赋值语句
@@ -3992,10 +4188,17 @@ function createErrorDiagnoser(context) {
3992
4188
  */
3993
4189
  function* checkBinaryExpression(node) {
3994
4190
  yield* semantic_rules_1.ExtraSemanticRules.rejectFileTypes(node, env, error);
4191
+ const isCandidate = ['+', '-', '*', '/', '%', '>', '<', '>=', '<='].includes(node.operator);
3995
4192
  if (ensureNodeKeyExists(node, 'left')) {
4193
+ if (isCandidate) {
4194
+ yield* rejectNullLiteral(node.left);
4195
+ }
3996
4196
  yield* checkNode(node.left);
3997
4197
  }
3998
4198
  if (ensureNodeKeyExists(node, 'right')) {
4199
+ if (isCandidate) {
4200
+ yield* rejectNullLiteral(node.right);
4201
+ }
3999
4202
  yield* checkNode(node.right);
4000
4203
  }
4001
4204
  return env.getType(node);
@@ -4008,6 +4211,7 @@ function createErrorDiagnoser(context) {
4008
4211
  error(node, '表达式不能为空!');
4009
4212
  }
4010
4213
  yield* (0, nasl_utils_1.wrapForEachToGenerator)(node.expressions, function* (node) {
4214
+ yield* rejectNullLiteral(node);
4011
4215
  yield* checkNode(node);
4012
4216
  });
4013
4217
  return env.getType(node);
@@ -4018,6 +4222,10 @@ function createErrorDiagnoser(context) {
4018
4222
  */
4019
4223
  function* checkUnaryExpression(node) {
4020
4224
  if (ensureNodeKeyExists(node, 'argument')) {
4225
+ const isCandidate = ['!'].includes(node.operator);
4226
+ if (isCandidate) {
4227
+ yield* rejectNullLiteral(node.argument);
4228
+ }
4021
4229
  yield* checkNode(node.argument);
4022
4230
  }
4023
4231
  return env.getType(node);
@@ -4197,6 +4405,15 @@ function createErrorDiagnoser(context) {
4197
4405
  error(node, `无返回值的${node?.constructor?.nodeTitle}${node.calleeName} 不能在${parentNode?.constructor?.nodeTitle}中作为表达式使用。`);
4198
4406
  }
4199
4407
  }
4408
+ /**
4409
+ * 检查 不允许使用 null 字面量
4410
+ * @param node
4411
+ */
4412
+ function* rejectNullLiteral(node) {
4413
+ if (node.concept === 'NullLiteral') {
4414
+ error(node, `此处不允许使用 null`);
4415
+ }
4416
+ }
4200
4417
  function* dispatchNode(node) {
4201
4418
  try {
4202
4419
  // 调用表达式
@@ -4233,6 +4450,8 @@ function createErrorDiagnoser(context) {
4233
4450
  return yield* checkBackendVariable(node);
4234
4451
  case 'Param':
4235
4452
  return yield* checkParam(node);
4453
+ case 'ParamWithGroup':
4454
+ return yield* checkParam(node);
4236
4455
  case 'Return':
4237
4456
  return yield* checkReturn(node);
4238
4457
  case 'DefaultValue':
@@ -4341,6 +4560,8 @@ function createErrorDiagnoser(context) {
4341
4560
  return yield* checkBlock(node);
4342
4561
  case 'IfStatement':
4343
4562
  return yield* checkIfStatement(node);
4563
+ case 'IfExpression':
4564
+ return yield* checkIfExpression(node);
4344
4565
  case 'Match':
4345
4566
  return yield* checkMatch(node);
4346
4567
  case 'MatchCase':
@@ -4580,6 +4801,45 @@ function createErrorDiagnoser(context) {
4580
4801
  });
4581
4802
  return flattenedDiags;
4582
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
+ }
4583
4843
  return {
4584
4844
  setMetadataTypeEnable,
4585
4845
  setLatestVersionsOfSharedApp,
@@ -4590,6 +4850,8 @@ function createErrorDiagnoser(context) {
4590
4850
  getAllDiagnostics,
4591
4851
  getDebugDiagnostics,
4592
4852
  error,
4853
+ checkViewIndexPageSetting,
4854
+ revalidateAncestorNode,
4593
4855
  };
4594
4856
  }
4595
4857
  exports.createErrorDiagnoser = createErrorDiagnoser;