@atlisp/lint 0.2.16 → 0.2.17
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 +0 -6
- 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
|
@@ -12,7 +12,7 @@ function checkRedundantLetAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const letNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'let'));
|
|
14
14
|
for (const node of letNodes) {
|
|
15
|
-
if (!node
|
|
15
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
|
|
16
16
|
continue;
|
|
17
17
|
const bindings = node.children[1];
|
|
18
18
|
// (let () ...) — binding list exists but is empty
|
|
@@ -13,7 +13,7 @@ function checkRedundantNilElseAst(ast, file) {
|
|
|
13
13
|
// Find (if cond then nil) — else branch is nil which is the default
|
|
14
14
|
const ifNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'if'));
|
|
15
15
|
for (const node of ifNodes) {
|
|
16
|
-
if (!node
|
|
16
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length !== 4)
|
|
17
17
|
continue;
|
|
18
18
|
// (if cond then nil) — 4 children: if, cond, then, nil-else
|
|
19
19
|
const elseBranch = node.children[3];
|
|
@@ -12,7 +12,7 @@ function checkRedundantPrognAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const prognNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'progn'));
|
|
14
14
|
for (const node of prognNodes) {
|
|
15
|
-
if (!
|
|
15
|
+
if (!(0, parser_1.astIsList)(node))
|
|
16
16
|
continue;
|
|
17
17
|
// count forms after 'progn'
|
|
18
18
|
const bodyCount = node.children.length - 1;
|
|
@@ -12,7 +12,7 @@ function checkRedundantSetqAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const setqNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'setq'));
|
|
14
14
|
for (const node of setqNodes) {
|
|
15
|
-
if (!
|
|
15
|
+
if (!(0, parser_1.astIsList)(node))
|
|
16
16
|
continue;
|
|
17
17
|
// (setq x x) — variable is set to itself
|
|
18
18
|
for (let i = 1; i < node.children.length - 1; i += 2) {
|
|
@@ -14,7 +14,7 @@ function checkSelfCompareAst(ast, file) {
|
|
|
14
14
|
for (const fn of COMPARE_FUNCTIONS) {
|
|
15
15
|
const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, fn));
|
|
16
16
|
for (const node of nodes) {
|
|
17
|
-
if (!
|
|
17
|
+
if (!(0, parser_1.astIsList)(node))
|
|
18
18
|
continue;
|
|
19
19
|
// (= x x) — 3 children: '=', x, x
|
|
20
20
|
if (node.children.length === 3) {
|
|
@@ -12,16 +12,17 @@ function checkSetqSingleArgAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'setq'));
|
|
14
14
|
for (const node of nodes) {
|
|
15
|
-
if (!
|
|
15
|
+
if (!(0, parser_1.astIsList)(node))
|
|
16
16
|
continue;
|
|
17
17
|
// (setq x) — 2 children: 'setq', x — missing value
|
|
18
18
|
if (node.children.length === 2) {
|
|
19
|
+
const varNode = node.children[1];
|
|
19
20
|
issues.push({
|
|
20
21
|
file,
|
|
21
22
|
line: node.pos.line,
|
|
22
23
|
severity: 'warn',
|
|
23
24
|
rule: 'setq_single_arg',
|
|
24
|
-
message: (0, locale_1.t)('setq_single_arg',
|
|
25
|
+
message: (0, locale_1.t)('setq_single_arg', varNode.type === 'symbol' && varNode.name ? varNode.name : ''),
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -13,7 +13,7 @@ function checkSingleArgAndOrAst(ast, file) {
|
|
|
13
13
|
for (const form of ['and', 'or']) {
|
|
14
14
|
const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, form));
|
|
15
15
|
for (const node of nodes) {
|
|
16
|
-
if (!
|
|
16
|
+
if (!(0, parser_1.astIsList)(node))
|
|
17
17
|
continue;
|
|
18
18
|
// (and x) — 2 children: 'and', x
|
|
19
19
|
if (node.children.length === 2) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Issue } from '../types';
|
|
2
|
-
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function checkUndeclaredSetq(content: string, file: string, ast?: AstNode): Issue[];
|
|
3
4
|
//# sourceMappingURL=undeclared-setq.d.ts.map
|
|
@@ -6,10 +6,10 @@ const parser_1 = require("@atlisp/parser");
|
|
|
6
6
|
/** Extract declared variable names from a defun's parameter list (both sides of /) */
|
|
7
7
|
function getDeclaredVars(defun) {
|
|
8
8
|
const vars = new Set();
|
|
9
|
-
if (!defun
|
|
9
|
+
if (!(0, parser_1.astIsList)(defun) || defun.children.length < 3)
|
|
10
10
|
return vars;
|
|
11
11
|
const paramList = defun.children[2];
|
|
12
|
-
if (
|
|
12
|
+
if (!(0, parser_1.astIsList)(paramList))
|
|
13
13
|
return vars;
|
|
14
14
|
for (const p of paramList.children) {
|
|
15
15
|
if (p.type === 'symbol' && p.name && p.name !== '/' && p.name !== 'T' && p.name !== 'nil') {
|
|
@@ -40,12 +40,13 @@ function findEnclosingDefun(node) {
|
|
|
40
40
|
}
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
|
-
function checkUndeclaredSetq(content, file) {
|
|
43
|
+
function checkUndeclaredSetq(content, file, ast) {
|
|
44
44
|
const issues = [];
|
|
45
|
-
|
|
45
|
+
if (!ast)
|
|
46
|
+
ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
46
47
|
const setqForms = (0, parser_1.astFindListWithHead)(ast, 'setq');
|
|
47
48
|
for (const setq of setqForms) {
|
|
48
|
-
if (!setq
|
|
49
|
+
if (!(0, parser_1.astIsList)(setq) || setq.children.length < 2)
|
|
49
50
|
continue;
|
|
50
51
|
// Skip top-level setq (not inside any defun)
|
|
51
52
|
const defun = findEnclosingDefun(setq);
|
|
@@ -12,15 +12,15 @@ function checkUnusedLetBindingAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const letNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'let'));
|
|
14
14
|
for (const node of letNodes) {
|
|
15
|
-
if (!node
|
|
15
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
|
|
16
16
|
continue;
|
|
17
17
|
const bindingList = node.children[1];
|
|
18
|
-
if (
|
|
18
|
+
if (!(0, parser_1.astIsList)(bindingList))
|
|
19
19
|
continue;
|
|
20
20
|
// Collect let-bound variables
|
|
21
21
|
const bindings = [];
|
|
22
22
|
for (const binding of bindingList.children) {
|
|
23
|
-
if (
|
|
23
|
+
if (!(0, parser_1.astIsList)(binding) || binding.children.length === 0)
|
|
24
24
|
continue;
|
|
25
25
|
const varNode = binding.children[0];
|
|
26
26
|
if (varNode.type !== 'symbol' || !varNode.name)
|
|
@@ -12,10 +12,10 @@ function checkUnusedLocalFunAst(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 paramList = defun.children[2];
|
|
18
|
-
if (
|
|
18
|
+
if (!(0, parser_1.astIsList)(paramList))
|
|
19
19
|
continue;
|
|
20
20
|
// Collect local variables after `/` in param list
|
|
21
21
|
// (defun foo (x / y z) ...) → y, z are local vars
|
|
@@ -1,96 +1,8 @@
|
|
|
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.checkUnusedPackageDep = checkUnusedPackageDep;
|
|
37
3
|
exports.checkUnusedPackageDepFromContent = checkUnusedPackageDepFromContent;
|
|
38
4
|
const locale_1 = require("../locale");
|
|
39
5
|
const parser_1 = require("@atlisp/parser");
|
|
40
|
-
const fs = __importStar(require("fs"));
|
|
41
|
-
function checkUnusedPackageDep(file) {
|
|
42
|
-
const issues = [];
|
|
43
|
-
const content = fs.readFileSync(file, 'utf-8');
|
|
44
|
-
const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
45
|
-
const inPackageNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'in-package'));
|
|
46
|
-
if (inPackageNodes.length === 0)
|
|
47
|
-
return issues;
|
|
48
|
-
const allPackages = new Set();
|
|
49
|
-
for (const node of inPackageNodes) {
|
|
50
|
-
if (node.children && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
|
|
51
|
-
allPackages.add(node.children[1].name);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const usedSymbols = new Set();
|
|
55
|
-
const allLists = (0, parser_1.astFindAll)(ast, n => n.type === 'list');
|
|
56
|
-
for (const node of allLists) {
|
|
57
|
-
if (!node.children || node.children.length === 0)
|
|
58
|
-
continue;
|
|
59
|
-
const car = node.children[0];
|
|
60
|
-
if (car.type === 'symbol' && car.name) {
|
|
61
|
-
const parts = car.name.split(':');
|
|
62
|
-
if (parts.length === 2 && parts[0] && parts[1]) {
|
|
63
|
-
usedSymbols.add(parts[0]);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
for (let i = 1; i < node.children.length; i++) {
|
|
67
|
-
const child = node.children[i];
|
|
68
|
-
if (child.type === 'symbol' && child.name) {
|
|
69
|
-
const parts = child.name.split(':');
|
|
70
|
-
if (parts.length === 2 && parts[0] && parts[1]) {
|
|
71
|
-
usedSymbols.add(parts[0]);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const mainPackage = inPackageNodes.length > 0 && inPackageNodes[0].children && inPackageNodes[0].children[1]
|
|
77
|
-
? inPackageNodes[0].children[1].name || ''
|
|
78
|
-
: '';
|
|
79
|
-
allPackages.delete(mainPackage);
|
|
80
|
-
for (const pkg of allPackages) {
|
|
81
|
-
if (!usedSymbols.has(pkg)) {
|
|
82
|
-
const node = inPackageNodes.find(n => n.children && n.children.length >= 2 && (0, parser_1.astIsSymbol)(n.children[1]) && n.children[1].name === pkg);
|
|
83
|
-
issues.push({
|
|
84
|
-
file,
|
|
85
|
-
line: node ? node.pos.line : 1,
|
|
86
|
-
severity: 'warn',
|
|
87
|
-
rule: 'unused_package_dep',
|
|
88
|
-
message: (0, locale_1.t)('unused_package_dep', pkg),
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return issues;
|
|
93
|
-
}
|
|
94
6
|
function checkUnusedPackageDepFromContent(file, content) {
|
|
95
7
|
const issues = [];
|
|
96
8
|
const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
@@ -99,14 +11,14 @@ function checkUnusedPackageDepFromContent(file, content) {
|
|
|
99
11
|
return issues;
|
|
100
12
|
const allPackages = new Set();
|
|
101
13
|
for (const node of inPackageNodes) {
|
|
102
|
-
if (node
|
|
14
|
+
if ((0, parser_1.astIsList)(node) && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
|
|
103
15
|
allPackages.add(node.children[1].name);
|
|
104
16
|
}
|
|
105
17
|
}
|
|
106
18
|
const usedSymbols = new Set();
|
|
107
19
|
const allLists = (0, parser_1.astFindAll)(ast, n => n.type === 'list');
|
|
108
20
|
for (const node of allLists) {
|
|
109
|
-
if (!node
|
|
21
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length === 0)
|
|
110
22
|
continue;
|
|
111
23
|
const car = node.children[0];
|
|
112
24
|
if (car.type === 'symbol' && car.name) {
|
|
@@ -125,13 +37,14 @@ function checkUnusedPackageDepFromContent(file, content) {
|
|
|
125
37
|
}
|
|
126
38
|
}
|
|
127
39
|
}
|
|
128
|
-
const
|
|
129
|
-
|
|
40
|
+
const mainPkgNode = inPackageNodes.length > 0 && (0, parser_1.astIsList)(inPackageNodes[0]) ? inPackageNodes[0] : null;
|
|
41
|
+
const mainPackage = mainPkgNode && mainPkgNode.children.length >= 2 && (0, parser_1.astIsSymbol)(mainPkgNode.children[1])
|
|
42
|
+
? mainPkgNode.children[1].name || ''
|
|
130
43
|
: '';
|
|
131
44
|
allPackages.delete(mainPackage);
|
|
132
45
|
for (const pkg of allPackages) {
|
|
133
46
|
if (!usedSymbols.has(pkg)) {
|
|
134
|
-
const node = inPackageNodes.find(n => n
|
|
47
|
+
const node = inPackageNodes.find(n => (0, parser_1.astIsList)(n) && n.children.length >= 2 && (0, parser_1.astIsSymbol)(n.children[1]) && n.children[1].name === pkg);
|
|
135
48
|
issues.push({
|
|
136
49
|
file,
|
|
137
50
|
line: node ? node.pos.line : 1,
|
|
@@ -12,14 +12,14 @@ function checkUnusedParameterAst(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 funcName = nameNode.type === 'symbol' && nameNode.name ? nameNode.name : '<unknown>';
|
|
19
19
|
if (funcName.startsWith('c:') || funcName.startsWith('C:'))
|
|
20
20
|
continue;
|
|
21
21
|
const paramList = defun.children[2];
|
|
22
|
-
if (
|
|
22
|
+
if (!(0, parser_1.astIsList)(paramList))
|
|
23
23
|
continue;
|
|
24
24
|
// Collect params before / (AutoLISP: (defun foo (a b / x y) ...) — a,b are params, x,y are locals)
|
|
25
25
|
const params = [];
|
|
@@ -14,7 +14,7 @@ function checkUnusedVariableAst(ast, file) {
|
|
|
14
14
|
const setqForms = (0, parser_1.astFindListWithHead)(ast, 'setq');
|
|
15
15
|
const setqVars = new Map();
|
|
16
16
|
for (const form of setqForms) {
|
|
17
|
-
if (!
|
|
17
|
+
if (!(0, parser_1.astIsList)(form))
|
|
18
18
|
continue;
|
|
19
19
|
for (let i = 1; i < form.children.length; i += 2) {
|
|
20
20
|
const v = form.children[i];
|
|
@@ -37,7 +37,7 @@ function checkUnusedVariableAst(ast, file) {
|
|
|
37
37
|
// Set of defined var nodes (to exclude from usage)
|
|
38
38
|
const defNodes = new Set();
|
|
39
39
|
for (const form of setqForms) {
|
|
40
|
-
if (!
|
|
40
|
+
if (!(0, parser_1.astIsList)(form))
|
|
41
41
|
continue;
|
|
42
42
|
for (let i = 1; i < form.children.length; i += 2) {
|
|
43
43
|
defNodes.add(form.children[i]);
|
|
@@ -12,15 +12,15 @@ function checkVariableShadowAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const letNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'let'));
|
|
14
14
|
for (const node of letNodes) {
|
|
15
|
-
if (!node
|
|
15
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
|
|
16
16
|
continue;
|
|
17
17
|
const bindingList = node.children[1];
|
|
18
|
-
if (
|
|
18
|
+
if (!(0, parser_1.astIsList)(bindingList))
|
|
19
19
|
continue;
|
|
20
20
|
// Collect let-bound variable names
|
|
21
21
|
const letVars = new Set();
|
|
22
22
|
for (const binding of bindingList.children) {
|
|
23
|
-
if (
|
|
23
|
+
if (!(0, parser_1.astIsList)(binding) || binding.children.length === 0)
|
|
24
24
|
continue;
|
|
25
25
|
const varNode = binding.children[0];
|
|
26
26
|
if (varNode.type !== 'symbol' || !varNode.name)
|
|
@@ -12,7 +12,7 @@ function checkWhileConstantAst(ast, file) {
|
|
|
12
12
|
const issues = [];
|
|
13
13
|
const whileNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'while'));
|
|
14
14
|
for (const node of whileNodes) {
|
|
15
|
-
if (!node
|
|
15
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
|
|
16
16
|
continue;
|
|
17
17
|
const cond = node.children[1];
|
|
18
18
|
// Check for constant conditions: nil, T, 1, 0, etc.
|
package/dist/cli/collector.js
CHANGED
|
@@ -41,6 +41,22 @@ const path = __importStar(require("path"));
|
|
|
41
41
|
const child_process_1 = require("child_process");
|
|
42
42
|
const GLOB_PLACEHOLDER = '\x00GLOB\x00';
|
|
43
43
|
const GLOB_RE = new RegExp(GLOB_PLACEHOLDER, 'g');
|
|
44
|
+
const globCache = new Map();
|
|
45
|
+
function toRegex(pattern) {
|
|
46
|
+
const cached = globCache.get(pattern);
|
|
47
|
+
if (cached)
|
|
48
|
+
return cached;
|
|
49
|
+
const str = pattern
|
|
50
|
+
.replace(/\./g, '\\.')
|
|
51
|
+
.replace(/\*\*\//g, GLOB_PLACEHOLDER)
|
|
52
|
+
.replace(/\*\*/g, '.*')
|
|
53
|
+
.replace(/\*/g, '[^/]*')
|
|
54
|
+
.replace(/\?/g, '.')
|
|
55
|
+
.replace(GLOB_RE, '(.*/)?');
|
|
56
|
+
const re = new RegExp(`^${str}$`);
|
|
57
|
+
globCache.set(pattern, re);
|
|
58
|
+
return re;
|
|
59
|
+
}
|
|
44
60
|
function* walkFiles(dir, rootDir, regex) {
|
|
45
61
|
let entries;
|
|
46
62
|
try {
|
|
@@ -88,15 +104,7 @@ function collectChangedFiles(rootDir, baseBranch) {
|
|
|
88
104
|
}
|
|
89
105
|
}
|
|
90
106
|
function globFiles(pattern, rootDir) {
|
|
91
|
-
|
|
92
|
-
.replace(/\./g, '\\.')
|
|
93
|
-
.replace(/\*\*\//g, GLOB_PLACEHOLDER)
|
|
94
|
-
.replace(/\*\*/g, '.*')
|
|
95
|
-
.replace(/\*/g, '[^/]*')
|
|
96
|
-
.replace(/\?/g, '.')
|
|
97
|
-
.replace(GLOB_RE, '(.*/)?');
|
|
98
|
-
const regex = new RegExp(`^${regexStr}$`);
|
|
99
|
-
return Array.from(walkFiles(rootDir, rootDir, regex));
|
|
107
|
+
return Array.from(walkFiles(rootDir, rootDir, toRegex(pattern)));
|
|
100
108
|
}
|
|
101
109
|
function collectFiles(rootDir, opts, config) {
|
|
102
110
|
const files = new Set();
|
|
@@ -127,23 +135,22 @@ function collectFiles(rootDir, opts, config) {
|
|
|
127
135
|
}
|
|
128
136
|
}
|
|
129
137
|
}
|
|
130
|
-
const
|
|
131
|
-
|
|
138
|
+
const excludeRe = config.source.exclude.length > 0
|
|
139
|
+
? new RegExp(`^(${config.source.exclude
|
|
140
|
+
.map(e => e
|
|
132
141
|
.replace(/\./g, '\\.')
|
|
133
142
|
.replace(/\*\*\//g, GLOB_PLACEHOLDER)
|
|
134
143
|
.replace(/\*\*/g, '.*')
|
|
135
144
|
.replace(/\*/g, '[^/]*')
|
|
136
|
-
.replace(GLOB_RE, '(.*/)?')
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
.replace(GLOB_RE, '(.*/)?'))
|
|
146
|
+
.join('|')})$`)
|
|
147
|
+
: null;
|
|
139
148
|
return Array.from(files)
|
|
140
149
|
.filter(f => {
|
|
150
|
+
if (!excludeRe)
|
|
151
|
+
return true;
|
|
141
152
|
const rel = path.relative(rootDir, f).replace(/\\/g, '/');
|
|
142
|
-
|
|
143
|
-
if (re.test(rel))
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
146
|
-
return true;
|
|
153
|
+
return !excludeRe.test(rel);
|
|
147
154
|
})
|
|
148
155
|
.sort();
|
|
149
156
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LintConfig } from './types';
|
|
2
2
|
export declare function loadConfig(configPath?: string): LintConfig;
|
|
3
|
-
export declare function getCheckSeverity(config: LintConfig, rule: string): string;
|
|
4
3
|
export declare function findOverrides(filepath: string): Partial<LintConfig> | null;
|
|
4
|
+
export declare function clearOverrideCache(): void;
|
|
5
5
|
export declare function mergeOverrides(base: LintConfig, overrides: Partial<LintConfig>): LintConfig;
|
|
6
6
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
CHANGED
|
@@ -34,12 +34,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.loadConfig = loadConfig;
|
|
37
|
-
exports.getCheckSeverity = getCheckSeverity;
|
|
38
37
|
exports.findOverrides = findOverrides;
|
|
38
|
+
exports.clearOverrideCache = clearOverrideCache;
|
|
39
39
|
exports.mergeOverrides = mergeOverrides;
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const presets_1 = require("./presets");
|
|
43
|
+
const overrideCache = new Map();
|
|
43
44
|
const DEFAULT_CONFIG = {
|
|
44
45
|
locale: 'zh',
|
|
45
46
|
source: {
|
|
@@ -199,7 +200,7 @@ function loadConfig(configPath) {
|
|
|
199
200
|
const raw = fs.readFileSync(absPath, 'utf-8');
|
|
200
201
|
const user = JSON.parse(raw);
|
|
201
202
|
// Start from default config
|
|
202
|
-
const merged =
|
|
203
|
+
const merged = structuredClone(DEFAULT_CONFIG);
|
|
203
204
|
// Apply preset if specified (preset applies before user overrides)
|
|
204
205
|
const preset = user.preset;
|
|
205
206
|
if (preset && presets_1.PRESETS[preset]) {
|
|
@@ -211,7 +212,7 @@ function loadConfig(configPath) {
|
|
|
211
212
|
delete merged.preset;
|
|
212
213
|
return merged;
|
|
213
214
|
}
|
|
214
|
-
return
|
|
215
|
+
return structuredClone(DEFAULT_CONFIG);
|
|
215
216
|
}
|
|
216
217
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
217
218
|
function deepMerge(target, source) {
|
|
@@ -228,29 +229,37 @@ function deepMerge(target, source) {
|
|
|
228
229
|
}
|
|
229
230
|
}
|
|
230
231
|
}
|
|
231
|
-
function getCheckSeverity(config, rule) {
|
|
232
|
-
return config.checks[rule] || 'off';
|
|
233
|
-
}
|
|
234
232
|
function findOverrides(filepath) {
|
|
233
|
+
const cached = overrideCache.get(filepath);
|
|
234
|
+
if (cached !== undefined)
|
|
235
|
+
return cached;
|
|
235
236
|
let dir = path.dirname(filepath);
|
|
236
237
|
for (;;) {
|
|
237
238
|
const configPath = path.join(dir, '.atlisp-lint.json');
|
|
238
239
|
if (fs.existsSync(configPath)) {
|
|
239
240
|
try {
|
|
240
|
-
|
|
241
|
+
const result = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
242
|
+
overrideCache.set(filepath, result);
|
|
243
|
+
return result;
|
|
241
244
|
}
|
|
242
245
|
catch {
|
|
246
|
+
overrideCache.set(filepath, null);
|
|
243
247
|
return null;
|
|
244
248
|
}
|
|
245
249
|
}
|
|
246
250
|
const parent = path.dirname(dir);
|
|
247
|
-
if (parent === dir)
|
|
251
|
+
if (parent === dir) {
|
|
252
|
+
overrideCache.set(filepath, null);
|
|
248
253
|
return null;
|
|
254
|
+
}
|
|
249
255
|
dir = parent;
|
|
250
256
|
}
|
|
251
257
|
}
|
|
258
|
+
function clearOverrideCache() {
|
|
259
|
+
overrideCache.clear();
|
|
260
|
+
}
|
|
252
261
|
function mergeOverrides(base, overrides) {
|
|
253
|
-
const merged =
|
|
262
|
+
const merged = structuredClone(base);
|
|
254
263
|
deepMerge(merged, overrides);
|
|
255
264
|
return merged;
|
|
256
265
|
}
|
package/dist/fixes/index.d.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { Issue } from '../types';
|
|
2
|
+
import { AstNode, AstPosition } from '@atlisp/parser';
|
|
2
3
|
export type FixResult = string | null;
|
|
3
4
|
export interface FixProvider {
|
|
4
5
|
(line: string): string;
|
|
5
6
|
}
|
|
7
|
+
export interface ContentFixProvider {
|
|
8
|
+
(issues: Issue[], content: string, ast?: AstNode, posToOffset?: (pos: AstPosition) => number): string;
|
|
9
|
+
}
|
|
6
10
|
export declare const FIXABLE_RULES: Set<string>;
|
|
7
11
|
export declare function applyFixesFromProviders(issues: Issue[], content: string, filepath: string): string;
|
|
8
12
|
//# sourceMappingURL=index.d.ts.map
|