@atlisp/lint 0.2.16 → 0.2.18
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 +1 -1
- package/atlisp-lint.default.json +1 -1
- package/dist/checks/arg-count.d.ts +2 -1
- package/dist/checks/arg-count.js +37 -19
- package/dist/checks/builtin-arg-count.d.ts +2 -1
- package/dist/checks/builtin-arg-count.js +3 -2
- package/dist/checks/cond-duplicate.js +5 -6
- package/dist/checks/constant-condition.js +9 -9
- package/dist/checks/dangerous-calls.js +1 -1
- package/dist/checks/dangling-defun.d.ts +0 -7
- package/dist/checks/dangling-defun.js +0 -63
- package/dist/checks/double-not.js +1 -1
- package/dist/checks/empty-branch.js +1 -1
- package/dist/checks/empty-catch.d.ts +2 -1
- package/dist/checks/empty-catch.js +8 -7
- package/dist/checks/function-complexity.js +5 -18
- package/dist/checks/global-naming.js +1 -1
- package/dist/checks/identical-branches.js +3 -3
- package/dist/checks/if-without-else.js +1 -1
- package/dist/checks/misplaced-else.js +2 -2
- package/dist/checks/missing-export.js +3 -3
- package/dist/checks/missing-princ.js +1 -1
- package/dist/checks/multiple-setq.js +1 -1
- package/dist/checks/no-return.d.ts +2 -1
- package/dist/checks/no-return.js +4 -3
- package/dist/checks/nth-usage.js +1 -1
- package/dist/checks/parameter-naming.js +2 -2
- package/dist/checks/quote-vs-function.js +1 -1
- package/dist/checks/recursive-call.js +1 -1
- package/dist/checks/redundant-let.js +1 -1
- package/dist/checks/redundant-nil-else.js +1 -1
- package/dist/checks/redundant-progn.js +1 -1
- package/dist/checks/redundant-setq.js +1 -1
- package/dist/checks/self-compare.js +1 -1
- package/dist/checks/setq-single-arg.js +3 -2
- package/dist/checks/single-arg-and-or.js +1 -1
- package/dist/checks/undeclared-setq.d.ts +2 -1
- package/dist/checks/undeclared-setq.js +6 -5
- package/dist/checks/unused-let.js +3 -3
- package/dist/checks/unused-local-fun.js +2 -2
- package/dist/checks/unused-package-dep.d.ts +0 -1
- package/dist/checks/unused-package-dep.js +6 -93
- package/dist/checks/unused-param.js +2 -2
- package/dist/checks/unused-variable.js +2 -2
- package/dist/checks/variable-shadow.js +3 -3
- package/dist/checks/while-constant.js +1 -1
- package/dist/cli/collector.js +26 -19
- package/dist/config.d.ts +1 -1
- package/dist/config.js +18 -9
- package/dist/fixes/index.d.ts +4 -0
- package/dist/fixes/index.js +843 -140
- package/dist/formatter.d.ts +0 -1
- package/dist/formatter.js +141 -31
- package/dist/index.js +59 -51
- package/dist/project.d.ts +1 -1
- package/dist/project.js +98 -112
- package/dist/runner.d.ts +1 -1
- package/dist/runner.js +17 -8
- package/dist/utils.js +21 -30
- package/dist/validate.js +3 -3
- package/dist/visitor-runner.d.ts +0 -28
- package/dist/visitor-runner.js +3 -809
- package/dist/visitors/equal-nil.d.ts +2 -2
- package/dist/visitors/equal-nil.js +5 -4
- package/dist/visitors/index.d.ts +1 -1
- package/dist/visitors/progn-in-cond.d.ts +2 -2
- package/dist/visitors/progn-in-cond.js +5 -4
- package/dist/visitors/redundant-and-or.d.ts +2 -2
- package/dist/visitors/redundant-and-or.js +3 -3
- package/dist/visitors/redundant-car-cdr.d.ts +2 -2
- package/dist/visitors/redundant-car-cdr.js +3 -3
- package/dist/visitors/register.d.ts +6 -0
- package/dist/visitors/register.js +625 -0
- package/dist/visitors/shared.d.ts +40 -0
- package/dist/visitors/shared.js +129 -0
- package/dist/visitors/types.d.ts +2 -7
- package/dist/visitors/types.js +0 -69
- package/dist/watch.js +33 -12
- package/dist/worker.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -120,7 +120,7 @@ npx @atlisp/lint --format-check
|
|
|
120
120
|
| `lambda_syntax` | warn | 风格 | (function (lambda ...)) 建议缩写 |
|
|
121
121
|
| `comment_style` | warn | 风格 | ; 后缺少空格 |
|
|
122
122
|
| `empty_catch` | warn | 最佳实践 | vl-catch-all-apply 未检查结果 |
|
|
123
|
-
| `nth_usage` |
|
|
123
|
+
| `nth_usage` | info | 风格 | (nth 0 ...) 建议 car |
|
|
124
124
|
| `append_single` | warn | 风格 | (append (list ...)) 建议 cons |
|
|
125
125
|
| `mixed_indent` | warn | 风格 | 缩进混用 tab 和空格 |
|
|
126
126
|
| `long_function_call` | off | 风格 | 单行参数过多 |
|
package/atlisp-lint.default.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Issue } from '../types';
|
|
2
|
-
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function checkArgCount(content: string, file: string, ast?: AstNode): Issue[];
|
|
3
4
|
//# sourceMappingURL=arg-count.d.ts.map
|
package/dist/checks/arg-count.js
CHANGED
|
@@ -3,52 +3,70 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.checkArgCount = checkArgCount;
|
|
4
4
|
const locale_1 = require("../locale");
|
|
5
5
|
const parser_1 = require("@atlisp/parser");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
const shared_1 = require("../visitors/shared");
|
|
7
|
+
function buildCallIndex(ast) {
|
|
8
|
+
const index = new Map();
|
|
9
|
+
function walk(node) {
|
|
10
|
+
if ((0, parser_1.astIsList)(node) && node.children.length > 0 && (0, parser_1.astIsSymbol)(node.children[0])) {
|
|
11
|
+
const name = node.children[0].name;
|
|
12
|
+
if (name && name !== 'defun' && name !== 'defun-q') {
|
|
13
|
+
let list = index.get(name);
|
|
14
|
+
if (!list) {
|
|
15
|
+
list = [];
|
|
16
|
+
index.set(name, list);
|
|
17
|
+
}
|
|
18
|
+
list.push(node);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if ((0, parser_1.astIsList)(node)) {
|
|
22
|
+
for (const c of node.children)
|
|
23
|
+
walk(c);
|
|
12
24
|
}
|
|
13
|
-
cur = cur.parent;
|
|
14
25
|
}
|
|
15
|
-
|
|
26
|
+
walk(ast);
|
|
27
|
+
return index;
|
|
16
28
|
}
|
|
17
|
-
function checkArgCount(content, file) {
|
|
29
|
+
function checkArgCount(content, file, ast) {
|
|
18
30
|
const issues = [];
|
|
19
|
-
|
|
31
|
+
if (!ast)
|
|
32
|
+
ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
20
33
|
const defuns = (0, parser_1.astFindListWithHead)(ast, 'defun');
|
|
34
|
+
const callIndex = buildCallIndex(ast);
|
|
21
35
|
for (const defun of defuns) {
|
|
22
|
-
if (!
|
|
36
|
+
if (!(0, parser_1.astIsList)(defun))
|
|
37
|
+
continue;
|
|
38
|
+
if (defun.children.length < 3)
|
|
23
39
|
continue;
|
|
24
40
|
const nameNode = defun.children[1];
|
|
25
|
-
if (nameNode
|
|
41
|
+
if (!(0, parser_1.astIsSymbol)(nameNode) || !nameNode.name)
|
|
26
42
|
continue;
|
|
27
43
|
const funcName = nameNode.name;
|
|
28
44
|
// Skip C: commands and local functions
|
|
29
45
|
if (funcName.startsWith('c:') || funcName.startsWith('C:') || funcName.includes('/'))
|
|
30
46
|
continue;
|
|
31
47
|
const paramList = defun.children[2];
|
|
32
|
-
if (
|
|
48
|
+
if (!(0, parser_1.astIsList)(paramList))
|
|
33
49
|
continue;
|
|
34
50
|
// Count params before /
|
|
35
51
|
let paramCount = 0;
|
|
36
52
|
for (const p of paramList.children) {
|
|
37
|
-
if (p
|
|
53
|
+
if ((0, parser_1.astIsSymbol)(p) && p.name === '/')
|
|
38
54
|
break;
|
|
39
|
-
if (p
|
|
55
|
+
if ((0, parser_1.astIsSymbol)(p) && p.name && p.name !== 'T' && p.name !== 'nil') {
|
|
40
56
|
paramCount++;
|
|
41
57
|
}
|
|
42
58
|
}
|
|
43
59
|
if (paramCount === 0)
|
|
44
60
|
continue;
|
|
45
|
-
// Find all calls to this function
|
|
46
|
-
const calls =
|
|
61
|
+
// Find all calls to this function via pre-built index
|
|
62
|
+
const calls = callIndex.get(funcName);
|
|
63
|
+
if (!calls)
|
|
64
|
+
continue;
|
|
47
65
|
for (const call of calls) {
|
|
48
|
-
if (!call
|
|
66
|
+
if (!(0, parser_1.astIsList)(call) || call.children.length === 0)
|
|
49
67
|
continue;
|
|
50
68
|
// Skip calls inside quoted or function forms
|
|
51
|
-
if (
|
|
69
|
+
if ((0, shared_1.isInQuote)(call))
|
|
52
70
|
continue;
|
|
53
71
|
const argCount = call.children.length - 1;
|
|
54
72
|
if (argCount !== paramCount) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Issue } from '../types';
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
2
3
|
export declare const BUILTIN_FUNCTIONS: Set<string>;
|
|
3
|
-
export declare function checkBuiltinArgCount(content: string, file: string): Issue[];
|
|
4
|
+
export declare function checkBuiltinArgCount(content: string, file: string, ast?: AstNode): Issue[];
|
|
4
5
|
//# sourceMappingURL=builtin-arg-count.d.ts.map
|
|
@@ -239,9 +239,10 @@ const BUILTIN_ARITY = {
|
|
|
239
239
|
for (const key of Object.keys(BUILTIN_ARITY)) {
|
|
240
240
|
exports.BUILTIN_FUNCTIONS.add(key);
|
|
241
241
|
}
|
|
242
|
-
function checkBuiltinArgCount(content, file) {
|
|
242
|
+
function checkBuiltinArgCount(content, file, ast) {
|
|
243
243
|
const issues = [];
|
|
244
|
-
|
|
244
|
+
if (!ast)
|
|
245
|
+
ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
245
246
|
for (const node of (0, parser_1.astChildren)(ast)) {
|
|
246
247
|
checkNode(node, content, file, issues);
|
|
247
248
|
}
|
|
@@ -5,15 +5,14 @@ exports.checkCondDuplicateAst = checkCondDuplicateAst;
|
|
|
5
5
|
const locale_1 = require("../locale");
|
|
6
6
|
const parser_1 = require("@atlisp/parser");
|
|
7
7
|
function condKey(node) {
|
|
8
|
-
if (node
|
|
8
|
+
if ((0, parser_1.astIsSymbol)(node) || node.type === 'keyword')
|
|
9
9
|
return `s:${node.name}`;
|
|
10
10
|
if (node.type === 'number')
|
|
11
11
|
return `n:${node.value}`;
|
|
12
12
|
if (node.type === 'string')
|
|
13
13
|
return `str:${node.value}`;
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
return `list(${children.map(condKey).join(',')})`;
|
|
14
|
+
if ((0, parser_1.astIsList)(node)) {
|
|
15
|
+
return `list(${node.children.map(condKey).join(',')})`;
|
|
17
16
|
}
|
|
18
17
|
return '';
|
|
19
18
|
}
|
|
@@ -25,12 +24,12 @@ function checkCondDuplicateAst(ast, file) {
|
|
|
25
24
|
const issues = [];
|
|
26
25
|
const condNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'cond'));
|
|
27
26
|
for (const node of condNodes) {
|
|
28
|
-
if (!
|
|
27
|
+
if (!(0, parser_1.astIsList)(node))
|
|
29
28
|
continue;
|
|
30
29
|
const seen = new Map();
|
|
31
30
|
for (let i = 1; i < node.children.length; i++) {
|
|
32
31
|
const clause = node.children[i];
|
|
33
|
-
if (!clause
|
|
32
|
+
if (!(0, parser_1.astIsList)(clause) || clause.children.length < 1)
|
|
34
33
|
continue;
|
|
35
34
|
const condition = clause.children[0];
|
|
36
35
|
const key = condKey(condition);
|
|
@@ -16,10 +16,10 @@ function checkConstantConditionAst(ast, file) {
|
|
|
16
16
|
for (const form of CONDITION_FORMS) {
|
|
17
17
|
const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, form));
|
|
18
18
|
for (const node of nodes) {
|
|
19
|
-
if (!node
|
|
19
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
|
|
20
20
|
continue;
|
|
21
21
|
const cond = node.children[1];
|
|
22
|
-
if (cond
|
|
22
|
+
if ((0, parser_1.astIsSymbol)(cond) && cond.name && CONSTANTS.has(cond.name)) {
|
|
23
23
|
issues.push({
|
|
24
24
|
file,
|
|
25
25
|
line: cond.pos.line,
|
|
@@ -33,14 +33,14 @@ function checkConstantConditionAst(ast, file) {
|
|
|
33
33
|
// Check (cond (T ...))
|
|
34
34
|
const condNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'cond'));
|
|
35
35
|
for (const condNode of condNodes) {
|
|
36
|
-
if (!
|
|
36
|
+
if (!(0, parser_1.astIsList)(condNode))
|
|
37
37
|
continue;
|
|
38
38
|
for (let i = 1; i < condNode.children.length; i++) {
|
|
39
39
|
const clause = condNode.children[i];
|
|
40
|
-
if (
|
|
40
|
+
if (!(0, parser_1.astIsList)(clause) || clause.children.length === 0)
|
|
41
41
|
continue;
|
|
42
42
|
const condTest = clause.children[0];
|
|
43
|
-
if (condTest
|
|
43
|
+
if ((0, parser_1.astIsSymbol)(condTest) && condTest.name && CONSTANTS.has(condTest.name)) {
|
|
44
44
|
// Skip last clause with T (only when there are multiple clauses — else/default pattern)
|
|
45
45
|
if (condTest.name === 'T' && i === condNode.children.length - 1 && i > 1)
|
|
46
46
|
continue;
|
|
@@ -57,10 +57,10 @@ function checkConstantConditionAst(ast, file) {
|
|
|
57
57
|
// Check (or T ...) — first arg is T, always short-circuits to T
|
|
58
58
|
const orNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'or'));
|
|
59
59
|
for (const node of orNodes) {
|
|
60
|
-
if (!node
|
|
60
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
|
|
61
61
|
continue;
|
|
62
62
|
const first = node.children[1];
|
|
63
|
-
if (first
|
|
63
|
+
if ((0, parser_1.astIsSymbol)(first) && first.name === 'T') {
|
|
64
64
|
issues.push({
|
|
65
65
|
file,
|
|
66
66
|
line: first.pos.line,
|
|
@@ -73,10 +73,10 @@ function checkConstantConditionAst(ast, file) {
|
|
|
73
73
|
// Check (and nil ...) — first arg is nil, always short-circuits to nil
|
|
74
74
|
const andNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'and'));
|
|
75
75
|
for (const node of andNodes) {
|
|
76
|
-
if (!node
|
|
76
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
|
|
77
77
|
continue;
|
|
78
78
|
const first = node.children[1];
|
|
79
|
-
if (first
|
|
79
|
+
if ((0, parser_1.astIsSymbol)(first) && first.name === 'nil') {
|
|
80
80
|
issues.push({
|
|
81
81
|
file,
|
|
82
82
|
line: first.pos.line,
|
|
@@ -52,7 +52,7 @@ function checkDangerousCallsAst(ast, file, config) {
|
|
|
52
52
|
for (const call of commandCalls) {
|
|
53
53
|
if (isInQuote(call))
|
|
54
54
|
continue;
|
|
55
|
-
if (!call
|
|
55
|
+
if (!(0, parser_1.astIsList)(call) || call.children.length < 2)
|
|
56
56
|
continue;
|
|
57
57
|
const firstArg = call.children[1];
|
|
58
58
|
if (firstArg.type === 'string' && firstArg.value === 'shell') {
|
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import { Issue } from '../types';
|
|
2
|
-
export declare function checkDanglingDefun(file: string, allDefuns: Map<string, {
|
|
3
|
-
file: string;
|
|
4
|
-
line: number;
|
|
5
|
-
}[]>, allReferences: Map<string, {
|
|
6
|
-
file: string;
|
|
7
|
-
line: number;
|
|
8
|
-
}[]>): Issue[];
|
|
9
2
|
export declare function checkDanglingDefunFromDefs(localDefuns: {
|
|
10
3
|
name: string;
|
|
11
4
|
line: number;
|
|
@@ -1,70 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.checkDanglingDefun = checkDanglingDefun;
|
|
37
3
|
exports.checkDanglingDefunFromDefs = checkDanglingDefunFromDefs;
|
|
38
4
|
const locale_1 = require("../locale");
|
|
39
|
-
const parser_1 = require("@atlisp/parser");
|
|
40
|
-
const fs = __importStar(require("fs"));
|
|
41
|
-
function checkDanglingDefun(file, allDefuns, allReferences) {
|
|
42
|
-
const issues = [];
|
|
43
|
-
const content = fs.readFileSync(file, 'utf-8');
|
|
44
|
-
const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
45
|
-
const localDefuns = [];
|
|
46
|
-
const defunNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
|
|
47
|
-
for (const node of defunNodes) {
|
|
48
|
-
if (node.children && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
|
|
49
|
-
localDefuns.push({ name: node.children[1].name, line: node.pos.line });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
for (const defun of localDefuns) {
|
|
53
|
-
const refs = allReferences.get(defun.name) || [];
|
|
54
|
-
const calledElsewhere = refs.some(r => r.file !== file);
|
|
55
|
-
const calledLocally = refs.some(r => r.file === file);
|
|
56
|
-
if (!calledLocally && !calledElsewhere) {
|
|
57
|
-
issues.push({
|
|
58
|
-
file,
|
|
59
|
-
line: defun.line,
|
|
60
|
-
severity: 'warn',
|
|
61
|
-
rule: 'dangling_defun',
|
|
62
|
-
message: (0, locale_1.t)('dangling_defun', defun.name),
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return issues;
|
|
67
|
-
}
|
|
68
5
|
function checkDanglingDefunFromDefs(localDefuns, allReferences, file) {
|
|
69
6
|
const issues = [];
|
|
70
7
|
for (const defun of localDefuns) {
|
|
@@ -12,7 +12,7 @@ function checkDoubleNotAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const notNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'not'));
|
|
14
14
|
for (const node of notNodes) {
|
|
15
|
-
if (!node
|
|
15
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
|
|
16
16
|
continue;
|
|
17
17
|
const arg = node.children[1];
|
|
18
18
|
if (!(0, parser_1.astIsList)(arg, 'not'))
|
|
@@ -15,7 +15,7 @@ function checkEmptyBranchAst(ast, file) {
|
|
|
15
15
|
for (const form of BRANCH_FORMS) {
|
|
16
16
|
const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, form));
|
|
17
17
|
for (const node of nodes) {
|
|
18
|
-
if (!
|
|
18
|
+
if (!(0, parser_1.astIsList)(node))
|
|
19
19
|
continue;
|
|
20
20
|
// (if cond) — no then or else, only 2 items (form + cond)
|
|
21
21
|
if (node.children.length < 3) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Issue } from '../types';
|
|
2
|
-
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function checkEmptyCatch(content: string, file: string, ast?: AstNode): Issue[];
|
|
3
4
|
//# sourceMappingURL=empty-catch.d.ts.map
|
|
@@ -3,9 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.checkEmptyCatch = checkEmptyCatch;
|
|
4
4
|
const locale_1 = require("../locale");
|
|
5
5
|
const parser_1 = require("@atlisp/parser");
|
|
6
|
-
function checkEmptyCatch(content, file) {
|
|
6
|
+
function checkEmptyCatch(content, file, ast) {
|
|
7
7
|
const issues = [];
|
|
8
|
-
|
|
8
|
+
if (!ast)
|
|
9
|
+
ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
9
10
|
const catchNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'vl-catch-all-apply'));
|
|
10
11
|
for (const node of catchNodes) {
|
|
11
12
|
if (isInQuote(node))
|
|
@@ -49,25 +50,25 @@ function isSetqStored(node) {
|
|
|
49
50
|
const parent = node.parent;
|
|
50
51
|
if (!parent || !(0, parser_1.astIsList)(parent, 'setq'))
|
|
51
52
|
return false;
|
|
52
|
-
if (
|
|
53
|
+
if (parent.children.length < 3)
|
|
53
54
|
return false;
|
|
54
55
|
const idx = parent.children.indexOf(node);
|
|
55
56
|
return idx >= 2 && idx % 2 === 0;
|
|
56
57
|
}
|
|
57
58
|
function getSetqVarName(node) {
|
|
58
59
|
const parent = node.parent;
|
|
59
|
-
if (!parent || !
|
|
60
|
+
if (!parent || !(0, parser_1.astIsList)(parent))
|
|
60
61
|
return null;
|
|
61
62
|
const idx = parent.children.indexOf(node);
|
|
62
63
|
const varNode = parent.children[idx - 1];
|
|
63
|
-
if (varNode && varNode
|
|
64
|
+
if (varNode && (0, parser_1.astIsSymbol)(varNode) && varNode.name) {
|
|
64
65
|
return varNode.name;
|
|
65
66
|
}
|
|
66
67
|
return null;
|
|
67
68
|
}
|
|
68
69
|
function hasErrorCheckInSiblings(node, varName) {
|
|
69
70
|
const parent = node.parent;
|
|
70
|
-
if (!parent || !
|
|
71
|
+
if (!parent || !(0, parser_1.astIsList)(parent))
|
|
71
72
|
return false;
|
|
72
73
|
const idx = parent.children.indexOf(node);
|
|
73
74
|
for (let i = idx + 1; i < parent.children.length; i++) {
|
|
@@ -76,7 +77,7 @@ function hasErrorCheckInSiblings(node, varName) {
|
|
|
76
77
|
}
|
|
77
78
|
if ((0, parser_1.astIsList)(parent, 'setq')) {
|
|
78
79
|
const grandparent = parent.parent;
|
|
79
|
-
if (grandparent &&
|
|
80
|
+
if (grandparent && (0, parser_1.astIsList)(grandparent)) {
|
|
80
81
|
const parentIdx = grandparent.children.indexOf(parent);
|
|
81
82
|
for (let i = parentIdx + 1; i < grandparent.children.length; i++) {
|
|
82
83
|
if (containsErrorCheck(grandparent.children[i], varName))
|
|
@@ -4,6 +4,7 @@ exports.checkFunctionComplexity = checkFunctionComplexity;
|
|
|
4
4
|
exports.checkFunctionComplexityAst = checkFunctionComplexityAst;
|
|
5
5
|
const locale_1 = require("../locale");
|
|
6
6
|
const parser_1 = require("@atlisp/parser");
|
|
7
|
+
const shared_1 = require("../visitors/shared");
|
|
7
8
|
function checkFunctionComplexity(content, file, maxLines, maxNesting) {
|
|
8
9
|
const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
9
10
|
return checkFunctionComplexityAst(ast, file, maxLines, maxNesting);
|
|
@@ -13,12 +14,12 @@ function checkFunctionComplexityAst(ast, file, maxLines, maxNesting) {
|
|
|
13
14
|
// Check defun forms
|
|
14
15
|
const defuns = (0, parser_1.astFindListWithHead)(ast, 'defun');
|
|
15
16
|
for (const defun of defuns) {
|
|
16
|
-
if (!defun
|
|
17
|
+
if (!(0, parser_1.astIsList)(defun) || !defun.end)
|
|
17
18
|
continue;
|
|
18
19
|
if (defun.children.length < 3)
|
|
19
20
|
continue;
|
|
20
21
|
const nameNode = defun.children[1];
|
|
21
|
-
const name = nameNode
|
|
22
|
+
const name = (0, parser_1.astIsSymbol)(nameNode) && nameNode.name ? nameNode.name : '<unknown>';
|
|
22
23
|
const bodyLines = (defun.end ? defun.end.line : defun.pos.line) - defun.pos.line + 1;
|
|
23
24
|
const depth = computeNestingDepth(defun, 1);
|
|
24
25
|
if (bodyLines > maxLines) {
|
|
@@ -43,10 +44,10 @@ function checkFunctionComplexityAst(ast, file, maxLines, maxNesting) {
|
|
|
43
44
|
// Check lambda forms: (lambda (args) body...)
|
|
44
45
|
const lambdas = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'lambda'));
|
|
45
46
|
for (const lam of lambdas) {
|
|
46
|
-
if (!lam
|
|
47
|
+
if (!(0, parser_1.astIsList)(lam) || !lam.end)
|
|
47
48
|
continue;
|
|
48
49
|
// Skip quoted lambdas: (quote (lambda ...)) — data, not code
|
|
49
|
-
if (
|
|
50
|
+
if ((0, shared_1.isInQuote)(lam, false))
|
|
50
51
|
continue;
|
|
51
52
|
const bodyLines = (lam.end ? lam.end.line : lam.pos.line) - lam.pos.line + 1;
|
|
52
53
|
const depth = computeNestingDepth(lam, 1);
|
|
@@ -71,20 +72,6 @@ function checkFunctionComplexityAst(ast, file, maxLines, maxNesting) {
|
|
|
71
72
|
}
|
|
72
73
|
return issues;
|
|
73
74
|
}
|
|
74
|
-
function isInsideQuote(node) {
|
|
75
|
-
let cur = node.parent;
|
|
76
|
-
while (cur) {
|
|
77
|
-
if (cur.type === 'list' &&
|
|
78
|
-
cur.children &&
|
|
79
|
-
cur.children.length > 0 &&
|
|
80
|
-
cur.children[0].type === 'symbol' &&
|
|
81
|
-
cur.children[0].name === 'quote') {
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
cur = cur.parent;
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
75
|
function computeNestingDepth(node, depth) {
|
|
89
76
|
if (node.type !== 'list' || !node.children)
|
|
90
77
|
return depth;
|
|
@@ -28,7 +28,7 @@ function checkGlobalNaming(content, file) {
|
|
|
28
28
|
const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
29
29
|
const setqForms = (0, parser_1.astFindListWithHead)(ast, 'setq');
|
|
30
30
|
for (const form of setqForms) {
|
|
31
|
-
if (!form
|
|
31
|
+
if (!(0, parser_1.astIsList)(form) || form.children.length < 2)
|
|
32
32
|
continue;
|
|
33
33
|
// Skip setq inside defun/lambda/let — those are local
|
|
34
34
|
if (isInsideLocalScope(form))
|
|
@@ -14,8 +14,8 @@ function astEqual(a, b) {
|
|
|
14
14
|
if (a.type === 'string')
|
|
15
15
|
return a.value === b.value;
|
|
16
16
|
if (a.type === 'list') {
|
|
17
|
-
const aChildren = a.children
|
|
18
|
-
const bChildren = b.children
|
|
17
|
+
const aChildren = a.children;
|
|
18
|
+
const bChildren = b.children;
|
|
19
19
|
if (aChildren.length !== bChildren.length)
|
|
20
20
|
return false;
|
|
21
21
|
for (let i = 0; i < aChildren.length; i++) {
|
|
@@ -35,7 +35,7 @@ function checkIdenticalBranchesAst(ast, file) {
|
|
|
35
35
|
// Check if statements: (if cond then-branch else-branch)
|
|
36
36
|
const ifNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'if'));
|
|
37
37
|
for (const node of ifNodes) {
|
|
38
|
-
if (!node
|
|
38
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 4)
|
|
39
39
|
continue;
|
|
40
40
|
const thenBranch = node.children[2];
|
|
41
41
|
const elseBranch = node.children[3];
|
|
@@ -7,7 +7,7 @@ function checkIfWithoutElse(ast, file) {
|
|
|
7
7
|
const issues = [];
|
|
8
8
|
const ifNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'if'));
|
|
9
9
|
for (const node of ifNodes) {
|
|
10
|
-
if (!node
|
|
10
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 3)
|
|
11
11
|
continue;
|
|
12
12
|
if (node.children.length === 3) {
|
|
13
13
|
const parent = node.parent;
|
|
@@ -12,12 +12,12 @@ function checkMisplacedElseAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const ifNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'if'));
|
|
14
14
|
for (const node of ifNodes) {
|
|
15
|
-
if (!node
|
|
15
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length !== 4)
|
|
16
16
|
continue;
|
|
17
17
|
const cond = node.children[1];
|
|
18
18
|
if (!(0, parser_1.astIsList)(cond, 'not'))
|
|
19
19
|
continue;
|
|
20
|
-
const innerName = cond.children && cond.children[1]
|
|
20
|
+
const innerName = cond.children[1] && (0, parser_1.astIsSymbol)(cond.children[1]) ? cond.children[1].name : '';
|
|
21
21
|
issues.push({
|
|
22
22
|
file,
|
|
23
23
|
line: node.pos.line,
|
|
@@ -48,13 +48,13 @@ function checkMissingExport(file, pkgDir, _allDefuns) {
|
|
|
48
48
|
const modulesNode = (0, parser_1.astFindAll)(pkgAst, n => (0, parser_1.astIsList)(n, 'setq'));
|
|
49
49
|
const registered = new Set();
|
|
50
50
|
for (const node of modulesNode) {
|
|
51
|
-
if (!
|
|
51
|
+
if (!(0, parser_1.astIsList)(node))
|
|
52
52
|
continue;
|
|
53
53
|
for (let i = 1; i < node.children.length - 1; i += 2) {
|
|
54
54
|
const sym = node.children[i];
|
|
55
55
|
if ((0, parser_1.astIsSymbol)(sym) && sym.name === '@::*modules*') {
|
|
56
56
|
const val = node.children[i + 1];
|
|
57
|
-
if (
|
|
57
|
+
if ((0, parser_1.astIsList)(val)) {
|
|
58
58
|
for (const item of val.children) {
|
|
59
59
|
if ((0, parser_1.astIsSymbol)(item)) {
|
|
60
60
|
registered.add(item.name);
|
|
@@ -71,7 +71,7 @@ function checkMissingExport(file, pkgDir, _allDefuns) {
|
|
|
71
71
|
const ast = (0, parser_1.parseAst)(fileContent);
|
|
72
72
|
const defunNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
|
|
73
73
|
for (const node of defunNodes) {
|
|
74
|
-
if (!node
|
|
74
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
|
|
75
75
|
continue;
|
|
76
76
|
const nameNode = node.children[1];
|
|
77
77
|
if (!(0, parser_1.astIsSymbol)(nameNode))
|
|
@@ -14,7 +14,7 @@ function checkMissingPrinc(content, file) {
|
|
|
14
14
|
}
|
|
15
15
|
const defunNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
|
|
16
16
|
for (const node of defunNodes) {
|
|
17
|
-
if (!node
|
|
17
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 3)
|
|
18
18
|
continue;
|
|
19
19
|
const nameNode = node.children[1];
|
|
20
20
|
if (!(0, parser_1.astIsSymbol)(nameNode))
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Issue } from '../types';
|
|
2
|
-
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function checkNoReturn(content: string, file: string, ast?: AstNode): Issue[];
|
|
3
4
|
//# sourceMappingURL=no-return.d.ts.map
|
package/dist/checks/no-return.js
CHANGED
|
@@ -3,12 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.checkNoReturn = checkNoReturn;
|
|
4
4
|
const locale_1 = require("../locale");
|
|
5
5
|
const parser_1 = require("@atlisp/parser");
|
|
6
|
-
function checkNoReturn(content, file) {
|
|
6
|
+
function checkNoReturn(content, file, ast) {
|
|
7
7
|
const issues = [];
|
|
8
|
-
|
|
8
|
+
if (!ast)
|
|
9
|
+
ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
9
10
|
const defuns = (0, parser_1.astFindListWithHead)(ast, 'defun');
|
|
10
11
|
for (const defun of defuns) {
|
|
11
|
-
if (!defun
|
|
12
|
+
if (!(0, parser_1.astIsList)(defun) || defun.children.length < 3)
|
|
12
13
|
continue;
|
|
13
14
|
const nameNode = defun.children[1];
|
|
14
15
|
if (nameNode.type !== 'symbol' || !nameNode.name)
|
package/dist/checks/nth-usage.js
CHANGED
|
@@ -18,7 +18,7 @@ function checkNthUsage(content, file) {
|
|
|
18
18
|
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
19
19
|
const m = stripped.match(NTH_RE);
|
|
20
20
|
if (m && REPLACEMENTS[m[1]]) {
|
|
21
|
-
issues.push({ file, line: i + 1, severity: '
|
|
21
|
+
issues.push({ file, line: i + 1, severity: 'info', rule: 'nth_usage', message: (0, locale_1.t)('nth_usage') });
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
return issues;
|
|
@@ -12,13 +12,13 @@ function checkParameterNamingAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const defuns = (0, parser_1.astFindListWithHead)(ast, 'defun');
|
|
14
14
|
for (const defun of defuns) {
|
|
15
|
-
if (!defun
|
|
15
|
+
if (!(0, parser_1.astIsList)(defun) || defun.children.length < 3)
|
|
16
16
|
continue;
|
|
17
17
|
const nameNode = defun.children[1];
|
|
18
18
|
const name = nameNode.type === 'symbol' && nameNode.name ? nameNode.name : '<unknown>';
|
|
19
19
|
// Third child should be the parameter list
|
|
20
20
|
const paramList = defun.children[2];
|
|
21
|
-
if (
|
|
21
|
+
if (!(0, parser_1.astIsList)(paramList))
|
|
22
22
|
continue;
|
|
23
23
|
for (const p of paramList.children) {
|
|
24
24
|
if (p.type !== 'symbol' || !p.name)
|
|
@@ -31,7 +31,7 @@ function checkQuoteVsFunctionAst(ast, file) {
|
|
|
31
31
|
});
|
|
32
32
|
for (const node of quotedLambdas) {
|
|
33
33
|
const parent = node.parent;
|
|
34
|
-
if (!parent || !
|
|
34
|
+
if (!parent || !(0, parser_1.astIsList)(parent))
|
|
35
35
|
continue;
|
|
36
36
|
const parentHead = parent.children[0];
|
|
37
37
|
const parentName = (parentHead.type === 'symbol' && parentHead.name) || '';
|
|
@@ -14,7 +14,7 @@ function checkRecursiveCallAst(ast, file) {
|
|
|
14
14
|
const issues = [];
|
|
15
15
|
const defuns = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun'));
|
|
16
16
|
for (const defun of defuns) {
|
|
17
|
-
if (!defun
|
|
17
|
+
if (!(0, parser_1.astIsList)(defun) || defun.children.length < 3)
|
|
18
18
|
continue;
|
|
19
19
|
const nameNode = defun.children[1];
|
|
20
20
|
const funcName = nameNode.type === 'symbol' && nameNode.name ? nameNode.name : '';
|