@atlisp/lint 0.1.4 → 0.1.6
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/LICENSE +21 -0
- package/README.md +182 -58
- package/atlisp-lint.default.json +31 -1
- package/dist/atlisp-lint.default.json +90 -0
- package/dist/cache.d.ts +2 -2
- package/dist/cache.js +6 -6
- package/dist/checks/append-single.d.ts +3 -0
- package/dist/checks/append-single.js +17 -0
- package/dist/checks/arg-count.d.ts +3 -0
- package/dist/checks/arg-count.js +123 -0
- package/dist/checks/assoc-without-cdr.d.ts +5 -0
- package/dist/checks/assoc-without-cdr.js +32 -0
- package/dist/checks/comment-style.d.ts +3 -0
- package/dist/checks/comment-style.js +24 -0
- package/dist/checks/cond-duplicate.d.ts +5 -0
- package/dist/checks/cond-duplicate.js +52 -0
- package/dist/checks/cond-simplify.d.ts +3 -0
- package/dist/checks/cond-simplify.js +45 -0
- package/dist/checks/constant-condition.js +4 -4
- package/dist/checks/dangerous-calls.js +2 -2
- package/dist/checks/dangling-defun.d.ts +3 -0
- package/dist/checks/dangling-defun.js +10 -28
- package/dist/checks/double-not.js +1 -1
- package/dist/checks/duplicate-defun.d.ts +6 -0
- package/dist/checks/duplicate-defun.js +50 -0
- package/dist/checks/dynamic-doc.d.ts +3 -0
- package/dist/checks/dynamic-doc.js +21 -0
- package/dist/checks/empty-branch.js +1 -1
- package/dist/checks/empty-catch.d.ts +3 -0
- package/dist/checks/empty-catch.js +34 -0
- package/dist/checks/eq-usage.d.ts +3 -0
- package/dist/checks/eq-usage.js +25 -0
- package/dist/checks/error-handling.d.ts +3 -0
- package/dist/checks/error-handling.js +56 -0
- package/dist/checks/extra-parens.d.ts +3 -0
- package/dist/checks/extra-parens.js +45 -0
- package/dist/checks/function-complexity.js +1 -1
- package/dist/checks/function-order.d.ts +3 -0
- package/dist/checks/function-order.js +33 -0
- package/dist/checks/global-naming.d.ts +3 -0
- package/dist/checks/global-naming.js +62 -0
- package/dist/checks/identical-branches.d.ts +5 -0
- package/dist/checks/identical-branches.js +54 -0
- package/dist/checks/index.d.ts +3 -0
- package/dist/checks/index.js +117 -0
- package/dist/checks/lambda-syntax.d.ts +3 -0
- package/dist/checks/lambda-syntax.js +25 -0
- package/dist/checks/long-function-call.d.ts +3 -0
- package/dist/checks/long-function-call.js +54 -0
- package/dist/checks/loop-optimization.d.ts +3 -0
- package/dist/checks/loop-optimization.js +17 -0
- package/dist/checks/magic-number.d.ts +3 -0
- package/dist/checks/magic-number.js +21 -0
- package/dist/checks/misplaced-else.js +1 -1
- package/dist/checks/missing-export.js +2 -2
- package/dist/checks/mixed-indent.d.ts +3 -0
- package/dist/checks/mixed-indent.js +19 -0
- package/dist/checks/module-reg.js +1 -1
- package/dist/checks/multiple-setq.js +1 -1
- package/dist/checks/no-return.d.ts +3 -0
- package/dist/checks/no-return.js +51 -0
- package/dist/checks/nth-usage.d.ts +3 -0
- package/dist/checks/nth-usage.js +17 -0
- package/dist/checks/quote-style.d.ts +3 -0
- package/dist/checks/quote-style.js +25 -0
- package/dist/checks/quote-vs-function.js +1 -1
- package/dist/checks/recursive-call.js +1 -1
- package/dist/checks/redundant-cond.js +2 -2
- package/dist/checks/redundant-if.d.ts +3 -0
- package/dist/checks/redundant-if.js +17 -0
- 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-quotes.js +1 -1
- package/dist/checks/redundant-setq.js +1 -1
- package/dist/checks/self-compare.js +1 -1
- package/dist/checks/setq-multiple.d.ts +3 -0
- package/dist/checks/setq-multiple.js +17 -0
- package/dist/checks/setq-single-arg.d.ts +5 -0
- package/dist/checks/setq-single-arg.js +30 -0
- package/dist/checks/shadow-builtin.d.ts +3 -0
- package/dist/checks/shadow-builtin.js +77 -0
- package/dist/checks/single-arg-and-or.js +1 -1
- package/dist/checks/strcat-usage.d.ts +3 -0
- package/dist/checks/strcat-usage.js +25 -0
- package/dist/checks/type-check.d.ts +3 -0
- package/dist/checks/type-check.js +26 -0
- package/dist/checks/unused-let.js +1 -1
- package/dist/checks/unused-package-dep.js +3 -3
- package/dist/checks/variable-shadow.js +1 -1
- package/dist/checks/while-constant.d.ts +5 -0
- package/dist/checks/while-constant.js +40 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +70 -2
- package/dist/disable.js +1 -1
- package/dist/formatters.d.ts +1 -0
- package/dist/formatters.js +18 -2
- package/dist/index.js +53 -13
- package/dist/lib/lint-sbcl.lisp +161 -0
- package/dist/locale.js +24 -0
- package/dist/presets.d.ts +4 -0
- package/dist/presets.js +158 -0
- package/dist/project.js +37 -6
- package/dist/rules.d.ts +9 -0
- package/dist/rules.js +238 -0
- package/dist/runner.d.ts +2 -0
- package/dist/runner.js +198 -11
- package/dist/sbcl.js +1 -1
- package/dist/stub-packages.json +41 -0
- package/dist/types.d.ts +6 -0
- package/dist/validate.d.ts +8 -0
- package/dist/validate.js +125 -0
- package/dist/watch.d.ts +9 -0
- package/dist/watch.js +113 -0
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkAssocWithoutCdr = checkAssocWithoutCdr;
|
|
4
|
+
exports.checkAssocWithoutCdrAst = checkAssocWithoutCdrAst;
|
|
5
|
+
const locale_1 = require("../locale");
|
|
6
|
+
const parser_1 = require("@atlisp/parser");
|
|
7
|
+
function checkAssocWithoutCdr(content, file) {
|
|
8
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
9
|
+
return checkAssocWithoutCdrAst(ast, file);
|
|
10
|
+
}
|
|
11
|
+
function checkAssocWithoutCdrAst(ast, file) {
|
|
12
|
+
const issues = [];
|
|
13
|
+
const assocNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'assoc'));
|
|
14
|
+
for (const node of assocNodes) {
|
|
15
|
+
if (!node.parent)
|
|
16
|
+
continue;
|
|
17
|
+
// Check if parent is not a cdr or cadr call
|
|
18
|
+
const parent = node.parent;
|
|
19
|
+
const isWrapped = (0, parser_1.astIsList)(parent, 'cdr') || (0, parser_1.astIsList)(parent, 'cadr');
|
|
20
|
+
if (!isWrapped) {
|
|
21
|
+
issues.push({
|
|
22
|
+
file,
|
|
23
|
+
line: node.pos.line,
|
|
24
|
+
severity: 'warn',
|
|
25
|
+
rule: 'assoc_without_cdr',
|
|
26
|
+
message: (0, locale_1.t)('assoc_without_cdr'),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return issues;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=assoc-without-cdr.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkCommentStyle = checkCommentStyle;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
function checkCommentStyle(content, file) {
|
|
6
|
+
const issues = [];
|
|
7
|
+
const lines = content.split('\n');
|
|
8
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9
|
+
const line = lines[i];
|
|
10
|
+
const trimmed = line.trim();
|
|
11
|
+
// Detect ; not followed by space (except ;; and ;|)
|
|
12
|
+
if (/^;[^ ;|]/.test(trimmed)) {
|
|
13
|
+
issues.push({
|
|
14
|
+
file,
|
|
15
|
+
line: i + 1,
|
|
16
|
+
severity: 'warn',
|
|
17
|
+
rule: 'comment_style',
|
|
18
|
+
message: (0, locale_1.t)('comment_style'),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return issues;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=comment-style.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Issue } from '../types';
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function checkCondDuplicate(content: string, file: string): Issue[];
|
|
4
|
+
export declare function checkCondDuplicateAst(ast: AstNode, file: string): Issue[];
|
|
5
|
+
//# sourceMappingURL=cond-duplicate.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkCondDuplicate = checkCondDuplicate;
|
|
4
|
+
exports.checkCondDuplicateAst = checkCondDuplicateAst;
|
|
5
|
+
const locale_1 = require("../locale");
|
|
6
|
+
const parser_1 = require("@atlisp/parser");
|
|
7
|
+
function condKey(node) {
|
|
8
|
+
if (node.type === 'symbol')
|
|
9
|
+
return `s:${node.name}`;
|
|
10
|
+
if (node.type === 'number')
|
|
11
|
+
return `n:${node.value}`;
|
|
12
|
+
if (node.type === 'string')
|
|
13
|
+
return `str:${node.value}`;
|
|
14
|
+
if (node.type === 'list') {
|
|
15
|
+
const children = node.children || [];
|
|
16
|
+
return `list(${children.map(condKey).join(',')})`;
|
|
17
|
+
}
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
function checkCondDuplicate(content, file) {
|
|
21
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
22
|
+
return checkCondDuplicateAst(ast, file);
|
|
23
|
+
}
|
|
24
|
+
function checkCondDuplicateAst(ast, file) {
|
|
25
|
+
const issues = [];
|
|
26
|
+
const condNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'cond'));
|
|
27
|
+
for (const node of condNodes) {
|
|
28
|
+
if (!node.children)
|
|
29
|
+
continue;
|
|
30
|
+
const seen = new Map();
|
|
31
|
+
for (let i = 1; i < node.children.length; i++) {
|
|
32
|
+
const clause = node.children[i];
|
|
33
|
+
if (!clause.children || clause.children.length < 1)
|
|
34
|
+
continue;
|
|
35
|
+
const condition = clause.children[0];
|
|
36
|
+
const key = condKey(condition);
|
|
37
|
+
if (seen.has(key)) {
|
|
38
|
+
issues.push({
|
|
39
|
+
file,
|
|
40
|
+
line: node.pos.line,
|
|
41
|
+
severity: 'warn',
|
|
42
|
+
rule: 'cond_duplicate',
|
|
43
|
+
message: (0, locale_1.t)('cond_duplicate'),
|
|
44
|
+
});
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
seen.set(key, i);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return issues;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=cond-duplicate.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkCondSimplify = checkCondSimplify;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkCondSimplify(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
// Match (cond (t ...)) or (cond (expr ...)) - single branch cond
|
|
12
|
+
// A single-branch cond typically looks like: (cond (expr body)) on one line
|
|
13
|
+
const m = stripped.match(/\(cond\s+\(/);
|
|
14
|
+
if (m) {
|
|
15
|
+
// Count how many top-level branches inside this cond
|
|
16
|
+
const condStart = stripped.indexOf('(cond');
|
|
17
|
+
const afterCond = stripped.slice(condStart + 5).trim();
|
|
18
|
+
let branchCount = 0;
|
|
19
|
+
let parenDepth = 0;
|
|
20
|
+
for (const ch of afterCond) {
|
|
21
|
+
if (ch === '(') {
|
|
22
|
+
if (parenDepth === 0)
|
|
23
|
+
branchCount++;
|
|
24
|
+
parenDepth++;
|
|
25
|
+
}
|
|
26
|
+
else if (ch === ')') {
|
|
27
|
+
parenDepth--;
|
|
28
|
+
if (parenDepth < 0)
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (branchCount === 1) {
|
|
33
|
+
issues.push({
|
|
34
|
+
file,
|
|
35
|
+
line: i + 1,
|
|
36
|
+
severity: 'warn',
|
|
37
|
+
rule: 'cond_simplify',
|
|
38
|
+
message: (0, locale_1.t)('cond_simplify'),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return issues;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=cond-simplify.js.map
|
|
@@ -14,7 +14,7 @@ function checkConstantConditionAst(ast, file) {
|
|
|
14
14
|
const issues = [];
|
|
15
15
|
// Check if / while with constant condition
|
|
16
16
|
for (const form of CONDITION_FORMS) {
|
|
17
|
-
const nodes = (0, parser_1.astFindAll)(ast,
|
|
17
|
+
const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, form));
|
|
18
18
|
for (const node of nodes) {
|
|
19
19
|
if (!node.children || node.children.length < 2)
|
|
20
20
|
continue;
|
|
@@ -31,7 +31,7 @@ function checkConstantConditionAst(ast, file) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
// Check (cond (T ...))
|
|
34
|
-
const condNodes = (0, parser_1.astFindAll)(ast,
|
|
34
|
+
const condNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'cond'));
|
|
35
35
|
for (const condNode of condNodes) {
|
|
36
36
|
if (!condNode.children)
|
|
37
37
|
continue;
|
|
@@ -52,7 +52,7 @@ function checkConstantConditionAst(ast, file) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
// Check (or T ...) — first arg is T, always short-circuits to T
|
|
55
|
-
const orNodes = (0, parser_1.astFindAll)(ast,
|
|
55
|
+
const orNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'or'));
|
|
56
56
|
for (const node of orNodes) {
|
|
57
57
|
if (!node.children || node.children.length < 2)
|
|
58
58
|
continue;
|
|
@@ -68,7 +68,7 @@ function checkConstantConditionAst(ast, file) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
// Check (and nil ...) — first arg is nil, always short-circuits to nil
|
|
71
|
-
const andNodes = (0, parser_1.astFindAll)(ast,
|
|
71
|
+
const andNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'and'));
|
|
72
72
|
for (const node of andNodes) {
|
|
73
73
|
if (!node.children || node.children.length < 2)
|
|
74
74
|
continue;
|
|
@@ -47,7 +47,7 @@ function checkDangerousCallsAst(ast, file, config) {
|
|
|
47
47
|
continue;
|
|
48
48
|
if (pattern.commandShell) {
|
|
49
49
|
// Find (command "shell" ...) calls
|
|
50
|
-
const commandCalls = (0, parser_1.astFindAll)(ast,
|
|
50
|
+
const commandCalls = (0, parser_1.astFindAll)(ast, n => isFunctionCall(n, 'command'));
|
|
51
51
|
for (const call of commandCalls) {
|
|
52
52
|
if (isInQuote(call))
|
|
53
53
|
continue;
|
|
@@ -67,7 +67,7 @@ function checkDangerousCallsAst(ast, file, config) {
|
|
|
67
67
|
}
|
|
68
68
|
else if (pattern.functionCall) {
|
|
69
69
|
// Find actual function calls
|
|
70
|
-
const calls = (0, parser_1.astFindAll)(ast,
|
|
70
|
+
const calls = (0, parser_1.astFindAll)(ast, n => isFunctionCall(n, pattern.rule));
|
|
71
71
|
for (const call of calls) {
|
|
72
72
|
if (isInQuote(call))
|
|
73
73
|
continue;
|
|
@@ -2,5 +2,8 @@ import { Issue } from '../types';
|
|
|
2
2
|
export declare function checkDanglingDefun(file: string, allDefuns: Map<string, {
|
|
3
3
|
file: string;
|
|
4
4
|
line: number;
|
|
5
|
+
}[]>, allReferences: Map<string, {
|
|
6
|
+
file: string;
|
|
7
|
+
line: number;
|
|
5
8
|
}[]>): Issue[];
|
|
6
9
|
//# sourceMappingURL=dangling-defun.d.ts.map
|
|
@@ -37,46 +37,28 @@ exports.checkDanglingDefun = checkDanglingDefun;
|
|
|
37
37
|
const locale_1 = require("../locale");
|
|
38
38
|
const parser_1 = require("@atlisp/parser");
|
|
39
39
|
const fs = __importStar(require("fs"));
|
|
40
|
-
function checkDanglingDefun(file, allDefuns) {
|
|
40
|
+
function checkDanglingDefun(file, allDefuns, allReferences) {
|
|
41
41
|
const issues = [];
|
|
42
42
|
const content = fs.readFileSync(file, 'utf-8');
|
|
43
43
|
const ast = (0, parser_1.parseAst)(content);
|
|
44
44
|
const localDefuns = [];
|
|
45
|
-
const defunNodes = (0, parser_1.astFindAll)(ast,
|
|
45
|
+
const defunNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
|
|
46
46
|
for (const node of defunNodes) {
|
|
47
47
|
if (node.children && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
|
|
48
|
-
localDefuns.push(node.children[1].name);
|
|
48
|
+
localDefuns.push({ name: node.children[1].name, line: node.pos.line });
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
-
for (const call of allCalls) {
|
|
58
|
-
const name = call.children[0].name;
|
|
59
|
-
if (name !== 'defun' && name !== 'defun-q' && !name.startsWith('c:') && !name.includes(':')) {
|
|
60
|
-
calledSymbols.add(name);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
for (const defunName of localDefuns) {
|
|
64
|
-
let isCalled = calledSymbols.has(defunName);
|
|
65
|
-
if (!isCalled) {
|
|
66
|
-
for (const [, defs] of allDefuns) {
|
|
67
|
-
if (defs.some((d) => d.file !== file)) {
|
|
68
|
-
isCalled = true;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (!isCalled) {
|
|
51
|
+
for (const defun of localDefuns) {
|
|
52
|
+
const refs = allReferences.get(defun.name) || [];
|
|
53
|
+
const calledElsewhere = refs.some(r => r.file !== file);
|
|
54
|
+
const calledLocally = refs.some(r => r.file === file);
|
|
55
|
+
if (!calledLocally && !calledElsewhere) {
|
|
74
56
|
issues.push({
|
|
75
57
|
file,
|
|
76
|
-
line:
|
|
58
|
+
line: defun.line,
|
|
77
59
|
severity: 'warn',
|
|
78
60
|
rule: 'dangling_defun',
|
|
79
|
-
message: (0, locale_1.t)('dangling_defun',
|
|
61
|
+
message: (0, locale_1.t)('dangling_defun', defun.name),
|
|
80
62
|
});
|
|
81
63
|
}
|
|
82
64
|
}
|
|
@@ -10,7 +10,7 @@ function checkDoubleNot(content, file) {
|
|
|
10
10
|
}
|
|
11
11
|
function checkDoubleNotAst(ast, file) {
|
|
12
12
|
const issues = [];
|
|
13
|
-
const notNodes = (0, parser_1.astFindAll)(ast,
|
|
13
|
+
const notNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'not'));
|
|
14
14
|
for (const node of notNodes) {
|
|
15
15
|
if (!node.children || node.children.length < 2)
|
|
16
16
|
continue;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkDuplicateDefun = checkDuplicateDefun;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
function checkDuplicateDefun(filepath, allDefuns) {
|
|
6
|
+
const issues = [];
|
|
7
|
+
const defunsInFile = new Map();
|
|
8
|
+
// Collect defuns in this file
|
|
9
|
+
for (const [name, locations] of allDefuns) {
|
|
10
|
+
for (const loc of locations) {
|
|
11
|
+
if (loc.file === filepath) {
|
|
12
|
+
const lines = defunsInFile.get(name) || [];
|
|
13
|
+
lines.push(loc.line);
|
|
14
|
+
defunsInFile.set(name, lines);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// Check for duplicates within this file
|
|
19
|
+
for (const [name, lines] of defunsInFile) {
|
|
20
|
+
if (lines.length > 1) {
|
|
21
|
+
for (const line of lines.slice(1)) {
|
|
22
|
+
issues.push({
|
|
23
|
+
file: filepath,
|
|
24
|
+
line,
|
|
25
|
+
severity: 'warn',
|
|
26
|
+
rule: 'duplicate_defun',
|
|
27
|
+
message: (0, locale_1.t)('duplicate_defun', name),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Check for duplicates across files
|
|
33
|
+
for (const [name, locations] of allDefuns) {
|
|
34
|
+
const matchingFiles = locations.filter(l => l.file !== filepath);
|
|
35
|
+
if (matchingFiles.length > 0) {
|
|
36
|
+
const thisFileLoc = locations.find(l => l.file === filepath);
|
|
37
|
+
if (thisFileLoc) {
|
|
38
|
+
issues.push({
|
|
39
|
+
file: filepath,
|
|
40
|
+
line: thisFileLoc.line,
|
|
41
|
+
severity: 'warn',
|
|
42
|
+
rule: 'duplicate_defun',
|
|
43
|
+
message: (0, locale_1.t)('duplicate_defun', name),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return issues;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=duplicate-defun.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkDynamicDoc = checkDynamicDoc;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkDynamicDoc(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
const m = stripped.match(/\(defun\s+(C:\S+)\b/);
|
|
12
|
+
if (m) {
|
|
13
|
+
const body = lines.slice(i + 1).join('\n');
|
|
14
|
+
if (!body.includes('(princ)')) {
|
|
15
|
+
issues.push({ file, line: i + 1, severity: 'warn', rule: 'dynamic_doc', message: (0, locale_1.t)('dynamic_doc', m[1]) });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return issues;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=dynamic-doc.js.map
|
|
@@ -13,7 +13,7 @@ function checkEmptyBranch(content, file) {
|
|
|
13
13
|
function checkEmptyBranchAst(ast, file) {
|
|
14
14
|
const issues = [];
|
|
15
15
|
for (const form of BRANCH_FORMS) {
|
|
16
|
-
const nodes = (0, parser_1.astFindAll)(ast,
|
|
16
|
+
const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, form));
|
|
17
17
|
for (const node of nodes) {
|
|
18
18
|
if (!node.children)
|
|
19
19
|
continue;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkEmptyCatch = checkEmptyCatch;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkEmptyCatch(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
// Detect (vl-catch-all-apply ...) not inside (vl-catch-all-error-p ...) check
|
|
12
|
+
if (stripped.includes('vl-catch-all-apply')) {
|
|
13
|
+
// Check subsequent lines for error-p check
|
|
14
|
+
let hasErrorCheck = false;
|
|
15
|
+
for (let j = i + 1; j < Math.min(i + 5, lines.length); j++) {
|
|
16
|
+
if ((0, utils_1.stripLine)(lines[j]).includes('vl-catch-all-error-p')) {
|
|
17
|
+
hasErrorCheck = true;
|
|
18
|
+
break;
|
|
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
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return issues;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=empty-catch.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkEqUsage = checkEqUsage;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkEqUsage(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
11
|
+
// Detect (eq num ...) where num is literal number
|
|
12
|
+
const m = stripped.match(/\(eq\s+\S+\s+(\d+)/);
|
|
13
|
+
if (m) {
|
|
14
|
+
issues.push({
|
|
15
|
+
file,
|
|
16
|
+
line: i + 1,
|
|
17
|
+
severity: 'warn',
|
|
18
|
+
rule: 'eq_usage',
|
|
19
|
+
message: (0, locale_1.t)('eq_usage'),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return issues;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=eq-usage.js.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkErrorHandling = checkErrorHandling;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkErrorHandling(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
let inDefun = false;
|
|
10
|
+
let defunName = '';
|
|
11
|
+
let defunStart = 0;
|
|
12
|
+
let defunText = '';
|
|
13
|
+
let hasErrorHandler = false;
|
|
14
|
+
for (let i = 0; i < lines.length; i++) {
|
|
15
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
16
|
+
if (!inDefun) {
|
|
17
|
+
const m = stripped.match(/\(defun\s+(\S+)/);
|
|
18
|
+
if (m) {
|
|
19
|
+
inDefun = true;
|
|
20
|
+
defunName = m[1];
|
|
21
|
+
defunStart = i + 1;
|
|
22
|
+
defunText = '';
|
|
23
|
+
hasErrorHandler = false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (inDefun) {
|
|
27
|
+
defunText += stripped + '\n';
|
|
28
|
+
if (stripped.includes('*error*'))
|
|
29
|
+
hasErrorHandler = true;
|
|
30
|
+
// Check for depth returning to 0 (end of defun)
|
|
31
|
+
let depth = 0;
|
|
32
|
+
for (const ch of stripped) {
|
|
33
|
+
if (ch === '(')
|
|
34
|
+
depth++;
|
|
35
|
+
else if (ch === ')')
|
|
36
|
+
depth--;
|
|
37
|
+
}
|
|
38
|
+
if (depth === 0 && defunText.trim().length > 0 && defunName) {
|
|
39
|
+
// End of defun - check if it uses command but no *error*
|
|
40
|
+
if (/\(command\b/.test(defunText) && !hasErrorHandler && !defunName.startsWith('c:')) {
|
|
41
|
+
issues.push({
|
|
42
|
+
file,
|
|
43
|
+
line: defunStart,
|
|
44
|
+
severity: 'warn',
|
|
45
|
+
rule: 'error_handling',
|
|
46
|
+
message: (0, locale_1.t)('error_handling', defunName),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
inDefun = false;
|
|
50
|
+
defunName = '';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return issues;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=error-handling.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkExtraParens = checkExtraParens;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkExtraParens(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
let runningDepth = 0;
|
|
10
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
12
|
+
const closeOnly = stripped.trim();
|
|
13
|
+
// Empty line resets nothing; just continue tracking depth
|
|
14
|
+
if (!closeOnly)
|
|
15
|
+
continue;
|
|
16
|
+
// Count parens on this line
|
|
17
|
+
let lineOpen = 0, lineClose = 0;
|
|
18
|
+
for (const ch of stripped) {
|
|
19
|
+
if (ch === '(')
|
|
20
|
+
lineOpen++;
|
|
21
|
+
else if (ch === ')')
|
|
22
|
+
lineClose++;
|
|
23
|
+
}
|
|
24
|
+
// Check if this line is pure closing parens
|
|
25
|
+
if (/^\)+$/.test(closeOnly)) {
|
|
26
|
+
// Only flag if these closing parens exceed the running depth
|
|
27
|
+
// (i.e., they are unmatched / extra)
|
|
28
|
+
if (lineClose > runningDepth + lineOpen) {
|
|
29
|
+
const extra = lineClose - runningDepth - lineOpen;
|
|
30
|
+
issues.push({
|
|
31
|
+
file,
|
|
32
|
+
line: i + 1,
|
|
33
|
+
severity: 'warn',
|
|
34
|
+
rule: 'extra_parens',
|
|
35
|
+
message: (0, locale_1.t)('extra_parens', extra),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
runningDepth += lineOpen - lineClose;
|
|
40
|
+
if (runningDepth < 0)
|
|
41
|
+
runningDepth = 0;
|
|
42
|
+
}
|
|
43
|
+
return issues;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=extra-parens.js.map
|
|
@@ -41,7 +41,7 @@ function checkFunctionComplexityAst(ast, file, maxLines, maxNesting) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
// Check lambda forms: (lambda (args) body...)
|
|
44
|
-
const lambdas = (0, parser_1.astFindAll)(ast,
|
|
44
|
+
const lambdas = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'lambda'));
|
|
45
45
|
for (const lam of lambdas) {
|
|
46
46
|
if (!lam.children || !lam.end)
|
|
47
47
|
continue;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkFunctionOrder = checkFunctionOrder;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
function checkFunctionOrder(content, file) {
|
|
7
|
+
const issues = [];
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
const defined = new Set();
|
|
10
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11
|
+
const stripped = (0, utils_1.stripLine)(lines[i]);
|
|
12
|
+
const defunM = stripped.match(/\(defun\s+(\S+)/);
|
|
13
|
+
if (defunM) {
|
|
14
|
+
const name = defunM[1];
|
|
15
|
+
if (name.includes('/') || name.startsWith('c:'))
|
|
16
|
+
continue;
|
|
17
|
+
defined.add(name);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const callM = stripped.match(/\((\S+)\s+/);
|
|
21
|
+
if (callM) {
|
|
22
|
+
const name = callM[1];
|
|
23
|
+
if (name.startsWith('(') || name === 'defun' || name.startsWith('@') || /^[a-z]+:/.test(name))
|
|
24
|
+
continue;
|
|
25
|
+
if (!defined.has(name) && name.length > 1 && /^[a-z]/.test(name)) {
|
|
26
|
+
defined.add(name);
|
|
27
|
+
issues.push({ file, line: i + 1, severity: 'warn', rule: 'function_order', message: (0, locale_1.t)('function_order', name) });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return issues;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=function-order.js.map
|