@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.
- package/out/checker.d.ts +3 -1
- package/out/checker.d.ts.map +1 -1
- package/out/checker.js +294 -32
- package/out/checker.js.map +1 -1
- package/out/reference-manager/collect-q-name.d.ts.map +1 -1
- package/out/reference-manager/collect-q-name.js +6 -3
- package/out/reference-manager/collect-q-name.js.map +1 -1
- package/out/reference-manager/reference-manager.d.ts +84 -14
- package/out/reference-manager/reference-manager.d.ts.map +1 -1
- package/out/reference-manager/reference-manager.js +217 -75
- package/out/reference-manager/reference-manager.js.map +1 -1
- package/out/reference-manager/update-nasl-fragment.d.ts.map +1 -1
- package/out/reference-manager/update-nasl-fragment.js +16 -10
- package/out/reference-manager/update-nasl-fragment.js.map +1 -1
- package/out/reference-manager/view-elem-logic.d.ts +3 -3
- package/out/reference-manager/view-elem-logic.d.ts.map +1 -1
- package/out/reference-manager/view-elem-logic.js +17 -0
- package/out/reference-manager/view-elem-logic.js.map +1 -1
- package/out/symbol/traverse/concepts/index.d.ts +1 -1
- package/out/symbol/traverse/concepts/index.d.ts.map +1 -1
- package/out/typer/collectGlobalDefs.d.ts.map +1 -1
- package/out/typer/collectGlobalDefs.js +25 -2
- package/out/typer/collectGlobalDefs.js.map +1 -1
- package/out/typer/component-def-manager/component-def-manager.js +1 -1
- 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 +6 -0
- package/out/typer/dispatch-call.js.map +1 -1
- package/out/typer/dispatch-def.d.ts.map +1 -1
- package/out/typer/dispatch-def.js +4 -7
- package/out/typer/dispatch-def.js.map +1 -1
- package/out/typer/dispatch-expr.d.ts +2 -1
- package/out/typer/dispatch-expr.d.ts.map +1 -1
- package/out/typer/dispatch-expr.js +177 -80
- package/out/typer/dispatch-expr.js.map +1 -1
- package/out/typer/dispatch-stmt.d.ts.map +1 -1
- package/out/typer/dispatch-stmt.js +36 -20
- package/out/typer/dispatch-stmt.js.map +1 -1
- package/out/typer/dispatch-view.d.ts.map +1 -1
- package/out/typer/dispatch-view.js +23 -14
- package/out/typer/dispatch-view.js.map +1 -1
- package/out/typer/get-oql-files.d.ts.map +1 -1
- package/out/typer/get-oql-files.js +2 -8
- package/out/typer/get-oql-files.js.map +1 -1
- package/out/typer/incremental-update.d.ts +12 -5
- package/out/typer/incremental-update.d.ts.map +1 -1
- package/out/typer/incremental-update.js +187 -18
- package/out/typer/incremental-update.js.map +1 -1
- package/out/typer/oql-checker/chain-call-transformer.d.ts.map +1 -1
- package/out/typer/oql-checker/chain-call-transformer.js +17 -4
- package/out/typer/oql-checker/chain-call-transformer.js.map +1 -1
- package/out/typer/oql-checker/ts-parser.d.ts.map +1 -1
- package/out/typer/oql-checker/ts-parser.js +157 -21
- package/out/typer/oql-checker/ts-parser.js.map +1 -1
- package/out/typer/overload-helper.d.ts.map +1 -1
- package/out/typer/overload-helper.js +9 -2
- package/out/typer/overload-helper.js.map +1 -1
- package/out/typer/subster.d.ts +1 -1
- package/out/typer/subster.d.ts.map +1 -1
- package/out/typer/subster.js +110 -31
- package/out/typer/subster.js.map +1 -1
- package/out/typer/type-predicate.d.ts +1 -0
- package/out/typer/type-predicate.d.ts.map +1 -1
- package/out/typer/type-predicate.js +43 -4
- package/out/typer/type-predicate.js.map +1 -1
- package/out/typer/typer.d.ts +31 -2
- package/out/typer/typer.d.ts.map +1 -1
- package/out/typer/typer.js +176 -18
- package/out/typer/typer.js.map +1 -1
- package/out/typer/unifier.d.ts +10 -1
- package/out/typer/unifier.d.ts.map +1 -1
- package/out/typer/unifier.js +52 -6
- package/out/typer/unifier.js.map +1 -1
- 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;
|
package/out/checker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,
|
|
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
|
-
|
|
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
|
-
//
|
|
3849
|
+
// 使用统一的类型兼容性检查
|
|
3631
3850
|
const leftType = env.getType(node.left);
|
|
3632
3851
|
const rightType = env.getType(node.right);
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
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;
|