@atlisp/lint 0.1.17 → 0.1.19
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/README.md +3 -4
- package/atlisp-lint.default.json +12 -5
- package/dist/checks/bare-names.js +2 -0
- package/dist/checks/constant-condition.js +3 -0
- package/dist/checks/empty-catch.js +68 -23
- package/dist/checks/entget-in-loop.d.ts +3 -0
- package/dist/checks/entget-in-loop.js +56 -0
- package/dist/checks/if-without-else.d.ts +4 -0
- package/dist/checks/if-without-else.js +31 -0
- package/dist/checks/long-function-call.js +1 -1
- package/dist/checks/promise-handler.d.ts +4 -0
- package/dist/checks/promise-handler.js +53 -0
- package/dist/checks/redundant-list.d.ts +4 -0
- package/dist/checks/redundant-list.js +22 -0
- package/dist/checks/string-concat-loop.d.ts +3 -0
- package/dist/checks/string-concat-loop.js +59 -0
- package/dist/checks/undeclared-setq.js +2 -0
- package/dist/checks/unused-catch-result.d.ts +3 -0
- package/dist/checks/unused-catch-result.js +28 -0
- package/dist/config.js +11 -4
- package/dist/formatters-html.d.ts +3 -0
- package/dist/formatters-html.js +91 -0
- package/dist/formatters-sarif.d.ts +3 -0
- package/dist/formatters-sarif.js +55 -0
- package/dist/index.js +41 -2
- package/dist/locale.js +23 -7
- package/dist/presets.js +16 -2
- package/dist/project.js +180 -2
- package/dist/rules.js +8 -1
- package/dist/runner.d.ts +1 -2
- package/dist/runner.js +106 -224
- package/dist/validate.js +8 -1
- package/dist/visitor-runner.d.ts +4 -0
- package/dist/visitor-runner.js +709 -0
- package/package.json +1 -1
- package/dist/atlisp-lint.default.json +0 -90
- package/dist/lib/lint-sbcl.lisp +0 -161
- package/dist/stub-packages.json +0 -41
package/README.md
CHANGED
|
@@ -92,7 +92,6 @@ npx @atlisp/lint --format-check
|
|
|
92
92
|
| `constant_condition` | warn | 风格 | if/while/cond 中常量条件 |
|
|
93
93
|
| `redundant_progn` | warn | 风格 | 分支中多余 progn |
|
|
94
94
|
| `empty_branch` | warn | 风格 | if/cond 中空分支 |
|
|
95
|
-
| `redundant_cond` | warn | 风格 | 单子句 cond 或末尾 T 子句 |
|
|
96
95
|
| `redundant_if` | warn | 风格 | if/when 中冗余 progn |
|
|
97
96
|
| `redundant_let` | warn | 风格 | 无绑定 let 建议改用 progn |
|
|
98
97
|
| `redundant_quotes` | warn | 风格 | 冗余双引号 ''x |
|
|
@@ -232,12 +231,12 @@ npx @atlisp/lint --format-check
|
|
|
232
231
|
},
|
|
233
232
|
"preset": "recommended",
|
|
234
233
|
"line_length": {
|
|
235
|
-
"max":
|
|
234
|
+
"max": 200,
|
|
236
235
|
"tab_width": 2
|
|
237
236
|
},
|
|
238
237
|
"function_complexity": {
|
|
239
|
-
"max_lines":
|
|
240
|
-
"max_nesting":
|
|
238
|
+
"max_lines": 300,
|
|
239
|
+
"max_nesting": 30
|
|
241
240
|
},
|
|
242
241
|
"cl_syntax": {
|
|
243
242
|
"keywords": ["&key"]
|
package/atlisp-lint.default.json
CHANGED
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"unused_let_binding": "warn",
|
|
32
32
|
"recursive_call": "warn",
|
|
33
33
|
"variable_shadow": "warn",
|
|
34
|
-
"redundant_cond": "warn",
|
|
35
34
|
"unused_local_fun": "warn",
|
|
36
35
|
"multiple_setq": "warn",
|
|
37
36
|
"redundant_quotes": "warn",
|
|
@@ -80,15 +79,23 @@
|
|
|
80
79
|
"loop_optimization": "off",
|
|
81
80
|
"type_check": "off",
|
|
82
81
|
"redundant_if": "warn",
|
|
83
|
-
"undeclared_setq": "warn"
|
|
82
|
+
"undeclared_setq": "warn",
|
|
83
|
+
"unused_catch_result": "warn",
|
|
84
|
+
"string_concat_loop": "warn",
|
|
85
|
+
"if_without_else": "warn",
|
|
86
|
+
"redundant_list": "warn",
|
|
87
|
+
"entget_in_loop": "warn",
|
|
88
|
+
"promise_handler": "warn",
|
|
89
|
+
"module_cycle": "warn",
|
|
90
|
+
"arg_count_project": "warn"
|
|
84
91
|
},
|
|
85
92
|
"line_length": {
|
|
86
|
-
"max":
|
|
93
|
+
"max": 200,
|
|
87
94
|
"tab_width": 2
|
|
88
95
|
},
|
|
89
96
|
"function_complexity": {
|
|
90
|
-
"max_lines":
|
|
91
|
-
"max_nesting":
|
|
97
|
+
"max_lines": 300,
|
|
98
|
+
"max_nesting": 30
|
|
92
99
|
},
|
|
93
100
|
"cl_syntax": {
|
|
94
101
|
"keywords": ["&key"]
|
|
@@ -18,6 +18,8 @@ function checkBareFunctionNames(content, file, allowlist, namespacePattern) {
|
|
|
18
18
|
continue; // local: defun foo/bar
|
|
19
19
|
if (name.startsWith('cb-'))
|
|
20
20
|
continue; // DCL callback
|
|
21
|
+
if (name === '*error*')
|
|
22
|
+
continue;
|
|
21
23
|
if (!nsRegex.test(name) && !name.includes(':')) {
|
|
22
24
|
issues.push({
|
|
23
25
|
file,
|
|
@@ -41,6 +41,9 @@ function checkConstantConditionAst(ast, file) {
|
|
|
41
41
|
continue;
|
|
42
42
|
const condTest = clause.children[0];
|
|
43
43
|
if (condTest.type === 'symbol' && condTest.name && CONSTANTS.has(condTest.name)) {
|
|
44
|
+
// Skip last clause with T — that's the standard else/default pattern
|
|
45
|
+
if (condTest.name === 'T' && i === condNode.children.length - 1)
|
|
46
|
+
continue;
|
|
44
47
|
issues.push({
|
|
45
48
|
file,
|
|
46
49
|
line: condTest.pos.line,
|
|
@@ -2,33 +2,78 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkEmptyCatch = checkEmptyCatch;
|
|
4
4
|
const locale_1 = require("../locale");
|
|
5
|
-
const
|
|
5
|
+
const parser_1 = require("@atlisp/parser");
|
|
6
6
|
function checkEmptyCatch(content, file) {
|
|
7
7
|
const issues = [];
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
if (!hasErrorCheck) {
|
|
22
|
-
issues.push({
|
|
23
|
-
file,
|
|
24
|
-
line: i + 1,
|
|
25
|
-
severity: 'warn',
|
|
26
|
-
rule: 'empty_catch',
|
|
27
|
-
message: (0, locale_1.t)('empty_catch'),
|
|
28
|
-
});
|
|
29
|
-
}
|
|
8
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
9
|
+
const catchNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'vl-catch-all-apply'));
|
|
10
|
+
for (const node of catchNodes) {
|
|
11
|
+
if (isInQuote(node))
|
|
12
|
+
continue;
|
|
13
|
+
if (isWrappedByCatchError(node))
|
|
14
|
+
continue;
|
|
15
|
+
if (isSetqStored(node)) {
|
|
16
|
+
const varName = getSetqVarName(node);
|
|
17
|
+
if (varName && hasErrorCheckInSiblings(node, varName))
|
|
18
|
+
continue;
|
|
30
19
|
}
|
|
20
|
+
issues.push({
|
|
21
|
+
file,
|
|
22
|
+
line: node.pos.line,
|
|
23
|
+
severity: 'warn',
|
|
24
|
+
rule: 'empty_catch',
|
|
25
|
+
message: (0, locale_1.t)('empty_catch'),
|
|
26
|
+
});
|
|
31
27
|
}
|
|
32
28
|
return issues;
|
|
33
29
|
}
|
|
30
|
+
function isInQuote(node) {
|
|
31
|
+
let p = node.parent;
|
|
32
|
+
while (p) {
|
|
33
|
+
if ((0, parser_1.astIsList)(p, 'quote'))
|
|
34
|
+
return true;
|
|
35
|
+
p = p.parent;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function isWrappedByCatchError(node) {
|
|
40
|
+
const parent = node.parent;
|
|
41
|
+
return !!(parent && (0, parser_1.astIsList)(parent, 'vl-catch-all-error-p'));
|
|
42
|
+
}
|
|
43
|
+
function isSetqStored(node) {
|
|
44
|
+
const parent = node.parent;
|
|
45
|
+
if (!parent || !(0, parser_1.astIsList)(parent, 'setq'))
|
|
46
|
+
return false;
|
|
47
|
+
if (!parent.children || parent.children.length < 3)
|
|
48
|
+
return false;
|
|
49
|
+
const idx = parent.children.indexOf(node);
|
|
50
|
+
return idx >= 2 && idx % 2 === 0;
|
|
51
|
+
}
|
|
52
|
+
function getSetqVarName(node) {
|
|
53
|
+
const parent = node.parent;
|
|
54
|
+
if (!parent || !parent.children)
|
|
55
|
+
return null;
|
|
56
|
+
const idx = parent.children.indexOf(node);
|
|
57
|
+
const varNode = parent.children[idx - 1];
|
|
58
|
+
if (varNode && varNode.type === 'symbol' && varNode.name) {
|
|
59
|
+
return varNode.name;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function hasErrorCheckInSiblings(node, varName) {
|
|
64
|
+
const parent = node.parent;
|
|
65
|
+
if (!parent || !parent.children)
|
|
66
|
+
return false;
|
|
67
|
+
const idx = parent.children.indexOf(node);
|
|
68
|
+
for (let i = idx + 1; i < parent.children.length; i++) {
|
|
69
|
+
const sibling = parent.children[i];
|
|
70
|
+
if ((0, parser_1.astIsList)(sibling, 'vl-catch-all-error-p')) {
|
|
71
|
+
if (sibling.children && sibling.children.length >= 2 &&
|
|
72
|
+
(0, parser_1.astIsSymbol)(sibling.children[1], varName)) {
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
34
79
|
//# sourceMappingURL=empty-catch.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkEntgetInLoop = checkEntgetInLoop;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
function isInsideLoop(ast, targetNode) {
|
|
6
|
+
let node = targetNode.parent;
|
|
7
|
+
while (node) {
|
|
8
|
+
if (node.type === 'list' && node.children && node.children.length > 0 &&
|
|
9
|
+
node.children[0].type === 'symbol') {
|
|
10
|
+
const name = node.children[0].name;
|
|
11
|
+
if (name === 'while' || name === 'repeat' || name === 'foreach') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
node = node.parent;
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
function checkEntgetInLoop(content, file) {
|
|
20
|
+
const issues = [];
|
|
21
|
+
const lines = content.split('\n');
|
|
22
|
+
const re = /\((while|repeat|foreach)\s/;
|
|
23
|
+
for (let i = 0; i < lines.length; i++) {
|
|
24
|
+
if (re.test(lines[i])) {
|
|
25
|
+
let depth = 0;
|
|
26
|
+
let inLoop = false;
|
|
27
|
+
let entgetCount = 0;
|
|
28
|
+
for (let j = i; j < Math.min(i + 50, lines.length); j++) {
|
|
29
|
+
for (let k = 0; k < lines[j].length; k++) {
|
|
30
|
+
if (lines[j][k] === '(') {
|
|
31
|
+
depth++;
|
|
32
|
+
if (!inLoop)
|
|
33
|
+
inLoop = true;
|
|
34
|
+
}
|
|
35
|
+
else if (lines[j][k] === ')')
|
|
36
|
+
depth--;
|
|
37
|
+
}
|
|
38
|
+
if (lines[j].includes('entget'))
|
|
39
|
+
entgetCount++;
|
|
40
|
+
if (depth === 0 && inLoop)
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
if (entgetCount > 0) {
|
|
44
|
+
issues.push({
|
|
45
|
+
file,
|
|
46
|
+
line: i + 1,
|
|
47
|
+
severity: 'warn',
|
|
48
|
+
rule: 'entget_in_loop',
|
|
49
|
+
message: (0, locale_1.t)('entget_in_loop'),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return issues;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=entget-in-loop.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkIfWithoutElse = checkIfWithoutElse;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const parser_1 = require("@atlisp/parser");
|
|
6
|
+
function checkIfWithoutElse(ast, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const ifNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'if'));
|
|
9
|
+
for (const node of ifNodes) {
|
|
10
|
+
if (!node.children || node.children.length < 3)
|
|
11
|
+
continue;
|
|
12
|
+
if (node.children.length === 3) {
|
|
13
|
+
const parent = node.parent;
|
|
14
|
+
if (parent && parent.type === 'list' && parent.children && parent.children.length > 0 &&
|
|
15
|
+
parent.children[0].type === 'symbol') {
|
|
16
|
+
const parentName = parent.children[0].name;
|
|
17
|
+
if (parentName === 'cond' || parentName === 'if')
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
issues.push({
|
|
21
|
+
file,
|
|
22
|
+
line: node.pos.line,
|
|
23
|
+
severity: 'warn',
|
|
24
|
+
rule: 'if_without_else',
|
|
25
|
+
message: (0, locale_1.t)('if_without_else'),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return issues;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=if-without-else.js.map
|
|
@@ -39,7 +39,7 @@ function checkLongFunctionCall(content, file, maxArgs) {
|
|
|
39
39
|
}
|
|
40
40
|
if (inArg)
|
|
41
41
|
argCount++;
|
|
42
|
-
if (argCount > maxArgs && !m[1].startsWith('(') && !m[1].startsWith('"') && m[1] !== 'if') {
|
|
42
|
+
if (argCount > maxArgs && !m[1].startsWith('(') && !m[1].startsWith('"') && m[1] !== 'if' && m[1] !== 'quote') {
|
|
43
43
|
issues.push({
|
|
44
44
|
file,
|
|
45
45
|
line: i + 1,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkPromiseHandler = checkPromiseHandler;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const parser_1 = require("@atlisp/parser");
|
|
6
|
+
function checkPromiseHandler(ast, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const vlaxNodes = (0, parser_1.astFindAll)(ast, (n) => !!(n.type === 'list' && n.children && n.children.length > 0 &&
|
|
9
|
+
n.children[0].type === 'symbol' && typeof n.children[0].name === 'string' &&
|
|
10
|
+
(n.children[0].name.startsWith('vlax-invoke') || n.children[0].name.startsWith('vlax-invoke-method'))));
|
|
11
|
+
for (const node of vlaxNodes) {
|
|
12
|
+
if (isInQuote(node))
|
|
13
|
+
continue;
|
|
14
|
+
const parent = node.parent;
|
|
15
|
+
if (parent && parent.type === 'list' && parent.children && parent.children.length > 0 &&
|
|
16
|
+
parent.children[0].type === 'symbol' && parent.children[0].name === 'vl-catch-all-apply') {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
let hasCallback = false;
|
|
20
|
+
if (node.children) {
|
|
21
|
+
for (const child of node.children) {
|
|
22
|
+
if (child.type === 'list' && child.children && child.children.length > 0 &&
|
|
23
|
+
child.children[0].type === 'symbol' &&
|
|
24
|
+
(child.children[0].name === 'lambda' || child.children[0].name === 'function')) {
|
|
25
|
+
hasCallback = true;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (!hasCallback) {
|
|
31
|
+
issues.push({
|
|
32
|
+
file,
|
|
33
|
+
line: node.pos.line,
|
|
34
|
+
severity: 'warn',
|
|
35
|
+
rule: 'promise_handler',
|
|
36
|
+
message: (0, locale_1.t)('promise_handler'),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return issues;
|
|
41
|
+
}
|
|
42
|
+
function isInQuote(node) {
|
|
43
|
+
let p = node.parent;
|
|
44
|
+
while (p) {
|
|
45
|
+
if (p.type === 'list' && p.children && p.children.length > 0 &&
|
|
46
|
+
p.children[0].type === 'symbol' && p.children[0].name === 'quote') {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
p = p.parent;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=promise-handler.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkRedundantList = checkRedundantList;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const parser_1 = require("@atlisp/parser");
|
|
6
|
+
function checkRedundantList(ast, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const listNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'list'));
|
|
9
|
+
for (const node of listNodes) {
|
|
10
|
+
if (!node.children || node.children.length === 1) {
|
|
11
|
+
issues.push({
|
|
12
|
+
file,
|
|
13
|
+
line: node.pos.line,
|
|
14
|
+
severity: 'warn',
|
|
15
|
+
rule: 'redundant_list',
|
|
16
|
+
message: (0, locale_1.t)('redundant_list'),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return issues;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=redundant-list.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkStringConcatInLoop = checkStringConcatInLoop;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const parser_1 = require("@atlisp/parser");
|
|
6
|
+
function hasStrcatInBody(body) {
|
|
7
|
+
for (const node of body) {
|
|
8
|
+
for (const n of (0, parser_1.astWalk)(node)) {
|
|
9
|
+
if (n.type === 'list' && n.children && n.children.length > 0 &&
|
|
10
|
+
n.children[0].type === 'symbol' && n.children[0].name === 'strcat') {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
function isLoopForm(name) {
|
|
18
|
+
return name === 'while' || name === 'repeat' || name === 'foreach';
|
|
19
|
+
}
|
|
20
|
+
function checkStringConcatInLoop(content, file) {
|
|
21
|
+
const issues = [];
|
|
22
|
+
const lines = content.split('\n');
|
|
23
|
+
const re = /\((while|repeat|foreach)\s/g;
|
|
24
|
+
for (let i = 0; i < lines.length; i++) {
|
|
25
|
+
let match;
|
|
26
|
+
while ((match = re.exec(lines[i])) !== null) {
|
|
27
|
+
const name = match[1];
|
|
28
|
+
let depth = 0;
|
|
29
|
+
let started = false;
|
|
30
|
+
let startLine = i;
|
|
31
|
+
let foundStrcat = false;
|
|
32
|
+
for (let j = i; j < lines.length; j++) {
|
|
33
|
+
for (let k = 0; k < lines[j].length; k++) {
|
|
34
|
+
const ch = lines[j][k];
|
|
35
|
+
if (ch === '(')
|
|
36
|
+
depth++;
|
|
37
|
+
else if (ch === ')')
|
|
38
|
+
depth--;
|
|
39
|
+
}
|
|
40
|
+
if (!started && depth > 0)
|
|
41
|
+
started = true;
|
|
42
|
+
if (depth === 0 && started)
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
const loopBody = lines.slice(i, i + 20).join('\n');
|
|
46
|
+
if (loopBody.includes('strcat') || loopBody.includes('strcat ')) {
|
|
47
|
+
issues.push({
|
|
48
|
+
file,
|
|
49
|
+
line: i + 1,
|
|
50
|
+
severity: 'warn',
|
|
51
|
+
rule: 'string_concat_loop',
|
|
52
|
+
message: (0, locale_1.t)('string_concat_loop', name),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return issues;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=string-concat-loop.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkUnusedCatchResult = checkUnusedCatchResult;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
function checkUnusedCatchResult(content, file) {
|
|
6
|
+
const issues = [];
|
|
7
|
+
const lines = content.split('\n');
|
|
8
|
+
const catchRe = /\(vl-catch-all-apply\s+'[^)]+\)/g;
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const line = lines[i];
|
|
11
|
+
let match;
|
|
12
|
+
while ((match = catchRe.exec(line)) !== null) {
|
|
13
|
+
const col = match.index;
|
|
14
|
+
const after = line.slice(col + match[0].length).trim();
|
|
15
|
+
if (!after.startsWith(')')) {
|
|
16
|
+
issues.push({
|
|
17
|
+
file,
|
|
18
|
+
line: i + 1,
|
|
19
|
+
severity: 'warn',
|
|
20
|
+
rule: 'unused_catch_result',
|
|
21
|
+
message: (0, locale_1.t)('unused_catch_result'),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return issues;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=unused-catch-result.js.map
|
package/dist/config.js
CHANGED
|
@@ -74,7 +74,6 @@ const DEFAULT_CONFIG = {
|
|
|
74
74
|
unused_let_binding: 'warn',
|
|
75
75
|
recursive_call: 'warn',
|
|
76
76
|
variable_shadow: 'warn',
|
|
77
|
-
redundant_cond: 'warn',
|
|
78
77
|
unused_local_fun: 'warn',
|
|
79
78
|
multiple_setq: 'warn',
|
|
80
79
|
redundant_quotes: 'warn',
|
|
@@ -123,14 +122,22 @@ const DEFAULT_CONFIG = {
|
|
|
123
122
|
type_check: 'off',
|
|
124
123
|
format_indent: 'off',
|
|
125
124
|
redundant_if: 'warn',
|
|
125
|
+
unused_catch_result: 'warn',
|
|
126
|
+
string_concat_loop: 'warn',
|
|
127
|
+
if_without_else: 'warn',
|
|
128
|
+
redundant_list: 'warn',
|
|
129
|
+
entget_in_loop: 'warn',
|
|
130
|
+
promise_handler: 'warn',
|
|
131
|
+
module_cycle: 'warn',
|
|
132
|
+
arg_count_project: 'warn',
|
|
126
133
|
},
|
|
127
134
|
line_length: {
|
|
128
|
-
max:
|
|
135
|
+
max: 200,
|
|
129
136
|
tab_width: 2,
|
|
130
137
|
},
|
|
131
138
|
function_complexity: {
|
|
132
|
-
max_lines:
|
|
133
|
-
max_nesting:
|
|
139
|
+
max_lines: 300,
|
|
140
|
+
max_nesting: 30,
|
|
134
141
|
},
|
|
135
142
|
cl_syntax: {
|
|
136
143
|
keywords: ['&key'],
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatHtml = formatHtml;
|
|
4
|
+
function formatHtml(issues, errorCount, warningCount) {
|
|
5
|
+
const summaryColor = errorCount > 0 ? '#dc3545' : warningCount > 0 ? '#ffc107' : '#198754';
|
|
6
|
+
const ruleGroups = new Map();
|
|
7
|
+
for (const iss of issues) {
|
|
8
|
+
const list = ruleGroups.get(iss.rule) || [];
|
|
9
|
+
list.push(iss);
|
|
10
|
+
ruleGroups.set(iss.rule, list);
|
|
11
|
+
}
|
|
12
|
+
const sortedRules = Array.from(ruleGroups.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
13
|
+
let rows = '';
|
|
14
|
+
for (const iss of issues) {
|
|
15
|
+
const tag = iss.severity === 'error'
|
|
16
|
+
? '<span class="badge bg-danger">ERROR</span>'
|
|
17
|
+
: '<span class="badge bg-warning text-dark">WARN</span>';
|
|
18
|
+
rows += `<tr><td>${tag}</td><td>${iss.rule}</td><td>${iss.file}</td><td>${iss.line || '-'}</td><td>${escHtml(iss.message)}</td></tr>\n`;
|
|
19
|
+
}
|
|
20
|
+
let summaryHtml = '';
|
|
21
|
+
if (errorCount > 0) {
|
|
22
|
+
summaryHtml += `<span class="text-danger fw-bold">${errorCount} error(s)</span> `;
|
|
23
|
+
}
|
|
24
|
+
if (warningCount > 0) {
|
|
25
|
+
summaryHtml += `<span class="text-warning fw-bold">${warningCount} warning(s)</span> `;
|
|
26
|
+
}
|
|
27
|
+
if (errorCount === 0 && warningCount === 0) {
|
|
28
|
+
summaryHtml = '<span class="text-success fw-bold">All files OK</span>';
|
|
29
|
+
}
|
|
30
|
+
return `<!DOCTYPE html>
|
|
31
|
+
<html lang="en">
|
|
32
|
+
<head>
|
|
33
|
+
<meta charset="UTF-8">
|
|
34
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
35
|
+
<title>@atlisp/lint Report</title>
|
|
36
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
37
|
+
<style>
|
|
38
|
+
body { padding: 20px; background: #f8f9fa; }
|
|
39
|
+
.summary-card { background: white; border-radius: 8px; padding: 20px; margin-bottom: 20px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
40
|
+
.summary-count { font-size: 2em; font-weight: bold; margin-right: 20px; }
|
|
41
|
+
table { font-size: 0.9em; }
|
|
42
|
+
.badge { font-size: 0.75em; }
|
|
43
|
+
footer { margin-top: 30px; color: #6c757d; font-size: 0.85em; text-align: center; }
|
|
44
|
+
</style>
|
|
45
|
+
</head>
|
|
46
|
+
<body>
|
|
47
|
+
<div class="container">
|
|
48
|
+
<h1 class="mb-4">@atlisp/lint Report</h1>
|
|
49
|
+
|
|
50
|
+
<div class="summary-card">
|
|
51
|
+
<h5 class="card-title" style="color:${summaryColor}">Summary</h5>
|
|
52
|
+
<p>${summaryHtml}</p>
|
|
53
|
+
<p class="text-muted">${issues.length} total issues across ${ruleGroups.size} rules</p>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
${sortedRules.map(([rule, ruleIssues]) => `
|
|
57
|
+
<div class="summary-card">
|
|
58
|
+
<h5 class="card-title">${escHtml(rule)} <span class="badge bg-secondary">${ruleIssues.length}</span></h5>
|
|
59
|
+
<ul class="list-group list-group-flush">
|
|
60
|
+
${ruleIssues.map(iss => `
|
|
61
|
+
<li class="list-group-item d-flex justify-content-between align-items-start">
|
|
62
|
+
<div>
|
|
63
|
+
<span class="fw-bold">${escHtml(iss.file)}:${iss.line || '-'}</span>
|
|
64
|
+
<br><span class="text-muted">${escHtml(iss.message)}</span>
|
|
65
|
+
</div>
|
|
66
|
+
${iss.severity === 'error' ? '<span class="badge bg-danger rounded-pill">ERROR</span>' : '<span class="badge bg-warning text-dark rounded-pill">WARN</span>'}
|
|
67
|
+
</li>
|
|
68
|
+
`).join('')}
|
|
69
|
+
</ul>
|
|
70
|
+
</div>
|
|
71
|
+
`).join('')}
|
|
72
|
+
|
|
73
|
+
<div class="summary-card">
|
|
74
|
+
<h5>All Issues</h5>
|
|
75
|
+
<div class="table-responsive">
|
|
76
|
+
<table class="table table-striped table-hover">
|
|
77
|
+
<thead><tr><th>Severity</th><th>Rule</th><th>File</th><th>Line</th><th>Message</th></tr></thead>
|
|
78
|
+
<tbody>${rows}</tbody>
|
|
79
|
+
</table>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<footer>Generated by @atlisp/lint on ${new Date().toISOString()}</footer>
|
|
84
|
+
</div>
|
|
85
|
+
</body>
|
|
86
|
+
</html>`;
|
|
87
|
+
}
|
|
88
|
+
function escHtml(s) {
|
|
89
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=formatters-html.js.map
|