@atlisp/lint 0.1.5 → 0.1.8
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 +219 -58
- package/atlisp-lint.default.json +32 -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/format-indent.d.ts +3 -0
- package/dist/checks/format-indent.js +29 -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-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 +71 -2
- package/dist/disable.js +1 -1
- package/dist/formatter.d.ts +2 -0
- package/dist/formatter.js +51 -0
- package/dist/formatters.d.ts +1 -0
- package/dist/formatters.js +18 -2
- package/dist/index.js +172 -32
- package/dist/lib/lint-sbcl.lisp +161 -0
- package/dist/locale.js +76 -0
- package/dist/presets.d.ts +4 -0
- package/dist/presets.js +159 -0
- package/dist/project.js +37 -6
- package/dist/rules.d.ts +9 -0
- package/dist/rules.js +239 -0
- package/dist/runner.d.ts +2 -0
- package/dist/runner.js +329 -12
- 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 +126 -0
- package/dist/watch.d.ts +9 -0
- package/dist/watch.js +113 -0
- package/package.json +1 -1
package/dist/locale.js
CHANGED
|
@@ -52,6 +52,16 @@ const messages = {
|
|
|
52
52
|
quote_vs_function: 'Quoted lambda passed to {0} — use (function (lambda ...)) instead for compiled code',
|
|
53
53
|
commented_code: 'Commented-out code detected — remove or uncomment if needed',
|
|
54
54
|
double_not: 'Double (not (not ...)) — simplifies to single not',
|
|
55
|
+
error_handling: "Functions using command but missing *error*: '{0}'",
|
|
56
|
+
global_naming: "Global variable '{0}' should use *...* naming convention",
|
|
57
|
+
extra_parens: 'Suspicious excessive closing parenthesis: {0} extra )',
|
|
58
|
+
format_indent: 'Code indentation does not match expected format',
|
|
59
|
+
setq_single_arg: "Variable '{0}' is setq'd with no value — likely a typo",
|
|
60
|
+
assoc_without_cdr: 'assoc result is not used — wrap with cdr to extract the value',
|
|
61
|
+
identical_branches: 'if has identical then and else branches — possible copy-paste error',
|
|
62
|
+
while_constant: "while with constant condition '{0}' — loop may never execute or never terminate",
|
|
63
|
+
cond_duplicate: 'cond has duplicate conditions — second clause is unreachable',
|
|
64
|
+
duplicate_defun: "Function '{0}' is defined multiple times",
|
|
55
65
|
dangling_defun: "Function '{0}' is defined but never called",
|
|
56
66
|
missing_export: "Function '{0}' is not registered in @::*modules*",
|
|
57
67
|
unused_package_dep: "Imported package '{0}' is never referenced",
|
|
@@ -74,6 +84,34 @@ const messages = {
|
|
|
74
84
|
'index.cached': 'Cached, skipping',
|
|
75
85
|
'index.all_cached': 'All files are cached, nothing to lint',
|
|
76
86
|
'index.cache_cleared': 'Cache cleared',
|
|
87
|
+
'index.watching': 'Watching',
|
|
88
|
+
'index.watch_hint': 'Press Ctrl+C to stop',
|
|
89
|
+
'index.watch_status': '{0} files checked: {1} error(s), {2} warning(s)',
|
|
90
|
+
'index.format_unstable': 'Formatting is not idempotent — re-running formatCode produces different output',
|
|
91
|
+
'help.text': `@atlisp/lint - AutoLISP static analysis tool
|
|
92
|
+
|
|
93
|
+
Usage: atlisp-lint [options]
|
|
94
|
+
|
|
95
|
+
Options:
|
|
96
|
+
--src <dir> Specify source directory (may be repeated)
|
|
97
|
+
--test <dir> Specify test directory (may be repeated)
|
|
98
|
+
--config <path> Path to config file
|
|
99
|
+
--staged Only check git staged .lsp files
|
|
100
|
+
--format <format> Output format: default | json
|
|
101
|
+
--init Generate atlisp-lint.json in current directory
|
|
102
|
+
--install-hook Install git pre-commit hook
|
|
103
|
+
--fix Auto-fix trailing whitespace, BOM, trailing parens, etc.
|
|
104
|
+
--cache Enable file hash caching, skip unchanged files
|
|
105
|
+
--clear-cache Clear cache directory
|
|
106
|
+
--parallel Enable multi-threaded parallel checking
|
|
107
|
+
--project Enable cross-file analysis (Phase 1b)
|
|
108
|
+
--sbcl Enable SBCL syntax validation (Phase 2)
|
|
109
|
+
--watch Watch mode: re-check on file changes
|
|
110
|
+
--hook-args <args> Custom pre-commit hook arguments (with --install-hook)
|
|
111
|
+
--help Show this help message
|
|
112
|
+
--version Show version number
|
|
113
|
+
--format-code Auto-format code indentation
|
|
114
|
+
--format-check Check if files are formatted (exit 1 if not)`,
|
|
77
115
|
},
|
|
78
116
|
zh: {
|
|
79
117
|
'parens.mismatch': "括号不匹配:{0} 个 '(' vs {1} 个 ')'({2})",
|
|
@@ -123,6 +161,16 @@ const messages = {
|
|
|
123
161
|
quote_vs_function: '在 {0} 中使用引用 lambda —— 建议改用 (function (lambda ...)) 以支持编译优化',
|
|
124
162
|
commented_code: '检测到被注释掉的代码 —— 请删除或取消注释',
|
|
125
163
|
double_not: '双重 (not (not ...)) —— 可以简化为单个 not',
|
|
164
|
+
error_handling: "使用 command 但缺少 *error* 的函数: '{0}'",
|
|
165
|
+
global_naming: "全局变量 '{0}' 应使用 *...* 命名约定",
|
|
166
|
+
extra_parens: '可疑的过多闭合括号:多余 {0} 个 )',
|
|
167
|
+
format_indent: '代码缩进不符合格式化规范',
|
|
168
|
+
setq_single_arg: "变量 '{0}' 被 setq 但未赋值——可能是笔误",
|
|
169
|
+
assoc_without_cdr: 'assoc 的结果未被使用——应使用 cdr 获取值',
|
|
170
|
+
identical_branches: 'if 的 then 和 else 分支完全相同——可能是复制粘贴错误',
|
|
171
|
+
while_constant: "while 使用了常量条件 '{0}'——循环可能永不执行或永不终止",
|
|
172
|
+
cond_duplicate: 'cond 中存在重复条件——第二个子句不可达',
|
|
173
|
+
duplicate_defun: "函数 '{0}' 被多次定义",
|
|
126
174
|
dangling_defun: "函数 '{0}' 定义了但从未被调用",
|
|
127
175
|
missing_export: "函数 '{0}' 未注册到 @::*modules*",
|
|
128
176
|
unused_package_dep: "导入的包 '{0}' 从未被引用",
|
|
@@ -145,6 +193,34 @@ const messages = {
|
|
|
145
193
|
'index.cached': '已缓存,跳过',
|
|
146
194
|
'index.all_cached': '所有文件均已缓存,无需检查',
|
|
147
195
|
'index.cache_cleared': '缓存已清除',
|
|
196
|
+
'index.watching': '监听中',
|
|
197
|
+
'index.watch_hint': '按 Ctrl+C 停止',
|
|
198
|
+
'index.watch_status': '已检查 {0} 个文件: {1} 个错误, {2} 个警告',
|
|
199
|
+
'index.format_unstable': '格式化结果不稳定——再次格式化后输出不同',
|
|
200
|
+
'help.text': `@atlisp/lint - AutoLISP 静态分析工具
|
|
201
|
+
|
|
202
|
+
用法: atlisp-lint [选项]
|
|
203
|
+
|
|
204
|
+
选项:
|
|
205
|
+
--src <目录> 指定源代码目录(可重复)
|
|
206
|
+
--test <目录> 指定测试目录(可重复)
|
|
207
|
+
--config <路径> 配置文件路径
|
|
208
|
+
--staged 仅检查 git 暂存区的 .lsp 文件
|
|
209
|
+
--format <格式> 输出格式:default | json
|
|
210
|
+
--init 在当前目录生成 atlisp-lint.json
|
|
211
|
+
--install-hook 安装 git pre-commit 钩子
|
|
212
|
+
--fix 自动修复尾部空格、BOM、尾部括号等
|
|
213
|
+
--cache 启用文件哈希缓存,跳过未变更的文件
|
|
214
|
+
--clear-cache 清除缓存目录
|
|
215
|
+
--parallel 启用多线程并行检查
|
|
216
|
+
--project 启用跨文件分析(Phase 1b)
|
|
217
|
+
--sbcl 启用 SBCL 语法验证(Phase 2)
|
|
218
|
+
--watch 监视模式:文件变更时重新检查
|
|
219
|
+
--hook-args <参数> 自定义 pre-commit 钩子参数(与 --install-hook 配合使用)
|
|
220
|
+
--help 显示此帮助信息
|
|
221
|
+
--version 显示版本号
|
|
222
|
+
--format-code 自动格式化代码缩进
|
|
223
|
+
--format-check 检查文件是否已格式化(未格式化则退出码为 1)`,
|
|
148
224
|
},
|
|
149
225
|
};
|
|
150
226
|
let currentLocale = 'zh';
|
package/dist/presets.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PRESETS = void 0;
|
|
4
|
+
exports.PRESETS = {
|
|
5
|
+
recommended: {
|
|
6
|
+
checks: {
|
|
7
|
+
parens: 'error',
|
|
8
|
+
encoding: 'error',
|
|
9
|
+
cl_syntax: 'warn',
|
|
10
|
+
quit_exit: 'error',
|
|
11
|
+
command_shell: 'error',
|
|
12
|
+
startapp: 'warn',
|
|
13
|
+
vl_registry_write: 'warn',
|
|
14
|
+
token_in_url: 'warn',
|
|
15
|
+
open_without_close: 'warn',
|
|
16
|
+
line_length: 'warn',
|
|
17
|
+
function_complexity: 'warn',
|
|
18
|
+
parameter_naming: 'warn',
|
|
19
|
+
unused_variable: 'warn',
|
|
20
|
+
missing_doc: 'warn',
|
|
21
|
+
trailing_whitespace: 'warn',
|
|
22
|
+
unused_parameter: 'warn',
|
|
23
|
+
constant_condition: 'warn',
|
|
24
|
+
redundant_progn: 'warn',
|
|
25
|
+
empty_branch: 'warn',
|
|
26
|
+
unused_let_binding: 'warn',
|
|
27
|
+
recursive_call: 'warn',
|
|
28
|
+
variable_shadow: 'warn',
|
|
29
|
+
redundant_cond: 'warn',
|
|
30
|
+
unused_local_fun: 'warn',
|
|
31
|
+
multiple_setq: 'warn',
|
|
32
|
+
redundant_quotes: 'warn',
|
|
33
|
+
trailing_paren: 'warn',
|
|
34
|
+
empty_comment: 'warn',
|
|
35
|
+
redundant_setq: 'warn',
|
|
36
|
+
redundant_nil_else: 'warn',
|
|
37
|
+
single_arg_and_or: 'warn',
|
|
38
|
+
redundant_let: 'warn',
|
|
39
|
+
self_compare: 'warn',
|
|
40
|
+
misplaced_else: 'warn',
|
|
41
|
+
quote_vs_function: 'warn',
|
|
42
|
+
commented_code: 'warn',
|
|
43
|
+
double_not: 'warn',
|
|
44
|
+
setq_single_arg: 'warn',
|
|
45
|
+
assoc_without_cdr: 'warn',
|
|
46
|
+
identical_branches: 'warn',
|
|
47
|
+
while_constant: 'warn',
|
|
48
|
+
cond_duplicate: 'warn',
|
|
49
|
+
error_handling: 'warn',
|
|
50
|
+
global_naming: 'warn',
|
|
51
|
+
extra_parens: 'warn',
|
|
52
|
+
arg_count: 'warn',
|
|
53
|
+
strcat_usage: 'warn',
|
|
54
|
+
cond_simplify: 'warn',
|
|
55
|
+
quote_style: 'warn',
|
|
56
|
+
eq_usage: 'warn',
|
|
57
|
+
lambda_syntax: 'warn',
|
|
58
|
+
comment_style: 'warn',
|
|
59
|
+
empty_catch: 'warn',
|
|
60
|
+
nth_usage: 'warn',
|
|
61
|
+
append_single: 'warn',
|
|
62
|
+
setq_multiple: 'warn',
|
|
63
|
+
mixed_indent: 'warn',
|
|
64
|
+
long_function_call: 'warn',
|
|
65
|
+
no_return: 'warn',
|
|
66
|
+
shadow_builtin: 'warn',
|
|
67
|
+
dynamic_doc: 'warn',
|
|
68
|
+
redundant_if: 'warn',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
strict: {
|
|
72
|
+
checks: {
|
|
73
|
+
parens: 'error',
|
|
74
|
+
encoding: 'error',
|
|
75
|
+
cl_syntax: 'error',
|
|
76
|
+
quit_exit: 'error',
|
|
77
|
+
command_shell: 'error',
|
|
78
|
+
startapp: 'error',
|
|
79
|
+
vl_registry_write: 'error',
|
|
80
|
+
vlax_without_loading: 'error',
|
|
81
|
+
token_in_url: 'error',
|
|
82
|
+
open_without_close: 'error',
|
|
83
|
+
bare_function_names: 'error',
|
|
84
|
+
line_length: 'error',
|
|
85
|
+
function_complexity: 'error',
|
|
86
|
+
parameter_naming: 'error',
|
|
87
|
+
unused_variable: 'error',
|
|
88
|
+
missing_doc: 'error',
|
|
89
|
+
trailing_whitespace: 'error',
|
|
90
|
+
unused_parameter: 'error',
|
|
91
|
+
constant_condition: 'error',
|
|
92
|
+
redundant_progn: 'error',
|
|
93
|
+
empty_branch: 'error',
|
|
94
|
+
unused_let_binding: 'error',
|
|
95
|
+
recursive_call: 'error',
|
|
96
|
+
variable_shadow: 'error',
|
|
97
|
+
redundant_cond: 'error',
|
|
98
|
+
unused_local_fun: 'error',
|
|
99
|
+
multiple_setq: 'warn',
|
|
100
|
+
redundant_quotes: 'error',
|
|
101
|
+
trailing_paren: 'error',
|
|
102
|
+
empty_comment: 'error',
|
|
103
|
+
redundant_setq: 'error',
|
|
104
|
+
redundant_nil_else: 'error',
|
|
105
|
+
single_arg_and_or: 'error',
|
|
106
|
+
redundant_let: 'error',
|
|
107
|
+
self_compare: 'error',
|
|
108
|
+
misplaced_else: 'error',
|
|
109
|
+
quote_vs_function: 'error',
|
|
110
|
+
commented_code: 'error',
|
|
111
|
+
double_not: 'error',
|
|
112
|
+
setq_single_arg: 'error',
|
|
113
|
+
assoc_without_cdr: 'error',
|
|
114
|
+
identical_branches: 'error',
|
|
115
|
+
while_constant: 'error',
|
|
116
|
+
cond_duplicate: 'error',
|
|
117
|
+
duplicate_defun: 'error',
|
|
118
|
+
dangling_defun: 'error',
|
|
119
|
+
missing_export: 'error',
|
|
120
|
+
unused_package_dep: 'error',
|
|
121
|
+
error_handling: 'error',
|
|
122
|
+
global_naming: 'error',
|
|
123
|
+
extra_parens: 'error',
|
|
124
|
+
arg_count: 'error',
|
|
125
|
+
strcat_usage: 'error',
|
|
126
|
+
cond_simplify: 'error',
|
|
127
|
+
quote_style: 'error',
|
|
128
|
+
eq_usage: 'error',
|
|
129
|
+
lambda_syntax: 'error',
|
|
130
|
+
comment_style: 'error',
|
|
131
|
+
empty_catch: 'error',
|
|
132
|
+
nth_usage: 'error',
|
|
133
|
+
append_single: 'error',
|
|
134
|
+
setq_multiple: 'error',
|
|
135
|
+
function_order: 'warn',
|
|
136
|
+
magic_number: 'warn',
|
|
137
|
+
mixed_indent: 'error',
|
|
138
|
+
long_function_call: 'error',
|
|
139
|
+
no_return: 'error',
|
|
140
|
+
shadow_builtin: 'error',
|
|
141
|
+
dynamic_doc: 'error',
|
|
142
|
+
loop_optimization: 'warn',
|
|
143
|
+
type_check: 'warn',
|
|
144
|
+
format_indent: 'warn',
|
|
145
|
+
redundant_if: 'error',
|
|
146
|
+
module_registration: 'error',
|
|
147
|
+
namespace_header: 'error',
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
relaxed: {
|
|
151
|
+
checks: {
|
|
152
|
+
parens: 'error',
|
|
153
|
+
encoding: 'error',
|
|
154
|
+
quit_exit: 'error',
|
|
155
|
+
command_shell: 'error',
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
//# sourceMappingURL=presets.js.map
|
package/dist/project.js
CHANGED
|
@@ -40,12 +40,13 @@ const parser_1 = require("@atlisp/parser");
|
|
|
40
40
|
const dangling_defun_1 = require("./checks/dangling-defun");
|
|
41
41
|
const missing_export_1 = require("./checks/missing-export");
|
|
42
42
|
const unused_package_dep_1 = require("./checks/unused-package-dep");
|
|
43
|
+
const duplicate_defun_1 = require("./checks/duplicate-defun");
|
|
43
44
|
const locale_1 = require("./locale");
|
|
44
45
|
function collectDefuns(file) {
|
|
45
46
|
const content = fs.readFileSync(file, 'utf-8');
|
|
46
47
|
const ast = (0, parser_1.parseAst)(content);
|
|
47
48
|
const results = [];
|
|
48
|
-
const defunNodes = (0, parser_1.astFindAll)(ast,
|
|
49
|
+
const defunNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
|
|
49
50
|
for (const node of defunNodes) {
|
|
50
51
|
if (node.children && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
|
|
51
52
|
results.push({ name: node.children[1].name, line: node.pos.line });
|
|
@@ -53,10 +54,27 @@ function collectDefuns(file) {
|
|
|
53
54
|
}
|
|
54
55
|
return results;
|
|
55
56
|
}
|
|
57
|
+
function collectReferences(file, _filepath) {
|
|
58
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
59
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
60
|
+
const results = [];
|
|
61
|
+
const allCalls = (0, parser_1.astFindAll)(ast, n => {
|
|
62
|
+
if (n.type !== 'list' || !n.children || n.children.length === 0)
|
|
63
|
+
return false;
|
|
64
|
+
return (0, parser_1.astIsSymbol)(n.children[0]);
|
|
65
|
+
});
|
|
66
|
+
for (const call of allCalls) {
|
|
67
|
+
const name = call.children[0].name;
|
|
68
|
+
if (name !== 'defun' && name !== 'defun-q' && !name.startsWith('c:') && !name.includes(':')) {
|
|
69
|
+
results.push({ name, line: call.pos.line });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
56
74
|
function lintProject(files, config, rootDir) {
|
|
57
75
|
(0, locale_1.setLocale)(config.locale || 'zh');
|
|
58
76
|
const allIssues = [];
|
|
59
|
-
const
|
|
77
|
+
const symbols = { defuns: new Map(), references: new Map() };
|
|
60
78
|
const fileContents = new Map();
|
|
61
79
|
for (const filepath of files) {
|
|
62
80
|
try {
|
|
@@ -70,9 +88,15 @@ function lintProject(files, config, rootDir) {
|
|
|
70
88
|
for (const [filepath] of fileContents) {
|
|
71
89
|
const defunList = collectDefuns(filepath);
|
|
72
90
|
for (const d of defunList) {
|
|
73
|
-
const list =
|
|
91
|
+
const list = symbols.defuns.get(d.name) || [];
|
|
74
92
|
list.push({ file: filepath, line: d.line });
|
|
75
|
-
|
|
93
|
+
symbols.defuns.set(d.name, list);
|
|
94
|
+
}
|
|
95
|
+
const refList = collectReferences(filepath, filepath);
|
|
96
|
+
for (const r of refList) {
|
|
97
|
+
const list = symbols.references.get(r.name) || [];
|
|
98
|
+
list.push({ file: filepath, line: r.line });
|
|
99
|
+
symbols.references.set(r.name, list);
|
|
76
100
|
}
|
|
77
101
|
}
|
|
78
102
|
for (const [filepath] of fileContents) {
|
|
@@ -80,7 +104,7 @@ function lintProject(files, config, rootDir) {
|
|
|
80
104
|
const override = findProjectOverride(filepath);
|
|
81
105
|
const checks = override?.checks || config.checks;
|
|
82
106
|
if (checks['dangling_defun'] !== 'off') {
|
|
83
|
-
const danglingIssues = (0, dangling_defun_1.checkDanglingDefun)(filepath, defuns.
|
|
107
|
+
const danglingIssues = (0, dangling_defun_1.checkDanglingDefun)(filepath, symbols.defuns, symbols.references);
|
|
84
108
|
for (const iss of danglingIssues) {
|
|
85
109
|
iss.file = relPath;
|
|
86
110
|
allIssues.push(iss);
|
|
@@ -89,7 +113,7 @@ function lintProject(files, config, rootDir) {
|
|
|
89
113
|
if (checks['missing_export'] !== 'off') {
|
|
90
114
|
const pkgDir = findPackageDir(filepath);
|
|
91
115
|
if (pkgDir) {
|
|
92
|
-
const missingIssues = (0, missing_export_1.checkMissingExport)(filepath, pkgDir,
|
|
116
|
+
const missingIssues = (0, missing_export_1.checkMissingExport)(filepath, pkgDir, symbols.defuns);
|
|
93
117
|
for (const iss of missingIssues) {
|
|
94
118
|
iss.file = relPath;
|
|
95
119
|
allIssues.push(iss);
|
|
@@ -103,6 +127,13 @@ function lintProject(files, config, rootDir) {
|
|
|
103
127
|
allIssues.push(iss);
|
|
104
128
|
}
|
|
105
129
|
}
|
|
130
|
+
if (checks['duplicate_defun'] !== 'off') {
|
|
131
|
+
const dupIssues = (0, duplicate_defun_1.checkDuplicateDefun)(filepath, symbols.defuns);
|
|
132
|
+
for (const iss of dupIssues) {
|
|
133
|
+
iss.file = relPath;
|
|
134
|
+
allIssues.push(iss);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
106
137
|
}
|
|
107
138
|
return allIssues;
|
|
108
139
|
}
|
package/dist/rules.d.ts
ADDED
package/dist/rules.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULES = void 0;
|
|
4
|
+
exports.generateRulesMarkdown = generateRulesMarkdown;
|
|
5
|
+
exports.RULES = [
|
|
6
|
+
{
|
|
7
|
+
name: 'append_single',
|
|
8
|
+
defaultSeverity: 'warn',
|
|
9
|
+
description: '检测 (append (list ...)) 推荐使用 cons',
|
|
10
|
+
category: '风格',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'arg_count',
|
|
14
|
+
defaultSeverity: 'warn',
|
|
15
|
+
description: '检查函数调用参数数量是否与定义匹配',
|
|
16
|
+
category: '最佳实践',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'assoc_without_cdr',
|
|
20
|
+
defaultSeverity: 'warn',
|
|
21
|
+
description: '检测 assoc 结果未用 cdr 提取值',
|
|
22
|
+
category: '正确性',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'bare_function_names',
|
|
26
|
+
defaultSeverity: 'off',
|
|
27
|
+
description: '检测缺少命名空间前缀的 defun',
|
|
28
|
+
category: '风格',
|
|
29
|
+
},
|
|
30
|
+
{ name: 'cl_syntax', defaultSeverity: 'warn', description: '检测 Common Lisp 特有语法(如 &key)', category: '语法' },
|
|
31
|
+
{
|
|
32
|
+
name: 'command_shell',
|
|
33
|
+
defaultSeverity: 'error',
|
|
34
|
+
description: '检测 command shell 调用(命令注入风险)',
|
|
35
|
+
category: '安全',
|
|
36
|
+
},
|
|
37
|
+
{ name: 'comment_style', defaultSeverity: 'warn', description: '检测 ; 后缺少空格的注释', category: '风格' },
|
|
38
|
+
{ name: 'commented_code', defaultSeverity: 'warn', description: '检测被注释掉的代码', category: '风格' },
|
|
39
|
+
{ name: 'cond_simplify', defaultSeverity: 'warn', description: '检测可简化为 if 的单分支 cond', category: '风格' },
|
|
40
|
+
{
|
|
41
|
+
name: 'constant_condition',
|
|
42
|
+
defaultSeverity: 'warn',
|
|
43
|
+
description: '检测 if/while/cond 中使用的常量条件',
|
|
44
|
+
category: '风格',
|
|
45
|
+
},
|
|
46
|
+
{ name: 'cond_duplicate', defaultSeverity: 'warn', description: '检测 cond 中的重复条件', category: '正确性' },
|
|
47
|
+
{
|
|
48
|
+
name: 'dangling_defun',
|
|
49
|
+
defaultSeverity: 'warn',
|
|
50
|
+
description: '检测定义了但从未被调用的函数(跨文件)',
|
|
51
|
+
category: '正确性',
|
|
52
|
+
},
|
|
53
|
+
{ name: 'duplicate_defun', defaultSeverity: 'warn', description: '检测多次定义的函数名', category: '正确性' },
|
|
54
|
+
{
|
|
55
|
+
name: 'double_not',
|
|
56
|
+
defaultSeverity: 'warn',
|
|
57
|
+
description: '检测双重 (not (not ...)) 可简化为单个 not',
|
|
58
|
+
category: '风格',
|
|
59
|
+
},
|
|
60
|
+
{ name: 'dynamic_doc', defaultSeverity: 'warn', description: '检测 C: 命令是否缺少 (princ)', category: '最佳实践' },
|
|
61
|
+
{ name: 'empty_branch', defaultSeverity: 'warn', description: '检测 if/cond 中为空的分支', category: '风格' },
|
|
62
|
+
{
|
|
63
|
+
name: 'empty_catch',
|
|
64
|
+
defaultSeverity: 'warn',
|
|
65
|
+
description: '检测未检查结果的 vl-catch-all-apply',
|
|
66
|
+
category: '最佳实践',
|
|
67
|
+
},
|
|
68
|
+
{ name: 'empty_comment', defaultSeverity: 'warn', description: '检测空注释行(只有分号没有内容)', category: '风格' },
|
|
69
|
+
{ name: 'encoding', defaultSeverity: 'error', description: '检查 UTF-8 BOM 和非 UTF-8 编码', category: '编码' },
|
|
70
|
+
{ name: 'eq_usage', defaultSeverity: 'warn', description: '检测 eq 与数字比较', category: '最佳实践' },
|
|
71
|
+
{
|
|
72
|
+
name: 'error_handling',
|
|
73
|
+
defaultSeverity: 'warn',
|
|
74
|
+
description: '检测使用 command 但缺少 *error* 处理',
|
|
75
|
+
category: '最佳实践',
|
|
76
|
+
},
|
|
77
|
+
{ name: 'extra_parens', defaultSeverity: 'warn', description: '检测可疑的连续右括号', category: '语法' },
|
|
78
|
+
{
|
|
79
|
+
name: 'function_complexity',
|
|
80
|
+
defaultSeverity: 'warn',
|
|
81
|
+
description: '检查函数行数和嵌套深度是否超限',
|
|
82
|
+
category: '复杂度',
|
|
83
|
+
},
|
|
84
|
+
{ name: 'function_order', defaultSeverity: 'off', description: '检测函数定义在使用之后', category: '风格' },
|
|
85
|
+
{ name: 'format_indent', defaultSeverity: 'off', description: '检测代码缩进是否符合格式化规范', category: '风格' },
|
|
86
|
+
{
|
|
87
|
+
name: 'identical_branches',
|
|
88
|
+
defaultSeverity: 'warn',
|
|
89
|
+
description: '检测 if 中 then 和 else 分支完全相同',
|
|
90
|
+
category: '正确性',
|
|
91
|
+
},
|
|
92
|
+
{ name: 'global_naming', defaultSeverity: 'warn', description: '检测全局变量是否使用 *...* 命名', category: '风格' },
|
|
93
|
+
{
|
|
94
|
+
name: 'lambda_syntax',
|
|
95
|
+
defaultSeverity: 'warn',
|
|
96
|
+
description: '检测 (function (lambda ...)) 建议使用缩写',
|
|
97
|
+
category: '风格',
|
|
98
|
+
},
|
|
99
|
+
{ name: 'line_length', defaultSeverity: 'warn', description: '检查行长度是否超过限制', category: '风格' },
|
|
100
|
+
{ name: 'long_function_call', defaultSeverity: 'warn', description: '检测单行参数过多的函数调用', category: '风格' },
|
|
101
|
+
{
|
|
102
|
+
name: 'loop_optimization',
|
|
103
|
+
defaultSeverity: 'off',
|
|
104
|
+
description: '检测可优化的 while/assoc 循环',
|
|
105
|
+
category: '最佳实践',
|
|
106
|
+
},
|
|
107
|
+
{ name: 'magic_number', defaultSeverity: 'off', description: '检测硬编码的魔法数字', category: '风格' },
|
|
108
|
+
{
|
|
109
|
+
name: 'misplaced_else',
|
|
110
|
+
defaultSeverity: 'warn',
|
|
111
|
+
description: '检测 (if (not ...) ...) 建议交换分支',
|
|
112
|
+
category: '风格',
|
|
113
|
+
},
|
|
114
|
+
{ name: 'missing_doc', defaultSeverity: 'warn', description: '检测缺少注释说明的函数', category: '风格' },
|
|
115
|
+
{ name: 'missing_export', defaultSeverity: 'warn', description: '检测函数未注册到 @::*modules*', category: '架构' },
|
|
116
|
+
{ name: 'mixed_indent', defaultSeverity: 'warn', description: '检测缩进中混用 tab 和空格', category: '风格' },
|
|
117
|
+
{
|
|
118
|
+
name: 'module_registration',
|
|
119
|
+
defaultSeverity: 'off',
|
|
120
|
+
description: '检测模块文件是否注册到 *modules*',
|
|
121
|
+
category: '架构',
|
|
122
|
+
},
|
|
123
|
+
{ name: 'multiple_setq', defaultSeverity: 'warn', description: '检测多个连续 setq 可以合并', category: '风格' },
|
|
124
|
+
{
|
|
125
|
+
name: 'namespace_header',
|
|
126
|
+
defaultSeverity: 'off',
|
|
127
|
+
description: '检测文件是否包含 (in-package ...) 头',
|
|
128
|
+
category: '架构',
|
|
129
|
+
},
|
|
130
|
+
{ name: 'no_return', defaultSeverity: 'warn', description: '检测没有返回值的函数', category: '最佳实践' },
|
|
131
|
+
{ name: 'nth_usage', defaultSeverity: 'warn', description: '检测 (nth 0 ...) 推荐使用 car', category: '风格' },
|
|
132
|
+
{
|
|
133
|
+
name: 'open_without_close',
|
|
134
|
+
defaultSeverity: 'warn',
|
|
135
|
+
description: '检测 open/close 数量不匹配(资源泄露)',
|
|
136
|
+
category: '最佳实践',
|
|
137
|
+
},
|
|
138
|
+
{ name: 'parameter_naming', defaultSeverity: 'warn', description: '检查参数名是否首字母大写', category: '风格' },
|
|
139
|
+
{ name: 'parens', defaultSeverity: 'error', description: '检查括号是否匹配(考虑字符串和注释)', category: '语法' },
|
|
140
|
+
{
|
|
141
|
+
name: 'quit_exit',
|
|
142
|
+
defaultSeverity: 'error',
|
|
143
|
+
description: '检测 quit/exit 调用(会终止 CAD 进程)',
|
|
144
|
+
category: '安全',
|
|
145
|
+
},
|
|
146
|
+
{ name: 'quote_style', defaultSeverity: 'warn', description: '检测 (quote ...) 建议使用缩写', category: '风格' },
|
|
147
|
+
{
|
|
148
|
+
name: 'quote_vs_function',
|
|
149
|
+
defaultSeverity: 'warn',
|
|
150
|
+
description: '检测在 mapcar/apply 中使用引用 lambda 而非 function',
|
|
151
|
+
category: '风格',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'recursive_call',
|
|
155
|
+
defaultSeverity: 'warn',
|
|
156
|
+
description: '检测函数调用自身(可能无限递归)',
|
|
157
|
+
category: '正确性',
|
|
158
|
+
},
|
|
159
|
+
{ name: 'redundant_cond', defaultSeverity: 'warn', description: '检测单子句 cond 或末尾 T 子句', category: '风格' },
|
|
160
|
+
{ name: 'redundant_if', defaultSeverity: 'warn', description: '检测 if/when 分支中冗余 progn', category: '风格' },
|
|
161
|
+
{ name: 'redundant_let', defaultSeverity: 'warn', description: '检测无绑定的 let 建议改用 progn', category: '风格' },
|
|
162
|
+
{ name: 'redundant_nil_else', defaultSeverity: 'warn', description: '检测 if 中冗余的 nil 分支', category: '风格' },
|
|
163
|
+
{ name: 'redundant_progn', defaultSeverity: 'warn', description: '检测分支中多余的 progn', category: '风格' },
|
|
164
|
+
{ name: 'redundant_quotes', defaultSeverity: 'warn', description: "检测冗余双引号(如 ''x)", category: '风格' },
|
|
165
|
+
{ name: 'redundant_setq', defaultSeverity: 'warn', description: '检测 setq 变量赋值为自身', category: '风格' },
|
|
166
|
+
{
|
|
167
|
+
name: 'self_compare',
|
|
168
|
+
defaultSeverity: 'warn',
|
|
169
|
+
description: '检测自比较表达式(恒为 T 或 nil)',
|
|
170
|
+
category: '正确性',
|
|
171
|
+
},
|
|
172
|
+
{ name: 'setq_single_arg', defaultSeverity: 'warn', description: '检测 setq 只有变量名缺少值', category: '正确性' },
|
|
173
|
+
{ name: 'setq_multiple', defaultSeverity: 'warn', description: '检测多个连续 setq 可以合并', category: '风格' },
|
|
174
|
+
{ name: 'shadow_builtin', defaultSeverity: 'warn', description: '检测覆盖内置函数的 defun', category: '最佳实践' },
|
|
175
|
+
{ name: 'single_arg_and_or', defaultSeverity: 'warn', description: '检测单参数 and/or 建议简化', category: '风格' },
|
|
176
|
+
{ name: 'startapp', defaultSeverity: 'warn', description: '检测 startapp 调用(启动外部程序)', category: '安全' },
|
|
177
|
+
{
|
|
178
|
+
name: 'strcat_usage',
|
|
179
|
+
defaultSeverity: 'warn',
|
|
180
|
+
description: '检测是否使用 + 而非 strcat 拼接字符串',
|
|
181
|
+
category: '最佳实践',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'token_in_url',
|
|
185
|
+
defaultSeverity: 'warn',
|
|
186
|
+
description: '检测字符串中的 token= 参数(凭据泄露)',
|
|
187
|
+
category: '安全',
|
|
188
|
+
},
|
|
189
|
+
{ name: 'trailing_paren', defaultSeverity: 'warn', description: '检测多余闭合括号', category: '语法' },
|
|
190
|
+
{ name: 'trailing_whitespace', defaultSeverity: 'warn', description: '检测行尾多余空格', category: '风格' },
|
|
191
|
+
{ name: 'type_check', defaultSeverity: 'off', description: '检测 getvar 结果未做类型检查', category: '最佳实践' },
|
|
192
|
+
{
|
|
193
|
+
name: 'unused_let_binding',
|
|
194
|
+
defaultSeverity: 'warn',
|
|
195
|
+
description: '检测 let 绑定但未使用的变量',
|
|
196
|
+
category: '最佳实践',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: 'unused_local_fun',
|
|
200
|
+
defaultSeverity: 'warn',
|
|
201
|
+
description: '检测 defun / 后定义的局部函数/变量未使用',
|
|
202
|
+
category: '最佳实践',
|
|
203
|
+
},
|
|
204
|
+
{ name: 'unused_package_dep', defaultSeverity: 'warn', description: '检测导入的包从未被引用', category: '架构' },
|
|
205
|
+
{ name: 'unused_parameter', defaultSeverity: 'warn', description: '检测函数中未使用的参数', category: '最佳实践' },
|
|
206
|
+
{ name: 'unused_variable', defaultSeverity: 'warn', description: '检测被赋值但未使用的变量', category: '最佳实践' },
|
|
207
|
+
{
|
|
208
|
+
name: 'variable_shadow',
|
|
209
|
+
defaultSeverity: 'warn',
|
|
210
|
+
description: '检测 setq 覆盖了 let 绑定的同名变量',
|
|
211
|
+
category: '最佳实践',
|
|
212
|
+
},
|
|
213
|
+
{ name: 'while_constant', defaultSeverity: 'warn', description: '检测 while 使用常量条件', category: '正确性' },
|
|
214
|
+
{
|
|
215
|
+
name: 'vl_registry_write',
|
|
216
|
+
defaultSeverity: 'warn',
|
|
217
|
+
description: '检测 vl-registry-write(修改注册表)',
|
|
218
|
+
category: '安全',
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'vlax_without_loading',
|
|
222
|
+
defaultSeverity: 'off',
|
|
223
|
+
description: '检测 vlax-* 使用前未调用 vl-load-com',
|
|
224
|
+
category: '最佳实践',
|
|
225
|
+
},
|
|
226
|
+
];
|
|
227
|
+
function generateRulesMarkdown() {
|
|
228
|
+
const lines = [
|
|
229
|
+
'# @atlisp/lint Rules',
|
|
230
|
+
'',
|
|
231
|
+
'| Rule | Default | Category | Description |',
|
|
232
|
+
'|------|---------|----------|-------------|',
|
|
233
|
+
];
|
|
234
|
+
for (const r of exports.RULES) {
|
|
235
|
+
lines.push(`| \`${r.name}\` | ${r.defaultSeverity} | ${r.category} | ${r.description} |`);
|
|
236
|
+
}
|
|
237
|
+
return lines.join('\n');
|
|
238
|
+
}
|
|
239
|
+
//# sourceMappingURL=rules.js.map
|
package/dist/runner.d.ts
CHANGED
|
@@ -4,5 +4,7 @@ export declare function runChecks(content: string, file: string, config: LintCon
|
|
|
4
4
|
export declare function runChecksWithVisitor(content: string, file: string, config: LintConfig): Issue[];
|
|
5
5
|
export declare function lintFiles(files: string[], config: LintConfig, rootDir: string): Issue[];
|
|
6
6
|
export declare function lintFilesParallel(files: string[], config: LintConfig, rootDir: string): Promise<Issue[]>;
|
|
7
|
+
export declare function applyFixes(issues: Issue[], content: string, filepath: string): string;
|
|
8
|
+
export declare function parseIgnoreFile(rootDir: string): string[];
|
|
7
9
|
export declare function fixFile(filepath: string): string[];
|
|
8
10
|
//# sourceMappingURL=runner.d.ts.map
|