@atlisp/lint 0.1.2 → 0.1.4
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 -0
- package/atlisp-lint.default.json +26 -7
- package/dist/checks/bare-names.js +4 -1
- package/dist/checks/cl-syntax.js +4 -1
- package/dist/checks/commented-code.d.ts +3 -0
- package/dist/checks/commented-code.js +46 -0
- package/dist/checks/constant-condition.d.ts +5 -0
- package/dist/checks/constant-condition.js +88 -0
- package/dist/checks/dangerous-calls.d.ts +2 -0
- package/dist/checks/dangerous-calls.js +70 -29
- package/dist/checks/dangling-defun.d.ts +6 -0
- package/dist/checks/dangling-defun.js +85 -0
- package/dist/checks/double-not.d.ts +5 -0
- package/dist/checks/double-not.js +30 -0
- package/dist/checks/empty-branch.d.ts +5 -0
- package/dist/checks/empty-branch.js +34 -0
- package/dist/checks/empty-comments.d.ts +3 -0
- package/dist/checks/empty-comments.js +26 -0
- package/dist/checks/encoding.js +13 -4
- package/dist/checks/function-complexity.d.ts +2 -0
- package/dist/checks/function-complexity.js +89 -45
- package/dist/checks/line-length.js +4 -1
- package/dist/checks/misplaced-else.d.ts +5 -0
- package/dist/checks/misplaced-else.js +31 -0
- package/dist/checks/missing-doc.js +5 -2
- package/dist/checks/missing-export.d.ts +13 -0
- package/dist/checks/missing-export.js +94 -0
- package/dist/checks/module-reg.js +9 -4
- package/dist/checks/multiple-setq.d.ts +5 -0
- package/dist/checks/multiple-setq.js +51 -0
- package/dist/checks/namespace-header.js +16 -6
- package/dist/checks/open-close.d.ts +2 -0
- package/dist/checks/open-close.js +18 -13
- package/dist/checks/parameter-naming.d.ts +2 -0
- package/dist/checks/parameter-naming.js +26 -14
- package/dist/checks/parens.js +8 -3
- package/dist/checks/quote-vs-function.d.ts +5 -0
- package/dist/checks/quote-vs-function.js +50 -0
- package/dist/checks/recursive-call.d.ts +5 -0
- package/dist/checks/recursive-call.js +49 -0
- package/dist/checks/redundant-cond.d.ts +5 -0
- package/dist/checks/redundant-cond.js +50 -0
- package/dist/checks/redundant-let.d.ts +5 -0
- package/dist/checks/redundant-let.js +31 -0
- package/dist/checks/redundant-nil-else.d.ts +5 -0
- package/dist/checks/redundant-nil-else.js +32 -0
- package/dist/checks/redundant-progn.d.ts +5 -0
- package/dist/checks/redundant-progn.js +31 -0
- package/dist/checks/redundant-quotes.d.ts +5 -0
- package/dist/checks/redundant-quotes.js +33 -0
- package/dist/checks/redundant-setq.d.ts +5 -0
- package/dist/checks/redundant-setq.js +34 -0
- package/dist/checks/self-compare.d.ts +5 -0
- package/dist/checks/self-compare.js +37 -0
- package/dist/checks/single-arg-and-or.d.ts +5 -0
- package/dist/checks/single-arg-and-or.js +32 -0
- package/dist/checks/token-in-url.js +4 -1
- package/dist/checks/trailing-paren.d.ts +3 -0
- package/dist/checks/trailing-paren.js +44 -0
- package/dist/checks/trailing-ws.js +4 -1
- package/dist/checks/unused-let.d.ts +5 -0
- package/dist/checks/unused-let.js +57 -0
- package/dist/checks/unused-local-fun.d.ts +5 -0
- package/dist/checks/unused-local-fun.js +62 -0
- package/dist/checks/unused-package-dep.d.ts +3 -0
- package/dist/checks/unused-package-dep.js +93 -0
- package/dist/checks/unused-param.d.ts +5 -0
- package/dist/checks/unused-param.js +63 -0
- package/dist/checks/unused-variable.d.ts +2 -0
- package/dist/checks/unused-variable.js +70 -30
- package/dist/checks/variable-shadow.d.ts +5 -0
- package/dist/checks/variable-shadow.js +66 -0
- package/dist/checks/vlax.js +8 -3
- package/dist/config.d.ts +1 -2
- package/dist/config.js +0 -13
- package/dist/disable.js +4 -1
- package/dist/formatters.d.ts +1 -2
- package/dist/formatters.js +8 -61
- package/dist/index.d.ts +1 -1
- package/dist/index.js +137 -117
- package/dist/locale.js +83 -47
- package/dist/project.d.ts +3 -0
- package/dist/project.js +138 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.js +215 -23
- package/dist/sbcl.js +16 -11
- package/dist/worker.js +10 -50
- package/lib/lint-sbcl.lisp +19 -54
- package/package.json +18 -4
- package/dist/atlisp-lint.default.json +0 -71
- package/dist/cache.d.ts.map +0 -1
- package/dist/cache.js.map +0 -1
- package/dist/checks/arg-count.d.ts +0 -3
- package/dist/checks/arg-count.d.ts.map +0 -1
- package/dist/checks/arg-count.js +0 -120
- package/dist/checks/arg-count.js.map +0 -1
- package/dist/checks/bare-names.d.ts.map +0 -1
- package/dist/checks/bare-names.js.map +0 -1
- package/dist/checks/cl-syntax.d.ts.map +0 -1
- package/dist/checks/cl-syntax.js.map +0 -1
- package/dist/checks/cond-simplify.d.ts +0 -3
- package/dist/checks/cond-simplify.d.ts.map +0 -1
- package/dist/checks/cond-simplify.js +0 -42
- package/dist/checks/cond-simplify.js.map +0 -1
- package/dist/checks/dangerous-calls.d.ts.map +0 -1
- package/dist/checks/dangerous-calls.js.map +0 -1
- package/dist/checks/encoding.d.ts.map +0 -1
- package/dist/checks/encoding.js.map +0 -1
- package/dist/checks/error-handling.d.ts +0 -3
- package/dist/checks/error-handling.d.ts.map +0 -1
- package/dist/checks/error-handling.js +0 -53
- package/dist/checks/error-handling.js.map +0 -1
- package/dist/checks/extra-parens.d.ts +0 -3
- package/dist/checks/extra-parens.d.ts.map +0 -1
- package/dist/checks/extra-parens.js +0 -42
- package/dist/checks/extra-parens.js.map +0 -1
- package/dist/checks/function-complexity.d.ts.map +0 -1
- package/dist/checks/function-complexity.js.map +0 -1
- package/dist/checks/global-naming.d.ts +0 -3
- package/dist/checks/global-naming.d.ts.map +0 -1
- package/dist/checks/global-naming.js +0 -51
- package/dist/checks/global-naming.js.map +0 -1
- package/dist/checks/line-length.d.ts.map +0 -1
- package/dist/checks/line-length.js.map +0 -1
- package/dist/checks/missing-doc.d.ts.map +0 -1
- package/dist/checks/missing-doc.js.map +0 -1
- package/dist/checks/module-reg.d.ts.map +0 -1
- package/dist/checks/module-reg.js.map +0 -1
- package/dist/checks/namespace-header.d.ts.map +0 -1
- package/dist/checks/namespace-header.js.map +0 -1
- package/dist/checks/open-close.d.ts.map +0 -1
- package/dist/checks/open-close.js.map +0 -1
- package/dist/checks/parameter-naming.d.ts.map +0 -1
- package/dist/checks/parameter-naming.js.map +0 -1
- package/dist/checks/parens.d.ts.map +0 -1
- package/dist/checks/parens.js.map +0 -1
- package/dist/checks/strcat-usage.d.ts +0 -3
- package/dist/checks/strcat-usage.d.ts.map +0 -1
- package/dist/checks/strcat-usage.js +0 -22
- package/dist/checks/strcat-usage.js.map +0 -1
- package/dist/checks/token-in-url.d.ts.map +0 -1
- package/dist/checks/token-in-url.js.map +0 -1
- package/dist/checks/trailing-ws.d.ts.map +0 -1
- package/dist/checks/trailing-ws.js.map +0 -1
- package/dist/checks/unused-variable.d.ts.map +0 -1
- package/dist/checks/unused-variable.js.map +0 -1
- package/dist/checks/vlax.d.ts.map +0 -1
- package/dist/checks/vlax.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/disable.d.ts.map +0 -1
- package/dist/disable.js.map +0 -1
- package/dist/formatters.d.ts.map +0 -1
- package/dist/formatters.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lib/lint-sbcl.lisp +0 -161
- package/dist/locale.d.ts.map +0 -1
- package/dist/locale.js.map +0 -1
- package/dist/rules.d.ts +0 -9
- package/dist/rules.d.ts.map +0 -1
- package/dist/rules.js +0 -39
- package/dist/rules.js.map +0 -1
- package/dist/runner.d.ts.map +0 -1
- package/dist/runner.js.map +0 -1
- package/dist/sbcl.d.ts.map +0 -1
- package/dist/sbcl.js.map +0 -1
- package/dist/stub-packages.json +0 -41
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
- package/dist/validate.d.ts +0 -8
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js +0 -55
- package/dist/validate.js.map +0 -1
- package/dist/watch.d.ts +0 -9
- package/dist/watch.d.ts.map +0 -1
- package/dist/watch.js +0 -109
- package/dist/watch.js.map +0 -1
- package/dist/worker.d.ts.map +0 -1
- package/dist/worker.js.map +0 -1
- package/pre-commit/hook.sh +0 -4
package/README.md
CHANGED
|
@@ -37,6 +37,9 @@ npx @atlisp/lint --fix
|
|
|
37
37
|
--init 在当前目录生成 atlisp-lint.json
|
|
38
38
|
--install-hook 安装 git pre-commit hook
|
|
39
39
|
--fix 自动修复尾部空格、UTF-8 BOM 等问题
|
|
40
|
+
--cache 启用文件哈希缓存,跳过未变更的文件
|
|
41
|
+
--clear-cache 清除缓存目录
|
|
42
|
+
--parallel 开启多线程并行检查(使用 Worker Threads)
|
|
40
43
|
--hook-args <args> 自定义 pre-commit hook 参数(配合 --install-hook)
|
|
41
44
|
```
|
|
42
45
|
|
package/atlisp-lint.default.json
CHANGED
|
@@ -21,15 +21,34 @@
|
|
|
21
21
|
"parameter_naming": "warn",
|
|
22
22
|
"unused_variable": "warn",
|
|
23
23
|
"missing_doc": "warn",
|
|
24
|
-
"error_handling": "warn",
|
|
25
|
-
"global_naming": "warn",
|
|
26
|
-
"extra_parens": "warn",
|
|
27
|
-
"arg_count": "warn",
|
|
28
|
-
"strcat_usage": "warn",
|
|
29
|
-
"cond_simplify": "warn",
|
|
30
24
|
"trailing_whitespace": "warn",
|
|
31
25
|
"module_registration": "off",
|
|
32
|
-
"namespace_header": "off"
|
|
26
|
+
"namespace_header": "off",
|
|
27
|
+
"unused_parameter": "warn",
|
|
28
|
+
"constant_condition": "warn",
|
|
29
|
+
"redundant_progn": "warn",
|
|
30
|
+
"empty_branch": "warn",
|
|
31
|
+
"unused_let_binding": "warn",
|
|
32
|
+
"recursive_call": "warn",
|
|
33
|
+
"variable_shadow": "warn",
|
|
34
|
+
"redundant_cond": "warn",
|
|
35
|
+
"unused_local_fun": "warn",
|
|
36
|
+
"multiple_setq": "warn",
|
|
37
|
+
"redundant_quotes": "warn",
|
|
38
|
+
"trailing_paren": "warn",
|
|
39
|
+
"empty_comment": "warn",
|
|
40
|
+
"redundant_setq": "warn",
|
|
41
|
+
"redundant_nil_else": "warn",
|
|
42
|
+
"single_arg_and_or": "warn",
|
|
43
|
+
"redundant_let": "warn",
|
|
44
|
+
"self_compare": "warn",
|
|
45
|
+
"misplaced_else": "warn",
|
|
46
|
+
"quote_vs_function": "warn",
|
|
47
|
+
"commented_code": "warn",
|
|
48
|
+
"double_not": "warn",
|
|
49
|
+
"dangling_defun": "warn",
|
|
50
|
+
"missing_export": "warn",
|
|
51
|
+
"unused_package_dep": "warn"
|
|
33
52
|
},
|
|
34
53
|
"line_length": {
|
|
35
54
|
"max": 100,
|
|
@@ -18,7 +18,10 @@ function checkBareFunctionNames(content, file, allowlist, namespacePattern) {
|
|
|
18
18
|
continue; // local: defun foo/bar
|
|
19
19
|
if (!nsRegex.test(name) && !name.includes(':')) {
|
|
20
20
|
issues.push({
|
|
21
|
-
file,
|
|
21
|
+
file,
|
|
22
|
+
line: lineno,
|
|
23
|
+
severity: 'warn',
|
|
24
|
+
rule: 'bare_function_names',
|
|
22
25
|
message: (0, locale_1.t)('bare_function_names', name),
|
|
23
26
|
});
|
|
24
27
|
}
|
package/dist/checks/cl-syntax.js
CHANGED
|
@@ -13,7 +13,10 @@ function checkClSyntax(content, file, keywords) {
|
|
|
13
13
|
const idx = s.indexOf(kw);
|
|
14
14
|
if (idx >= 0 && (idx === 0 || !/[a-zA-Z]/.test(s[idx - 1]))) {
|
|
15
15
|
issues.push({
|
|
16
|
-
file,
|
|
16
|
+
file,
|
|
17
|
+
line: lineno,
|
|
18
|
+
severity: 'warn',
|
|
19
|
+
rule: 'cl_syntax',
|
|
17
20
|
message: (0, locale_1.t)('cl_syntax', kw),
|
|
18
21
|
});
|
|
19
22
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkCommentedCode = checkCommentedCode;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
const CODE_PATTERNS = [
|
|
6
|
+
/^\s*\(defun\s/i,
|
|
7
|
+
/^\s*\(setq\s/i,
|
|
8
|
+
/^\s*\(if\s/i,
|
|
9
|
+
/^\s*\(cond\s/i,
|
|
10
|
+
/^\s*\(while\s/i,
|
|
11
|
+
/^\s*\(foreach\s/i,
|
|
12
|
+
/^\s*\(let\s/i,
|
|
13
|
+
/^\s*\(lambda\s/i,
|
|
14
|
+
/^\s*\(repeat\s/i,
|
|
15
|
+
/^\s*\(progn\s/i,
|
|
16
|
+
/^\s*\(princ?\s/i,
|
|
17
|
+
/^\s*\(command\s/i,
|
|
18
|
+
/^\s*\(defun-q\s/i,
|
|
19
|
+
/^\s*\(vl-\S/,
|
|
20
|
+
/^\s*\(vla-\S/,
|
|
21
|
+
];
|
|
22
|
+
function checkCommentedCode(content, file) {
|
|
23
|
+
const issues = [];
|
|
24
|
+
const lines = content.split('\n');
|
|
25
|
+
for (let i = 0; i < lines.length; i++) {
|
|
26
|
+
const line = lines[i];
|
|
27
|
+
const match = line.match(/^\s*;+([^;].*)$/);
|
|
28
|
+
if (!match)
|
|
29
|
+
continue;
|
|
30
|
+
const afterSemicolon = match[1];
|
|
31
|
+
for (const pat of CODE_PATTERNS) {
|
|
32
|
+
if (pat.test(afterSemicolon)) {
|
|
33
|
+
issues.push({
|
|
34
|
+
file,
|
|
35
|
+
line: i + 1,
|
|
36
|
+
severity: 'warn',
|
|
37
|
+
rule: 'commented_code',
|
|
38
|
+
message: (0, locale_1.t)('commented_code'),
|
|
39
|
+
});
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return issues;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=commented-code.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Issue } from '../types';
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function checkConstantCondition(content: string, file: string): Issue[];
|
|
4
|
+
export declare function checkConstantConditionAst(ast: AstNode, file: string): Issue[];
|
|
5
|
+
//# sourceMappingURL=constant-condition.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkConstantCondition = checkConstantCondition;
|
|
4
|
+
exports.checkConstantConditionAst = checkConstantConditionAst;
|
|
5
|
+
const locale_1 = require("../locale");
|
|
6
|
+
const parser_1 = require("@atlisp/parser");
|
|
7
|
+
const CONDITION_FORMS = new Set(['if', 'while']);
|
|
8
|
+
const CONSTANTS = new Set(['T', 'nil']);
|
|
9
|
+
function checkConstantCondition(content, file) {
|
|
10
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
11
|
+
return checkConstantConditionAst(ast, file);
|
|
12
|
+
}
|
|
13
|
+
function checkConstantConditionAst(ast, file) {
|
|
14
|
+
const issues = [];
|
|
15
|
+
// Check if / while with constant condition
|
|
16
|
+
for (const form of CONDITION_FORMS) {
|
|
17
|
+
const nodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, form));
|
|
18
|
+
for (const node of nodes) {
|
|
19
|
+
if (!node.children || node.children.length < 2)
|
|
20
|
+
continue;
|
|
21
|
+
const cond = node.children[1];
|
|
22
|
+
if (cond.type === 'symbol' && cond.name && CONSTANTS.has(cond.name)) {
|
|
23
|
+
issues.push({
|
|
24
|
+
file,
|
|
25
|
+
line: cond.pos.line,
|
|
26
|
+
severity: 'warn',
|
|
27
|
+
rule: 'constant_condition',
|
|
28
|
+
message: (0, locale_1.t)('constant_condition', cond.name, `(${form} ...)`),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Check (cond (T ...))
|
|
34
|
+
const condNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'cond'));
|
|
35
|
+
for (const condNode of condNodes) {
|
|
36
|
+
if (!condNode.children)
|
|
37
|
+
continue;
|
|
38
|
+
for (let i = 1; i < condNode.children.length; i++) {
|
|
39
|
+
const clause = condNode.children[i];
|
|
40
|
+
if (clause.type !== 'list' || !clause.children || clause.children.length === 0)
|
|
41
|
+
continue;
|
|
42
|
+
const condTest = clause.children[0];
|
|
43
|
+
if (condTest.type === 'symbol' && condTest.name && CONSTANTS.has(condTest.name)) {
|
|
44
|
+
issues.push({
|
|
45
|
+
file,
|
|
46
|
+
line: condTest.pos.line,
|
|
47
|
+
severity: 'warn',
|
|
48
|
+
rule: 'constant_condition',
|
|
49
|
+
message: (0, locale_1.t)('constant_condition', condTest.name, '(cond ...)'),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Check (or T ...) — first arg is T, always short-circuits to T
|
|
55
|
+
const orNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'or'));
|
|
56
|
+
for (const node of orNodes) {
|
|
57
|
+
if (!node.children || node.children.length < 2)
|
|
58
|
+
continue;
|
|
59
|
+
const first = node.children[1];
|
|
60
|
+
if (first.type === 'symbol' && first.name === 'T') {
|
|
61
|
+
issues.push({
|
|
62
|
+
file,
|
|
63
|
+
line: first.pos.line,
|
|
64
|
+
severity: 'warn',
|
|
65
|
+
rule: 'constant_condition',
|
|
66
|
+
message: (0, locale_1.t)('constant_condition', 'T', '(or ...)'),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Check (and nil ...) — first arg is nil, always short-circuits to nil
|
|
71
|
+
const andNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'and'));
|
|
72
|
+
for (const node of andNodes) {
|
|
73
|
+
if (!node.children || node.children.length < 2)
|
|
74
|
+
continue;
|
|
75
|
+
const first = node.children[1];
|
|
76
|
+
if (first.type === 'symbol' && first.name === 'nil') {
|
|
77
|
+
issues.push({
|
|
78
|
+
file,
|
|
79
|
+
line: first.pos.line,
|
|
80
|
+
severity: 'warn',
|
|
81
|
+
rule: 'constant_condition',
|
|
82
|
+
message: (0, locale_1.t)('constant_condition', 'nil', '(and ...)'),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return issues;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=constant-condition.js.map
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { Issue, DangerousCallsConfig } from '../types';
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
2
3
|
export declare function checkDangerousCalls(content: string, file: string, config: DangerousCallsConfig): Issue[];
|
|
4
|
+
export declare function checkDangerousCallsAst(ast: AstNode, file: string, config: DangerousCallsConfig): Issue[];
|
|
3
5
|
//# sourceMappingURL=dangerous-calls.d.ts.map
|
|
@@ -1,41 +1,82 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkDangerousCalls = checkDangerousCalls;
|
|
4
|
-
|
|
4
|
+
exports.checkDangerousCallsAst = checkDangerousCallsAst;
|
|
5
5
|
const locale_1 = require("../locale");
|
|
6
|
+
const parser_1 = require("@atlisp/parser");
|
|
6
7
|
const PATTERNS = [
|
|
7
|
-
{ rule: 'quit',
|
|
8
|
-
{ rule: 'exit',
|
|
9
|
-
{ rule: '
|
|
10
|
-
{
|
|
11
|
-
|
|
8
|
+
{ rule: 'quit', key: 'quit', messageKey: 'dangerous.quit', functionCall: true },
|
|
9
|
+
{ rule: 'exit', key: 'exit', messageKey: 'dangerous.exit', functionCall: true },
|
|
10
|
+
{ rule: 'startapp', key: 'startapp', messageKey: 'dangerous.startapp', functionCall: true },
|
|
11
|
+
{
|
|
12
|
+
rule: 'vl_registry_write',
|
|
13
|
+
key: 'vl_registry_write',
|
|
14
|
+
messageKey: 'dangerous.vl_registry_write',
|
|
15
|
+
functionCall: true,
|
|
16
|
+
},
|
|
17
|
+
{ rule: 'command_shell', key: 'command_shell', messageKey: 'dangerous.command_shell', commandShell: true },
|
|
12
18
|
];
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
function isInQuote(node) {
|
|
20
|
+
let cur = node.parent;
|
|
21
|
+
while (cur) {
|
|
22
|
+
if (cur.type === 'list' &&
|
|
23
|
+
cur.children &&
|
|
24
|
+
cur.children.length > 0 &&
|
|
25
|
+
cur.children[0].type === 'symbol' &&
|
|
26
|
+
cur.children[0].name === 'quote') {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
cur = cur.parent;
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function isFunctionCall(list, name) {
|
|
34
|
+
if (list.type !== 'list' || !list.children || list.children.length === 0)
|
|
35
|
+
return false;
|
|
36
|
+
return list.children[0].type === 'symbol' && list.children[0].name === name;
|
|
37
|
+
}
|
|
20
38
|
function checkDangerousCalls(content, file, config) {
|
|
39
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
40
|
+
return checkDangerousCallsAst(ast, file, config);
|
|
41
|
+
}
|
|
42
|
+
function checkDangerousCallsAst(ast, file, config) {
|
|
21
43
|
const issues = [];
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
for (const pattern of PATTERNS) {
|
|
45
|
+
const severity = config[pattern.key];
|
|
46
|
+
if (severity === 'off')
|
|
47
|
+
continue;
|
|
48
|
+
if (pattern.commandShell) {
|
|
49
|
+
// Find (command "shell" ...) calls
|
|
50
|
+
const commandCalls = (0, parser_1.astFindAll)(ast, (n) => isFunctionCall(n, 'command'));
|
|
51
|
+
for (const call of commandCalls) {
|
|
52
|
+
if (isInQuote(call))
|
|
53
|
+
continue;
|
|
54
|
+
if (!call.children || call.children.length < 2)
|
|
55
|
+
continue;
|
|
56
|
+
const firstArg = call.children[1];
|
|
57
|
+
if (firstArg.type === 'string' && firstArg.value === 'shell') {
|
|
58
|
+
issues.push({
|
|
59
|
+
file,
|
|
60
|
+
line: call.pos.line,
|
|
61
|
+
severity,
|
|
62
|
+
rule: pattern.rule,
|
|
63
|
+
message: (0, locale_1.t)(pattern.messageKey),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (pattern.functionCall) {
|
|
69
|
+
// Find actual function calls
|
|
70
|
+
const calls = (0, parser_1.astFindAll)(ast, (n) => isFunctionCall(n, pattern.rule));
|
|
71
|
+
for (const call of calls) {
|
|
72
|
+
if (isInQuote(call))
|
|
73
|
+
continue;
|
|
36
74
|
issues.push({
|
|
37
|
-
file,
|
|
38
|
-
|
|
75
|
+
file,
|
|
76
|
+
line: call.pos.line,
|
|
77
|
+
severity,
|
|
78
|
+
rule: pattern.rule,
|
|
79
|
+
message: (0, locale_1.t)(pattern.messageKey),
|
|
39
80
|
});
|
|
40
81
|
}
|
|
41
82
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.checkDanglingDefun = checkDanglingDefun;
|
|
37
|
+
const locale_1 = require("../locale");
|
|
38
|
+
const parser_1 = require("@atlisp/parser");
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
function checkDanglingDefun(file, allDefuns) {
|
|
41
|
+
const issues = [];
|
|
42
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
43
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
44
|
+
const localDefuns = [];
|
|
45
|
+
const defunNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
|
|
46
|
+
for (const node of defunNodes) {
|
|
47
|
+
if (node.children && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
|
|
48
|
+
localDefuns.push(node.children[1].name);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const calledSymbols = new Set();
|
|
52
|
+
const allCalls = (0, parser_1.astFindAll)(ast, (n) => {
|
|
53
|
+
if (n.type !== 'list' || !n.children || n.children.length === 0)
|
|
54
|
+
return false;
|
|
55
|
+
return (0, parser_1.astIsSymbol)(n.children[0]);
|
|
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) {
|
|
74
|
+
issues.push({
|
|
75
|
+
file,
|
|
76
|
+
line: defunNodes.find((n) => n.children && n.children.length >= 2 && (0, parser_1.astIsSymbol)(n.children[1]) && n.children[1].name === defunName).pos.line,
|
|
77
|
+
severity: 'warn',
|
|
78
|
+
rule: 'dangling_defun',
|
|
79
|
+
message: (0, locale_1.t)('dangling_defun', defunName),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return issues;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=dangling-defun.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Issue } from '../types';
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function checkDoubleNot(content: string, file: string): Issue[];
|
|
4
|
+
export declare function checkDoubleNotAst(ast: AstNode, file: string): Issue[];
|
|
5
|
+
//# sourceMappingURL=double-not.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkDoubleNot = checkDoubleNot;
|
|
4
|
+
exports.checkDoubleNotAst = checkDoubleNotAst;
|
|
5
|
+
const locale_1 = require("../locale");
|
|
6
|
+
const parser_1 = require("@atlisp/parser");
|
|
7
|
+
function checkDoubleNot(content, file) {
|
|
8
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
9
|
+
return checkDoubleNotAst(ast, file);
|
|
10
|
+
}
|
|
11
|
+
function checkDoubleNotAst(ast, file) {
|
|
12
|
+
const issues = [];
|
|
13
|
+
const notNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'not'));
|
|
14
|
+
for (const node of notNodes) {
|
|
15
|
+
if (!node.children || node.children.length < 2)
|
|
16
|
+
continue;
|
|
17
|
+
const arg = node.children[1];
|
|
18
|
+
if (!(0, parser_1.astIsList)(arg, 'not'))
|
|
19
|
+
continue;
|
|
20
|
+
issues.push({
|
|
21
|
+
file,
|
|
22
|
+
line: node.pos.line,
|
|
23
|
+
severity: 'warn',
|
|
24
|
+
rule: 'double_not',
|
|
25
|
+
message: (0, locale_1.t)('double_not'),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return issues;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=double-not.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Issue } from '../types';
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function checkEmptyBranch(content: string, file: string): Issue[];
|
|
4
|
+
export declare function checkEmptyBranchAst(ast: AstNode, file: string): Issue[];
|
|
5
|
+
//# sourceMappingURL=empty-branch.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkEmptyBranch = checkEmptyBranch;
|
|
4
|
+
exports.checkEmptyBranchAst = checkEmptyBranchAst;
|
|
5
|
+
const locale_1 = require("../locale");
|
|
6
|
+
const parser_1 = require("@atlisp/parser");
|
|
7
|
+
// AutoLISP only has `if` for conditional branching; `while` with no body is valid (no-op loop)
|
|
8
|
+
const BRANCH_FORMS = new Set(['if']);
|
|
9
|
+
function checkEmptyBranch(content, file) {
|
|
10
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
11
|
+
return checkEmptyBranchAst(ast, file);
|
|
12
|
+
}
|
|
13
|
+
function checkEmptyBranchAst(ast, file) {
|
|
14
|
+
const issues = [];
|
|
15
|
+
for (const form of BRANCH_FORMS) {
|
|
16
|
+
const nodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, form));
|
|
17
|
+
for (const node of nodes) {
|
|
18
|
+
if (!node.children)
|
|
19
|
+
continue;
|
|
20
|
+
// (if cond) — no then or else, only 2 items (form + cond)
|
|
21
|
+
if (node.children.length < 3) {
|
|
22
|
+
issues.push({
|
|
23
|
+
file,
|
|
24
|
+
line: node.pos.line,
|
|
25
|
+
severity: 'warn',
|
|
26
|
+
rule: 'empty_branch',
|
|
27
|
+
message: (0, locale_1.t)('empty_branch', `(${form} ...)`),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return issues;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=empty-branch.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkEmptyComments = checkEmptyComments;
|
|
4
|
+
const locale_1 = require("../locale");
|
|
5
|
+
function checkEmptyComments(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].trim();
|
|
10
|
+
// Check: line is a comment with only whitespace after the ;
|
|
11
|
+
if (line.startsWith(';')) {
|
|
12
|
+
const afterSemicolon = line.slice(1);
|
|
13
|
+
if (afterSemicolon.trim() === '') {
|
|
14
|
+
issues.push({
|
|
15
|
+
file,
|
|
16
|
+
line: i + 1,
|
|
17
|
+
severity: 'warn',
|
|
18
|
+
rule: 'empty_comment',
|
|
19
|
+
message: (0, locale_1.t)('empty_comment'),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return issues;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=empty-comments.js.map
|
package/dist/checks/encoding.js
CHANGED
|
@@ -6,9 +6,12 @@ function checkEncoding(content, file) {
|
|
|
6
6
|
const issues = [];
|
|
7
7
|
const raw = Buffer.from(content, 'utf-8');
|
|
8
8
|
// BOM detection
|
|
9
|
-
if (raw.length >= 3 && raw[0] ===
|
|
9
|
+
if (raw.length >= 3 && raw[0] === 0xef && raw[1] === 0xbb && raw[2] === 0xbf) {
|
|
10
10
|
issues.push({
|
|
11
|
-
file,
|
|
11
|
+
file,
|
|
12
|
+
line: 1,
|
|
13
|
+
severity: 'warn',
|
|
14
|
+
rule: 'encoding',
|
|
12
15
|
message: (0, locale_1.t)('encoding.bom'),
|
|
13
16
|
});
|
|
14
17
|
}
|
|
@@ -17,14 +20,20 @@ function checkEncoding(content, file) {
|
|
|
17
20
|
const roundtrip = Buffer.from(content, 'utf-8').toString('utf-8');
|
|
18
21
|
if (roundtrip !== content) {
|
|
19
22
|
issues.push({
|
|
20
|
-
file,
|
|
23
|
+
file,
|
|
24
|
+
line: 1,
|
|
25
|
+
severity: 'error',
|
|
26
|
+
rule: 'encoding',
|
|
21
27
|
message: (0, locale_1.t)('encoding.non_utf8_cad'),
|
|
22
28
|
});
|
|
23
29
|
}
|
|
24
30
|
}
|
|
25
31
|
catch {
|
|
26
32
|
issues.push({
|
|
27
|
-
file,
|
|
33
|
+
file,
|
|
34
|
+
line: 1,
|
|
35
|
+
severity: 'error',
|
|
36
|
+
rule: 'encoding',
|
|
28
37
|
message: (0, locale_1.t)('encoding.non_utf8'),
|
|
29
38
|
});
|
|
30
39
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { Issue } from '../types';
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
2
3
|
export declare function checkFunctionComplexity(content: string, file: string, maxLines: number, maxNesting: number): Issue[];
|
|
4
|
+
export declare function checkFunctionComplexityAst(ast: AstNode, file: string, maxLines: number, maxNesting: number): Issue[];
|
|
3
5
|
//# sourceMappingURL=function-complexity.d.ts.map
|