@atlisp/lint 0.2.11 → 0.2.13
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/atlisp-lint.schema.json +32 -2
- package/dist/cache.js +19 -9
- package/dist/checks/builtin-arg-count.d.ts +1 -0
- package/dist/checks/builtin-arg-count.js +5 -0
- package/dist/checks/dangerous-calls.js +1 -0
- package/dist/checks/dangling-defun.d.ts +7 -0
- package/dist/checks/dangling-defun.js +19 -0
- package/dist/checks/global-function-naming.js +159 -35
- package/dist/checks/missing-princ.js +2 -2
- package/dist/checks/nth-usage.d.ts +1 -0
- package/dist/checks/nth-usage.js +16 -1
- package/dist/checks/unused-package-dep.d.ts +1 -0
- package/dist/checks/unused-package-dep.js +53 -0
- package/dist/cli/collector.d.ts +7 -0
- package/dist/cli/collector.js +154 -0
- package/dist/cli/parser.d.ts +30 -0
- package/dist/cli/parser.js +119 -0
- package/dist/config.js +32 -6
- package/dist/disable.js +12 -0
- package/dist/fixes/index.d.ts +8 -0
- package/dist/fixes/index.js +200 -0
- package/dist/index.js +56 -236
- package/dist/locale.js +30 -2
- package/dist/presets.js +24 -0
- package/dist/project.js +182 -134
- package/dist/rules.d.ts +1 -0
- package/dist/rules.js +85 -0
- package/dist/runner.d.ts +2 -3
- package/dist/runner.js +46 -371
- package/dist/types.d.ts +6 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +43 -0
- package/dist/validate.js +12 -0
- package/dist/visitor-runner.d.ts +28 -0
- package/dist/visitor-runner.js +115 -1
- package/dist/visitors/equal-nil.d.ts +8 -0
- package/dist/visitors/equal-nil.js +26 -0
- package/dist/visitors/index.d.ts +5 -0
- package/dist/visitors/index.js +14 -0
- package/dist/visitors/progn-in-cond.d.ts +8 -0
- package/dist/visitors/progn-in-cond.js +33 -0
- package/dist/visitors/redundant-and-or.d.ts +8 -0
- package/dist/visitors/redundant-and-or.js +27 -0
- package/dist/visitors/redundant-car-cdr.d.ts +8 -0
- package/dist/visitors/redundant-car-cdr.js +35 -0
- package/dist/visitors/types.d.ts +13 -0
- package/dist/visitors/types.js +72 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36,7 +36,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.main = main;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
|
-
const child_process_1 = require("child_process");
|
|
40
39
|
const config_1 = require("./config");
|
|
41
40
|
const runner_1 = require("./runner");
|
|
42
41
|
const project_1 = require("./project");
|
|
@@ -49,221 +48,8 @@ const locale_1 = require("./locale");
|
|
|
49
48
|
const rules_1 = require("./rules");
|
|
50
49
|
const cache_1 = require("./cache");
|
|
51
50
|
const watch_1 = require("./watch");
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const opts = {
|
|
55
|
-
src: [],
|
|
56
|
-
test: [],
|
|
57
|
-
staged: false,
|
|
58
|
-
diff: false,
|
|
59
|
-
changed: '',
|
|
60
|
-
format: 'default',
|
|
61
|
-
init: false,
|
|
62
|
-
installHook: false,
|
|
63
|
-
fix: false,
|
|
64
|
-
hookArgs: '',
|
|
65
|
-
cache: false,
|
|
66
|
-
clearCache: false,
|
|
67
|
-
parallel: false,
|
|
68
|
-
project: false,
|
|
69
|
-
watch: false,
|
|
70
|
-
help: false,
|
|
71
|
-
version: false,
|
|
72
|
-
formatCode: false,
|
|
73
|
-
formatCheck: false,
|
|
74
|
-
sbcl: false,
|
|
75
|
-
docs: false,
|
|
76
|
-
};
|
|
77
|
-
for (let i = 0; i < argv.length; i++) {
|
|
78
|
-
switch (argv[i]) {
|
|
79
|
-
case '--src':
|
|
80
|
-
opts.src.push(argv[++i]);
|
|
81
|
-
break;
|
|
82
|
-
case '--test':
|
|
83
|
-
opts.test.push(argv[++i]);
|
|
84
|
-
break;
|
|
85
|
-
case '--config':
|
|
86
|
-
opts.config = argv[++i];
|
|
87
|
-
break;
|
|
88
|
-
case '--staged':
|
|
89
|
-
opts.staged = true;
|
|
90
|
-
break;
|
|
91
|
-
case '--diff':
|
|
92
|
-
opts.diff = true;
|
|
93
|
-
break;
|
|
94
|
-
case '--changed':
|
|
95
|
-
opts.changed = argv[++i] || 'main';
|
|
96
|
-
break;
|
|
97
|
-
case '--format':
|
|
98
|
-
opts.format = argv[++i];
|
|
99
|
-
break;
|
|
100
|
-
case '--init':
|
|
101
|
-
opts.init = true;
|
|
102
|
-
break;
|
|
103
|
-
case '--install-hook':
|
|
104
|
-
opts.installHook = true;
|
|
105
|
-
break;
|
|
106
|
-
case '--fix':
|
|
107
|
-
opts.fix = true;
|
|
108
|
-
break;
|
|
109
|
-
case '--hook-args':
|
|
110
|
-
opts.hookArgs = argv[++i];
|
|
111
|
-
break;
|
|
112
|
-
case '--cache':
|
|
113
|
-
opts.cache = true;
|
|
114
|
-
break;
|
|
115
|
-
case '--clear-cache':
|
|
116
|
-
opts.clearCache = true;
|
|
117
|
-
break;
|
|
118
|
-
case '--parallel':
|
|
119
|
-
opts.parallel = true;
|
|
120
|
-
break;
|
|
121
|
-
case '--project':
|
|
122
|
-
opts.project = true;
|
|
123
|
-
break;
|
|
124
|
-
case '--watch':
|
|
125
|
-
opts.watch = true;
|
|
126
|
-
break;
|
|
127
|
-
case '--help':
|
|
128
|
-
opts.help = true;
|
|
129
|
-
break;
|
|
130
|
-
case '--version':
|
|
131
|
-
opts.version = true;
|
|
132
|
-
break;
|
|
133
|
-
case '--format-code':
|
|
134
|
-
opts.formatCode = true;
|
|
135
|
-
break;
|
|
136
|
-
case '--format-check':
|
|
137
|
-
opts.formatCheck = true;
|
|
138
|
-
break;
|
|
139
|
-
case '--sbcl':
|
|
140
|
-
opts.sbcl = true;
|
|
141
|
-
break;
|
|
142
|
-
case '--docs':
|
|
143
|
-
opts.docs = true;
|
|
144
|
-
break;
|
|
145
|
-
default:
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return opts;
|
|
150
|
-
}
|
|
151
|
-
// Zero-dependency glob: **/*.lsp → recursive walk + regex match
|
|
152
|
-
function* walkFiles(dir, rootDir, regex) {
|
|
153
|
-
let entries;
|
|
154
|
-
try {
|
|
155
|
-
entries = fs.readdirSync(dir);
|
|
156
|
-
}
|
|
157
|
-
catch {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
for (const entry of entries) {
|
|
161
|
-
const fullPath = path.join(dir, entry);
|
|
162
|
-
let stat;
|
|
163
|
-
try {
|
|
164
|
-
stat = fs.statSync(fullPath);
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
const rel = path.relative(rootDir, fullPath).replace(/\\/g, '/');
|
|
170
|
-
if (rel.startsWith('..'))
|
|
171
|
-
continue;
|
|
172
|
-
if (stat.isDirectory()) {
|
|
173
|
-
yield* walkFiles(fullPath, rootDir, regex);
|
|
174
|
-
}
|
|
175
|
-
else if (stat.isFile()) {
|
|
176
|
-
if (regex.test(rel))
|
|
177
|
-
yield fullPath;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
function collectChangedFiles(rootDir, baseBranch) {
|
|
182
|
-
try {
|
|
183
|
-
const output = (0, child_process_1.execSync)(`git diff --name-only --diff-filter=ACM ${baseBranch}`, {
|
|
184
|
-
cwd: rootDir,
|
|
185
|
-
encoding: 'utf-8',
|
|
186
|
-
});
|
|
187
|
-
return output
|
|
188
|
-
.split('\n')
|
|
189
|
-
.map(l => l.trim())
|
|
190
|
-
.filter(l => l.endsWith('.lsp'))
|
|
191
|
-
.map(l => path.resolve(rootDir, l))
|
|
192
|
-
.filter(f => fs.existsSync(f));
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
return [];
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
// Placeholder regex (dynamically constructed to avoid no-control-regex)
|
|
199
|
-
const GLOB_PLACEHOLDER = '\x00GLOB\x00';
|
|
200
|
-
const GLOB_RE = new RegExp(GLOB_PLACEHOLDER, 'g');
|
|
201
|
-
function globFiles(pattern, rootDir) {
|
|
202
|
-
const regexStr = pattern
|
|
203
|
-
.replace(/\./g, '\\.')
|
|
204
|
-
.replace(/\*\*\//g, GLOB_PLACEHOLDER)
|
|
205
|
-
.replace(/\*\*/g, '.*')
|
|
206
|
-
.replace(/\*/g, '[^/]*')
|
|
207
|
-
.replace(/\?/g, '.')
|
|
208
|
-
.replace(GLOB_RE, '(.*/)?');
|
|
209
|
-
const regex = new RegExp(`^${regexStr}$`);
|
|
210
|
-
return Array.from(walkFiles(rootDir, rootDir, regex));
|
|
211
|
-
}
|
|
212
|
-
function collectFiles(rootDir, opts, config) {
|
|
213
|
-
const files = new Set();
|
|
214
|
-
if (opts.src.length > 0 || opts.test.length > 0) {
|
|
215
|
-
// Walk specific directories
|
|
216
|
-
for (const d of opts.src) {
|
|
217
|
-
for (const f of globFiles('**/*.lsp', path.resolve(rootDir, d))) {
|
|
218
|
-
files.add(f);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
for (const d of opts.test) {
|
|
222
|
-
for (const f of globFiles('**/*.lsp', path.resolve(rootDir, d))) {
|
|
223
|
-
files.add(f);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
// Use config globs relative to rootDir
|
|
229
|
-
for (const pattern of config.source.globs) {
|
|
230
|
-
for (const f of globFiles(pattern, rootDir)) {
|
|
231
|
-
files.add(f);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
const excludePatterns = config.source.exclude.map(e => {
|
|
236
|
-
const str = e
|
|
237
|
-
.replace(/\./g, '\\.')
|
|
238
|
-
.replace(/\*\*\//g, GLOB_PLACEHOLDER)
|
|
239
|
-
.replace(/\*\*/g, '.*')
|
|
240
|
-
.replace(/\*/g, '[^/]*')
|
|
241
|
-
.replace(GLOB_RE, '(.*/)?');
|
|
242
|
-
return new RegExp(`^${str}$`);
|
|
243
|
-
});
|
|
244
|
-
return Array.from(files)
|
|
245
|
-
.filter(f => {
|
|
246
|
-
const rel = path.relative(rootDir, f).replace(/\\/g, '/');
|
|
247
|
-
for (const re of excludePatterns) {
|
|
248
|
-
if (re.test(rel))
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
return true;
|
|
252
|
-
})
|
|
253
|
-
.sort();
|
|
254
|
-
}
|
|
255
|
-
function collectStagedFiles(rootDir) {
|
|
256
|
-
const output = (0, child_process_1.execSync)('git diff --cached --name-only --diff-filter=ACM', {
|
|
257
|
-
cwd: rootDir,
|
|
258
|
-
encoding: 'utf-8',
|
|
259
|
-
});
|
|
260
|
-
return output
|
|
261
|
-
.split('\n')
|
|
262
|
-
.map(l => l.trim())
|
|
263
|
-
.filter(l => l.endsWith('.lsp'))
|
|
264
|
-
.map(l => path.resolve(rootDir, l))
|
|
265
|
-
.filter(f => fs.existsSync(f));
|
|
266
|
-
}
|
|
51
|
+
const parser_1 = require("./cli/parser");
|
|
52
|
+
const collector_1 = require("./cli/collector");
|
|
267
53
|
function initConfig(rootDir) {
|
|
268
54
|
const defaultPath = path.join(__dirname, '..', 'atlisp-lint.default.json');
|
|
269
55
|
const targetPath = path.join(rootDir, 'atlisp-lint.json');
|
|
@@ -296,12 +82,20 @@ function printHelp() {
|
|
|
296
82
|
}
|
|
297
83
|
async function main() {
|
|
298
84
|
try {
|
|
299
|
-
const opts = parseArgs();
|
|
85
|
+
const opts = (0, parser_1.parseArgs)();
|
|
300
86
|
const rootDir = process.cwd();
|
|
301
87
|
// Load config early so locale is available for --help and other commands
|
|
302
88
|
const configPath = opts.config || path.join(rootDir, 'atlisp-lint.json');
|
|
303
89
|
const config = (0, config_1.loadConfig)(fs.existsSync(configPath) ? configPath : undefined);
|
|
304
90
|
(0, locale_1.setLocale)(config.locale || 'zh');
|
|
91
|
+
if (opts.listRules) {
|
|
92
|
+
console.log('Rule'.padEnd(30) + 'Default'.padEnd(10) + 'Category'.padEnd(14) + 'Description');
|
|
93
|
+
console.log('-'.repeat(90));
|
|
94
|
+
for (const r of rules_1.RULES) {
|
|
95
|
+
console.log(r.name.padEnd(30) + r.defaultSeverity.padEnd(10) + r.category.padEnd(14) + r.description);
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
305
99
|
if (opts.docs) {
|
|
306
100
|
console.log((0, rules_1.generateRulesMarkdown)());
|
|
307
101
|
console.log();
|
|
@@ -331,14 +125,18 @@ async function main() {
|
|
|
331
125
|
console.log((0, locale_1.t)('index.cache_cleared'));
|
|
332
126
|
return;
|
|
333
127
|
}
|
|
128
|
+
if (opts.explain) {
|
|
129
|
+
console.log((0, rules_1.generateRuleDetail)(opts.explain));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
334
132
|
// Collect files
|
|
335
133
|
const files = opts.staged
|
|
336
|
-
? collectStagedFiles(rootDir)
|
|
134
|
+
? (0, collector_1.collectStagedFiles)(rootDir)
|
|
337
135
|
: opts.diff
|
|
338
|
-
? collectChangedFiles(rootDir, 'HEAD')
|
|
136
|
+
? (0, collector_1.collectChangedFiles)(rootDir, 'HEAD')
|
|
339
137
|
: opts.changed
|
|
340
|
-
? collectChangedFiles(rootDir, opts.changed)
|
|
341
|
-
: collectFiles(rootDir, opts, config);
|
|
138
|
+
? (0, collector_1.collectChangedFiles)(rootDir, opts.changed)
|
|
139
|
+
: (0, collector_1.collectFiles)(rootDir, opts, config);
|
|
342
140
|
if (files.length === 0) {
|
|
343
141
|
console.log((0, locale_1.t)('index.no_files'));
|
|
344
142
|
return;
|
|
@@ -384,9 +182,17 @@ async function main() {
|
|
|
384
182
|
// --fix mode: auto-fix before linting
|
|
385
183
|
if (opts.fix) {
|
|
386
184
|
for (const f of files) {
|
|
185
|
+
const relFile = path.relative(rootDir, f);
|
|
186
|
+
if (opts.dryRun) {
|
|
187
|
+
const content = fs.readFileSync(f, 'utf-8');
|
|
188
|
+
const bomFix = content.charCodeAt(0) === 0xfeff;
|
|
189
|
+
if (bomFix)
|
|
190
|
+
console.log(`${(0, locale_1.t)('summary.tag_info')}: ${relFile} — would fix BOM`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
387
193
|
const fixes = (0, runner_1.fixFile)(f);
|
|
388
194
|
for (const rule of fixes) {
|
|
389
|
-
console.log(`${(0, locale_1.t)('summary.tag_fail')}: ${
|
|
195
|
+
console.log(`${(0, locale_1.t)('summary.tag_fail')}: ${relFile} — ${(0, locale_1.t)('index.fixed', rule)}`);
|
|
390
196
|
}
|
|
391
197
|
}
|
|
392
198
|
// format_indent fix for non-staged mode (no lint results available yet)
|
|
@@ -401,8 +207,8 @@ async function main() {
|
|
|
401
207
|
console.log(`${(0, locale_1.t)('summary.tag_fail')}: ${path.relative(rootDir, f)} — ${(0, locale_1.t)('index.fixed', 'format_indent')}`);
|
|
402
208
|
}
|
|
403
209
|
}
|
|
404
|
-
catch {
|
|
405
|
-
|
|
210
|
+
catch (e) {
|
|
211
|
+
console.warn(`[lint] format_indent fix failed for ${path.relative(rootDir, f)}: ${e instanceof Error ? e.message : String(e)}`);
|
|
406
212
|
}
|
|
407
213
|
}
|
|
408
214
|
}
|
|
@@ -434,9 +240,10 @@ async function main() {
|
|
|
434
240
|
return;
|
|
435
241
|
}
|
|
436
242
|
// Phase 1: Static checks (parallel or sequential)
|
|
243
|
+
const contentArg = opts.cache && contentCache.size > 0 ? contentCache : undefined;
|
|
437
244
|
const issues = opts.parallel
|
|
438
245
|
? await (0, runner_1.lintFilesParallel)(filesToLint, config, rootDir)
|
|
439
|
-
: (0, runner_1.lintFiles)(filesToLint, config, rootDir);
|
|
246
|
+
: (0, runner_1.lintFiles)(filesToLint, config, rootDir, contentArg);
|
|
440
247
|
// Phase 1b: Project-level cross-file checks
|
|
441
248
|
if (opts.project) {
|
|
442
249
|
const projectIssues = (0, project_1.lintProject)(filesToLint, config, rootDir);
|
|
@@ -452,12 +259,17 @@ async function main() {
|
|
|
452
259
|
const content = fs.readFileSync(f, 'utf-8');
|
|
453
260
|
const fixed = (0, runner_1.applyFixes)(fileIssues, content, relPath);
|
|
454
261
|
if (fixed !== content) {
|
|
455
|
-
|
|
456
|
-
|
|
262
|
+
if (opts.dryRun) {
|
|
263
|
+
console.log(`${(0, locale_1.t)('summary.tag_info')}: ${relPath} — would apply ${fileIssues.length} fix(es)`);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
fs.writeFileSync(f, fixed, 'utf-8');
|
|
267
|
+
console.log(`${(0, locale_1.t)('summary.tag_fail')}: ${relPath} — ${(0, locale_1.t)('index.fixed')}`);
|
|
268
|
+
}
|
|
457
269
|
}
|
|
458
270
|
}
|
|
459
|
-
catch {
|
|
460
|
-
|
|
271
|
+
catch (e) {
|
|
272
|
+
console.warn(`[lint] applyFixes failed for ${relPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
461
273
|
}
|
|
462
274
|
}
|
|
463
275
|
}
|
|
@@ -472,18 +284,23 @@ async function main() {
|
|
|
472
284
|
const content = fs.readFileSync(f, 'utf-8');
|
|
473
285
|
const formatted = (0, formatter_1.formatCode)(content, indentSize);
|
|
474
286
|
if (formatted !== content) {
|
|
475
|
-
|
|
476
|
-
|
|
287
|
+
if (opts.dryRun) {
|
|
288
|
+
console.log(`${(0, locale_1.t)('summary.tag_info')}: ${relPath} — would fix format_indent`);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
fs.writeFileSync(f, formatted, 'utf-8');
|
|
292
|
+
console.log(`${(0, locale_1.t)('summary.tag_fail')}: ${relPath} — ${(0, locale_1.t)('index.fixed', 'format_indent')}`);
|
|
293
|
+
}
|
|
477
294
|
}
|
|
478
295
|
}
|
|
479
|
-
catch {
|
|
480
|
-
|
|
296
|
+
catch (e) {
|
|
297
|
+
console.warn(`[lint] format_indent fix failed for ${relPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
481
298
|
}
|
|
482
299
|
}
|
|
483
300
|
}
|
|
484
301
|
}
|
|
485
302
|
}
|
|
486
|
-
// Mark as cached
|
|
303
|
+
// Mark as cached
|
|
487
304
|
if (opts.cache) {
|
|
488
305
|
for (const f of filesToLint) {
|
|
489
306
|
(0, cache_1.markCached)(f, rootDir, contentCache.get(f));
|
|
@@ -515,8 +332,8 @@ async function main() {
|
|
|
515
332
|
try {
|
|
516
333
|
fileContents.set(iss.file, fs.readFileSync(fp, 'utf-8'));
|
|
517
334
|
}
|
|
518
|
-
catch {
|
|
519
|
-
|
|
335
|
+
catch (e) {
|
|
336
|
+
console.warn(`[lint] failed to read ${fp} for display: ${e instanceof Error ? e.message : String(e)}`);
|
|
520
337
|
}
|
|
521
338
|
}
|
|
522
339
|
}
|
|
@@ -535,6 +352,9 @@ async function main() {
|
|
|
535
352
|
else {
|
|
536
353
|
console.log((0, formatters_1.formatDefault)(issues, fileContents));
|
|
537
354
|
}
|
|
355
|
+
if (opts.maxWarnings >= 0 && warningCount > opts.maxWarnings) {
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
538
358
|
process.exit(errorCount > 0 ? 1 : 0);
|
|
539
359
|
}
|
|
540
360
|
catch (err) {
|
package/dist/locale.js
CHANGED
|
@@ -105,6 +105,19 @@ const messages = {
|
|
|
105
105
|
redundant_entmake: '(entmake (list (cons ...))) should use quoted association list for performance',
|
|
106
106
|
missing_princ: "C: command '{0}' is missing trailing (princ) — may return nil to the command line",
|
|
107
107
|
global_function_naming: "Function '{0}' lacks a package prefix — consider using namespace: prefix",
|
|
108
|
+
'dangerous.eval': 'eval call — may execute arbitrary code (code injection risk)',
|
|
109
|
+
command_s: '(command ...) call — use (command-s ...) for better error handling',
|
|
110
|
+
zerop_usage: 'Use (zerop {0}) instead of comparing with zero',
|
|
111
|
+
progn_in_body: 'Redundant (progn ...) in {0} body — {0} already allows multiple forms',
|
|
112
|
+
if_progn_both: 'Both branches of if use progn — use (cond (test ...) (t ...)) instead',
|
|
113
|
+
redundant_cond: 'Single-clause cond — use (if ...) instead',
|
|
114
|
+
selection_set_leak: 'More ssget calls ({0}) than ssset calls ({1}) — possible selection set leak',
|
|
115
|
+
empty_branch_cond: 'Empty cond clause — no body forms after condition',
|
|
116
|
+
function_reference: "Function '{0}' is referenced but never defined",
|
|
117
|
+
progn_in_cond: 'Redundant (progn ...) in cond clause — cond already allows multiple forms',
|
|
118
|
+
redundant_car_cdr: '({0} ({1} x)) can be simplified to ({2} x)',
|
|
119
|
+
equal_nil: 'Use (null {0}) or (not {0}) instead of (equal {0} nil)',
|
|
120
|
+
redundant_and_or: '{0} — may be leftover code',
|
|
108
121
|
'help.text': `@atlisp/lint - AutoLISP static analysis tool
|
|
109
122
|
|
|
110
123
|
Usage: atlisp-lint [options]
|
|
@@ -131,7 +144,8 @@ Options:
|
|
|
131
144
|
--format-check Check if files are formatted (exit 1 if not)
|
|
132
145
|
--diff Only check files changed since last commit
|
|
133
146
|
--changed <branch> Only check files changed against a branch (default: main)
|
|
134
|
-
--docs Print rules documentation as markdown
|
|
147
|
+
--docs Print rules documentation as markdown
|
|
148
|
+
--list-rules List all rules with descriptions and default severities`,
|
|
135
149
|
},
|
|
136
150
|
zh: {
|
|
137
151
|
'parens.mismatch': "括号不匹配:{0} 个 '(' vs {1} 个 ')'({2})",
|
|
@@ -234,6 +248,19 @@ Options:
|
|
|
234
248
|
redundant_entmake: '(entmake (list (cons ...))) 应使用 quoted 关联表以提升性能',
|
|
235
249
|
missing_princ: "C: 命令 '{0}' 末尾缺少 (princ) — 可能在命令行返回 nil",
|
|
236
250
|
global_function_naming: "函数 '{0}' 缺少包名前缀 — 建议使用 namespace: 前缀",
|
|
251
|
+
'dangerous.eval': 'eval 调用 —— 可能执行任意代码(代码注入风险)',
|
|
252
|
+
command_s: '(command ...) 调用 —— 建议使用 (command-s ...) 以获得更好的错误处理',
|
|
253
|
+
zerop_usage: '建议使用 (zerop {0}) 替代与零的比较',
|
|
254
|
+
progn_in_body: '{0} 体中冗余的 (progn ...) —— {0} 已隐含 progn,可直接写多个表达式',
|
|
255
|
+
if_progn_both: 'if 的两个分支都使用了 progn —— 建议改用 (cond (test ...) (t ...))',
|
|
256
|
+
redundant_cond: '单子句 cond —— 建议改用 (if ...)',
|
|
257
|
+
selection_set_leak: 'ssget 调用 ({0} 次) 超过 ssset 调用 ({1} 次) —— 可能造成选择集泄漏',
|
|
258
|
+
empty_branch_cond: 'cond 子句为空 —— 条件后没有表达式',
|
|
259
|
+
function_reference: "函数 '{0}' 被引用但从未定义",
|
|
260
|
+
progn_in_cond: 'cond 子句中冗余的 (progn ...) — cond 已隐含 progn',
|
|
261
|
+
redundant_car_cdr: '({0} ({1} x)) 可简化为 ({2} x)',
|
|
262
|
+
equal_nil: '建议使用 (null {0}) 或 (not {0}) 替代 (equal {0} nil)',
|
|
263
|
+
redundant_and_or: '{0} —— 可能是残留代码',
|
|
237
264
|
'help.text': `@atlisp/lint - AutoLISP 静态分析工具
|
|
238
265
|
|
|
239
266
|
用法: atlisp-lint [选项]
|
|
@@ -260,7 +287,8 @@ Options:
|
|
|
260
287
|
--format-check 检查文件是否已格式化(未格式化则退出码为 1)
|
|
261
288
|
--diff 仅检查自上次提交后变更的文件
|
|
262
289
|
--changed <分支> 仅检查相对于某分支有变更的文件(默认:main)
|
|
263
|
-
--docs 打印规则文档(Markdown
|
|
290
|
+
--docs 打印规则文档(Markdown 格式)
|
|
291
|
+
--list-rules 列出所有规则及其描述和默认严重级别`,
|
|
264
292
|
},
|
|
265
293
|
};
|
|
266
294
|
let currentLocale = 'zh';
|
package/dist/presets.js
CHANGED
|
@@ -78,6 +78,18 @@ exports.PRESETS = {
|
|
|
78
78
|
redundant_entmake: 'warn',
|
|
79
79
|
missing_princ: 'warn',
|
|
80
80
|
global_function_naming: 'info',
|
|
81
|
+
command_s: 'warn',
|
|
82
|
+
zerop_usage: 'info',
|
|
83
|
+
progn_in_body: 'info',
|
|
84
|
+
if_progn_both: 'info',
|
|
85
|
+
redundant_cond: 'info',
|
|
86
|
+
selection_set_leak: 'warn',
|
|
87
|
+
empty_branch_cond: 'warn',
|
|
88
|
+
function_reference: 'warn',
|
|
89
|
+
progn_in_cond: 'info',
|
|
90
|
+
redundant_car_cdr: 'info',
|
|
91
|
+
equal_nil: 'info',
|
|
92
|
+
redundant_and_or: 'info',
|
|
81
93
|
},
|
|
82
94
|
},
|
|
83
95
|
strict: {
|
|
@@ -168,6 +180,18 @@ exports.PRESETS = {
|
|
|
168
180
|
redundant_entmake: 'error',
|
|
169
181
|
missing_princ: 'error',
|
|
170
182
|
global_function_naming: 'error',
|
|
183
|
+
command_s: 'error',
|
|
184
|
+
zerop_usage: 'error',
|
|
185
|
+
progn_in_body: 'error',
|
|
186
|
+
if_progn_both: 'error',
|
|
187
|
+
redundant_cond: 'error',
|
|
188
|
+
selection_set_leak: 'error',
|
|
189
|
+
empty_branch_cond: 'error',
|
|
190
|
+
function_reference: 'error',
|
|
191
|
+
progn_in_cond: 'error',
|
|
192
|
+
redundant_car_cdr: 'error',
|
|
193
|
+
equal_nil: 'error',
|
|
194
|
+
redundant_and_or: 'error',
|
|
171
195
|
},
|
|
172
196
|
},
|
|
173
197
|
relaxed: {
|