@atlisp/lint 0.2.12 → 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.
Files changed (44) hide show
  1. package/atlisp-lint.schema.json +27 -4
  2. package/dist/cache.js +19 -9
  3. package/dist/checks/builtin-arg-count.d.ts +1 -0
  4. package/dist/checks/builtin-arg-count.js +5 -0
  5. package/dist/checks/dangerous-calls.js +1 -0
  6. package/dist/checks/global-function-naming.js +159 -35
  7. package/dist/checks/missing-princ.js +2 -2
  8. package/dist/checks/nth-usage.d.ts +1 -0
  9. package/dist/checks/nth-usage.js +16 -1
  10. package/dist/cli/collector.d.ts +7 -0
  11. package/dist/cli/collector.js +154 -0
  12. package/dist/cli/parser.d.ts +30 -0
  13. package/dist/cli/parser.js +119 -0
  14. package/dist/config.js +28 -6
  15. package/dist/disable.js +12 -0
  16. package/dist/fixes/index.d.ts +8 -0
  17. package/dist/fixes/index.js +200 -0
  18. package/dist/index.js +56 -236
  19. package/dist/locale.js +30 -2
  20. package/dist/presets.js +24 -0
  21. package/dist/project.js +64 -34
  22. package/dist/rules.d.ts +1 -0
  23. package/dist/rules.js +85 -0
  24. package/dist/runner.d.ts +2 -3
  25. package/dist/runner.js +46 -371
  26. package/dist/types.d.ts +1 -0
  27. package/dist/utils.d.ts +4 -0
  28. package/dist/utils.js +43 -0
  29. package/dist/validate.js +12 -0
  30. package/dist/visitor-runner.d.ts +28 -0
  31. package/dist/visitor-runner.js +115 -1
  32. package/dist/visitors/equal-nil.d.ts +8 -0
  33. package/dist/visitors/equal-nil.js +26 -0
  34. package/dist/visitors/index.d.ts +5 -0
  35. package/dist/visitors/index.js +14 -0
  36. package/dist/visitors/progn-in-cond.d.ts +8 -0
  37. package/dist/visitors/progn-in-cond.js +33 -0
  38. package/dist/visitors/redundant-and-or.d.ts +8 -0
  39. package/dist/visitors/redundant-and-or.js +27 -0
  40. package/dist/visitors/redundant-car-cdr.d.ts +8 -0
  41. package/dist/visitors/redundant-car-cdr.js +35 -0
  42. package/dist/visitors/types.d.ts +13 -0
  43. package/dist/visitors/types.js +72 -0
  44. package/package.json +1 -1
package/dist/rules.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RULES = void 0;
4
4
  exports.generateRulesMarkdown = generateRulesMarkdown;
5
5
  exports.generateSchemaHint = generateSchemaHint;
6
+ exports.generateRuleDetail = generateRuleDetail;
6
7
  exports.RULES = [
7
8
  {
8
9
  name: 'append_single',
@@ -281,6 +282,78 @@ exports.RULES = [
281
282
  description: '检测 defun 函数名缺少包名前缀',
282
283
  category: '风格',
283
284
  },
285
+ {
286
+ name: 'command_s',
287
+ defaultSeverity: 'warn',
288
+ description: '检测 (command ...) 调用,推荐 (command-s ...)',
289
+ category: '最佳实践',
290
+ },
291
+ {
292
+ name: 'zerop_usage',
293
+ defaultSeverity: 'info',
294
+ description: '检测 (= x 0),推荐 (zerop x)',
295
+ category: '风格',
296
+ },
297
+ {
298
+ name: 'progn_in_body',
299
+ defaultSeverity: 'info',
300
+ description: '检测 while/repeat/foreach 体中冗余 progn',
301
+ category: '风格',
302
+ },
303
+ {
304
+ name: 'if_progn_both',
305
+ defaultSeverity: 'info',
306
+ description: '检测 if 两个分支都使用 progn,推荐 cond',
307
+ category: '风格',
308
+ },
309
+ {
310
+ name: 'redundant_cond',
311
+ defaultSeverity: 'info',
312
+ description: '检测单子句 cond,推荐 if',
313
+ category: '风格',
314
+ },
315
+ {
316
+ name: 'selection_set_leak',
317
+ defaultSeverity: 'warn',
318
+ description: '检测 ssget/ssset 次数不匹配',
319
+ category: '正确性',
320
+ },
321
+ {
322
+ name: 'empty_branch_cond',
323
+ defaultSeverity: 'warn',
324
+ description: '检测 cond 子句没有表达式',
325
+ category: '正确性',
326
+ },
327
+ {
328
+ name: 'function_reference',
329
+ defaultSeverity: 'warn',
330
+ description: '检测函数被引用但从未定义(跨文件)',
331
+ category: '正确性',
332
+ },
333
+ {
334
+ name: 'progn_in_cond',
335
+ defaultSeverity: 'info',
336
+ description: '检测 cond 子句中冗余 progn',
337
+ category: '风格',
338
+ },
339
+ {
340
+ name: 'redundant_car_cdr',
341
+ defaultSeverity: 'info',
342
+ description: '检测 (car (car x)) 等可简化为 (caar x)',
343
+ category: '风格',
344
+ },
345
+ {
346
+ name: 'equal_nil',
347
+ defaultSeverity: 'info',
348
+ description: '检测 (equal x nil) 建议使用 (null x)',
349
+ category: '风格',
350
+ },
351
+ {
352
+ name: 'redundant_and_or',
353
+ defaultSeverity: 'info',
354
+ description: '检测无参数的 (and) 或 (or)——可能是残留代码',
355
+ category: '风格',
356
+ },
284
357
  ];
285
358
  function generateRulesMarkdown() {
286
359
  const lines = [
@@ -300,4 +373,16 @@ function generateSchemaHint() {
300
373
 
301
374
  Total rules: ${exports.RULES.length}`;
302
375
  }
376
+ function generateRuleDetail(ruleName) {
377
+ const rule = exports.RULES.find(r => r.name === ruleName);
378
+ if (!rule)
379
+ return `Unknown rule: ${ruleName}. Use --list-rules to see all rules.`;
380
+ const lines = [
381
+ `Rule: ${rule.name}`,
382
+ `Severity: ${rule.defaultSeverity}`,
383
+ `Category: ${rule.category}`,
384
+ `Description: ${rule.description}`,
385
+ ];
386
+ return lines.join('\n');
387
+ }
303
388
  //# sourceMappingURL=rules.js.map
package/dist/runner.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  import { Issue, LintConfig } from './types';
2
2
  export declare function runChecks(content: string, file: string, config: LintConfig): Issue[];
3
- export declare function lintFiles(files: string[], config: LintConfig, rootDir: string): Issue[];
3
+ export declare function lintFiles(files: string[], config: LintConfig, rootDir: string, contentCache?: Map<string, string>): Issue[];
4
4
  export declare function lintFilesParallel(files: string[], config: LintConfig, rootDir: string): Promise<Issue[]>;
5
- export declare const FIXABLE_RULES: Set<string>;
6
- export declare function applyFixes(issues: Issue[], content: string, filepath: string): string;
5
+ export { applyFixesFromProviders as applyFixes, FIXABLE_RULES } from './fixes/index';
7
6
  export declare function parseIgnoreFile(rootDir: string): string[];
8
7
  export declare function fixFile(filepath: string): string[];
9
8
  //# sourceMappingURL=runner.d.ts.map
package/dist/runner.js CHANGED
@@ -33,11 +33,10 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.FIXABLE_RULES = void 0;
36
+ exports.FIXABLE_RULES = exports.applyFixes = void 0;
37
37
  exports.runChecks = runChecks;
38
38
  exports.lintFiles = lintFiles;
39
39
  exports.lintFilesParallel = lintFilesParallel;
40
- exports.applyFixes = applyFixes;
41
40
  exports.parseIgnoreFile = parseIgnoreFile;
42
41
  exports.fixFile = fixFile;
43
42
  const fs = __importStar(require("fs"));
@@ -110,8 +109,8 @@ function runChecks(content, file, config) {
110
109
  }
111
110
  }
112
111
  }
113
- catch {
114
- // Check failed, continue with remaining checks
112
+ catch (e) {
113
+ console.warn(`[lint] check "${rule}" failed for ${file}: ${e instanceof Error ? e.message : String(e)}`);
115
114
  }
116
115
  }
117
116
  function addIssues(ruleIssues) {
@@ -198,6 +197,17 @@ function runChecks(content, file, config) {
198
197
  'identical_branches',
199
198
  'while_constant',
200
199
  'cond_duplicate',
200
+ 'command_s',
201
+ 'zerop_usage',
202
+ 'progn_in_body',
203
+ 'if_progn_both',
204
+ 'redundant_cond',
205
+ 'selection_set_leak',
206
+ 'empty_branch_cond',
207
+ 'progn_in_cond',
208
+ 'redundant_car_cdr',
209
+ 'equal_nil',
210
+ 'redundant_and_or',
201
211
  ]);
202
212
  const needsAst = Array.from(astRules).some(r => checks[r] !== 'off');
203
213
  if (needsAst) {
@@ -206,35 +216,49 @@ function runChecks(content, file, config) {
206
216
  const visitorIssues = (0, visitor_runner_1.runChecksWithVisitor)(ast, file, config, disableMap);
207
217
  addIssues(visitorIssues);
208
218
  }
209
- catch {
210
- // AST-based checks are skipped, text-based checks above still report issues
219
+ catch (e) {
220
+ console.warn(`[lint] AST parse/visitor failed for ${file}: ${e instanceof Error ? e.message : String(e)}`);
211
221
  }
212
222
  }
213
223
  return issues;
214
224
  }
215
- function lintFiles(files, config, rootDir) {
225
+ function lintFiles(files, config, rootDir, contentCache) {
216
226
  (0, locale_1.setLocale)(config.locale || 'zh');
217
227
  const allIssues = [];
218
- for (const filepath of files) {
228
+ const total = files.length;
229
+ const showProgress = process.stderr.isTTY && total > 5;
230
+ for (let fi = 0; fi < total; fi++) {
231
+ const filepath = files[fi];
219
232
  const relPath = path.relative(rootDir, filepath);
233
+ if (showProgress) {
234
+ process.stderr.write(`\r[${fi + 1}/${total}] ${relPath} `);
235
+ }
220
236
  let content;
221
- try {
222
- content = fs.readFileSync(filepath, 'utf-8');
237
+ if (contentCache && contentCache.has(filepath)) {
238
+ content = contentCache.get(filepath);
223
239
  }
224
- catch {
225
- allIssues.push({
226
- file: relPath,
227
- line: 1,
228
- severity: 'error',
229
- rule: 'read',
230
- message: (0, locale_1.t)('runner.read_error'),
231
- });
232
- continue;
240
+ else {
241
+ try {
242
+ content = fs.readFileSync(filepath, 'utf-8');
243
+ }
244
+ catch {
245
+ allIssues.push({
246
+ file: relPath,
247
+ line: 1,
248
+ severity: 'error',
249
+ rule: 'read',
250
+ message: (0, locale_1.t)('runner.read_error'),
251
+ });
252
+ continue;
253
+ }
233
254
  }
234
255
  const override = (0, config_1.findOverrides)(filepath);
235
256
  const effectiveConfig = override ? (0, config_1.mergeOverrides)(config, override) : config;
236
257
  allIssues.push(...runChecks(content, relPath, effectiveConfig));
237
258
  }
259
+ if (showProgress) {
260
+ process.stderr.write('\r' + ' '.repeat(60) + '\r');
261
+ }
238
262
  return allIssues;
239
263
  }
240
264
  function lintFilesParallel(files, config, rootDir) {
@@ -302,358 +326,9 @@ function lintFilesParallel(files, config, rootDir) {
302
326
  resolve([]);
303
327
  });
304
328
  }
305
- function findMatchingParen(s, start) {
306
- if (s[start] !== '(')
307
- return start;
308
- let depth = 0;
309
- for (let i = start; i < s.length; i++) {
310
- if (s[i] === '"') {
311
- i++;
312
- while (i < s.length && s[i] !== '"') {
313
- if (s[i] === '\\')
314
- i++;
315
- i++;
316
- }
317
- continue;
318
- }
319
- if (s[i] === '(')
320
- depth++;
321
- else if (s[i] === ')') {
322
- depth--;
323
- if (depth === 0)
324
- return i;
325
- }
326
- }
327
- return s.length - 1;
328
- }
329
- function extractForm(s, start) {
330
- let i = start;
331
- while (i < s.length && s[i] === ' ')
332
- i++;
333
- if (i >= s.length)
334
- return ['', i];
335
- if (s[i] !== '(') {
336
- const m = s.slice(i).match(/^\S+/);
337
- if (m)
338
- return [m[0], i + m[0].length];
339
- return ['', i];
340
- }
341
- const end = findMatchingParen(s, i);
342
- return [s.slice(i, end + 1), end + 1];
343
- }
344
- // ===== FIX APPLICATION =====
345
- exports.FIXABLE_RULES = new Set([
346
- 'redundant_quotes',
347
- 'double_not',
348
- 'redundant_nil_else',
349
- 'single_arg_and_or',
350
- 'redundant_setq',
351
- 'redundant_progn',
352
- 'redundant_let',
353
- 'quote_style',
354
- 'redundant_if',
355
- 'misplaced_else',
356
- 'setq_multiple',
357
- 'append_single',
358
- 'nth_usage',
359
- 'setq_single_arg',
360
- 'extra_parens',
361
- 'empty_branch',
362
- 'comment_style',
363
- 'eq_usage',
364
- 'cond_simplify',
365
- 'self_compare',
366
- 'while_constant',
367
- 'redundant_list',
368
- ]);
369
- function applyFixes(issues, content, filepath) {
370
- const lines = content.split('\n');
371
- const fixedLines = new Set();
372
- const fixesByRule = new Map();
373
- for (const iss of issues) {
374
- if (iss.file !== filepath)
375
- continue;
376
- const set = fixesByRule.get(iss.rule) || new Set();
377
- set.add(iss.line);
378
- fixesByRule.set(iss.rule, set);
379
- }
380
- const fixableRules = exports.FIXABLE_RULES;
381
- for (const [rule, lineSet] of fixesByRule) {
382
- if (!fixableRules.has(rule))
383
- continue;
384
- const sortedLines = Array.from(lineSet).sort((a, b) => b - a);
385
- for (const line of sortedLines) {
386
- const idx = line - 1;
387
- if (idx < 0 || idx >= lines.length)
388
- continue;
389
- const lineContent = lines[idx];
390
- switch (rule) {
391
- case 'redundant_quotes': {
392
- const fixed = lineContent.replace(/''(\S)/g, "'$1");
393
- if (fixed !== lineContent) {
394
- lines[idx] = fixed;
395
- fixedLines.add(idx);
396
- }
397
- break;
398
- }
399
- case 'double_not': {
400
- const fixed = lineContent.replace(/\(not\s+\(not\s+/g, '(not ');
401
- if (fixed !== lineContent) {
402
- lines[idx] = fixed;
403
- fixedLines.add(idx);
404
- }
405
- break;
406
- }
407
- case 'redundant_nil_else': {
408
- const fixed = lineContent.replace(/\s+nil\s*\)$/, ')');
409
- if (fixed !== lineContent) {
410
- lines[idx] = fixed;
411
- fixedLines.add(idx);
412
- }
413
- break;
414
- }
415
- case 'single_arg_and_or': {
416
- const m = lineContent.match(/\((and|or)\s+([^)\s]+)\s*\)/);
417
- if (m) {
418
- lines[idx] = m[2];
419
- fixedLines.add(idx);
420
- }
421
- break;
422
- }
423
- case 'redundant_progn': {
424
- const proIdx = lineContent.indexOf('(progn ');
425
- if (proIdx !== -1) {
426
- const beforePro = lineContent.slice(0, proIdx);
427
- const close = findMatchingParen(lineContent, proIdx);
428
- if (close > proIdx) {
429
- const inner = lineContent.slice(proIdx + 7, close);
430
- const rest = lineContent.slice(close + 1);
431
- const fixed = beforePro + inner + rest;
432
- if (fixed !== lineContent) {
433
- lines[idx] = fixed;
434
- fixedLines.add(idx);
435
- }
436
- }
437
- }
438
- break;
439
- }
440
- case 'redundant_let': {
441
- const fixed = lineContent.replace(/\(let\s+\(\)\s*/, '(progn ');
442
- if (fixed !== lineContent) {
443
- lines[idx] = fixed;
444
- fixedLines.add(idx);
445
- }
446
- break;
447
- }
448
- case 'quote_style': {
449
- const qIdx = lineContent.indexOf('(quote ');
450
- if (qIdx !== -1) {
451
- const beforeQ = lineContent.slice(0, qIdx);
452
- const close = findMatchingParen(lineContent, qIdx);
453
- if (close > qIdx) {
454
- const quoted = lineContent.slice(qIdx + 7, close);
455
- const rest = lineContent.slice(close + 1);
456
- const fixed = beforeQ + "'" + quoted + rest;
457
- if (fixed !== lineContent) {
458
- lines[idx] = fixed;
459
- fixedLines.add(idx);
460
- }
461
- }
462
- }
463
- break;
464
- }
465
- case 'redundant_if': {
466
- const pIdx = lineContent.indexOf('(progn ');
467
- if (pIdx !== -1) {
468
- const beforeP = lineContent.slice(0, pIdx);
469
- const close = findMatchingParen(lineContent, pIdx);
470
- if (close > pIdx) {
471
- const inner = lineContent.slice(pIdx + 7, close);
472
- const rest = lineContent.slice(close + 1);
473
- const fixed = beforeP + inner + rest;
474
- if (fixed !== lineContent) {
475
- lines[idx] = fixed;
476
- fixedLines.add(idx);
477
- }
478
- }
479
- }
480
- break;
481
- }
482
- case 'misplaced_else': {
483
- const ifStart = lineContent.indexOf('(if (not ');
484
- if (ifStart !== -1) {
485
- const notIdx = lineContent.indexOf('(not ', ifStart);
486
- if (notIdx === -1)
487
- break;
488
- const notClose = findMatchingParen(lineContent, notIdx);
489
- if (notClose <= notIdx)
490
- break;
491
- const ifClose = findMatchingParen(lineContent, ifStart);
492
- if (ifClose <= ifStart)
493
- break;
494
- const indent = lineContent.slice(0, ifStart);
495
- const condVar = lineContent.slice(notIdx + 5, notClose);
496
- const branchContent = lineContent.slice(notClose + 1, ifClose).trim();
497
- const [thenForm, thenEnd] = extractForm(branchContent, 0);
498
- const elseForm = branchContent.slice(thenEnd).trim();
499
- if (thenForm && elseForm) {
500
- const rest = lineContent.slice(ifClose + 1);
501
- const fixed = indent + '(if ' + condVar + ' ' + elseForm + ' ' + thenForm + ')' + rest;
502
- if (fixed !== lineContent) {
503
- lines[idx] = fixed;
504
- fixedLines.add(idx);
505
- }
506
- }
507
- }
508
- break;
509
- }
510
- case 'redundant_setq': {
511
- const trimmed = lines[idx].trim();
512
- if (/^\(setq\s+(\S+)\s+\1\s*\)$/.test(trimmed)) {
513
- lines.splice(idx, 1);
514
- }
515
- break;
516
- }
517
- case 'setq_multiple': {
518
- const fixed = lineContent.replace(/\)\(setq\s+/g, ' ');
519
- if (fixed !== lineContent) {
520
- const merged = lineContent.replace(/^\(\s*setq\s+/, '(setq ');
521
- if (merged.includes('setq') && !merged.match(/\)\(/)) {
522
- lines[idx] = merged;
523
- fixedLines.add(idx);
524
- }
525
- else {
526
- const simplified = lineContent.replace(/\)\(setq\s+/g, '\n');
527
- if (simplified !== lineContent) {
528
- lines[idx] = simplified;
529
- fixedLines.add(idx);
530
- }
531
- }
532
- }
533
- break;
534
- }
535
- case 'append_single': {
536
- const aIdx = lineContent.indexOf('(append ');
537
- if (aIdx !== -1) {
538
- const close = findMatchingParen(lineContent, aIdx);
539
- if (close > aIdx) {
540
- const inner = lineContent.slice(aIdx + 8, close);
541
- const listMatch = inner.match(/^\(list\s+(.*)\)\s*$/);
542
- if (listMatch) {
543
- const fixed = lineContent.slice(0, aIdx) + '(cons ' + listMatch[1] + ')' + lineContent.slice(close + 1);
544
- if (fixed !== lineContent) {
545
- lines[idx] = fixed;
546
- fixedLines.add(idx);
547
- }
548
- }
549
- }
550
- }
551
- break;
552
- }
553
- case 'nth_usage': {
554
- const fixed = lineContent.replace(/\(nth\s+0\s+/g, '(car ');
555
- if (fixed !== lineContent) {
556
- lines[idx] = fixed;
557
- fixedLines.add(idx);
558
- }
559
- break;
560
- }
561
- case 'setq_single_arg': {
562
- if (/^\s*\(setq\s+\S+\s*\)\s*$/.test(lines[idx])) {
563
- lines.splice(idx, 1);
564
- }
565
- break;
566
- }
567
- case 'extra_parens': {
568
- const fixed = lineContent.replace(/\){2,}/g, ')');
569
- if (fixed !== lineContent) {
570
- lines[idx] = fixed;
571
- fixedLines.add(idx);
572
- }
573
- break;
574
- }
575
- case 'empty_branch': {
576
- const fixed = lineContent.replace(/\(if\s+\S+\s*\)/g, m => m.slice(0, -1) + ' nil)');
577
- if (fixed !== lineContent) {
578
- lines[idx] = fixed;
579
- fixedLines.add(idx);
580
- }
581
- break;
582
- }
583
- case 'comment_style': {
584
- const fixed = lineContent.replace(/^(\s*;)([^ ;|].*)$/gm, '$1 $2');
585
- if (fixed !== lineContent) {
586
- lines[idx] = fixed;
587
- fixedLines.add(idx);
588
- }
589
- break;
590
- }
591
- case 'eq_usage': {
592
- const fixed = lineContent.replace(/\(eq\s+(\S+)\s+(\d+)\s*\)/g, '(= $1 $2)');
593
- if (fixed !== lineContent) {
594
- lines[idx] = fixed;
595
- fixedLines.add(idx);
596
- }
597
- break;
598
- }
599
- case 'cond_simplify': {
600
- const condIdx = lineContent.indexOf('(cond ');
601
- if (condIdx !== -1) {
602
- const condEnd = findMatchingParen(lineContent, condIdx);
603
- if (condEnd > condIdx) {
604
- const inner = lineContent.slice(condIdx + 6, condEnd).trim();
605
- const branchOpen = inner.indexOf('(');
606
- if (branchOpen !== -1) {
607
- const branchEnd = findMatchingParen(inner, branchOpen);
608
- if (branchEnd > branchOpen) {
609
- const indent = lineContent.slice(0, condIdx);
610
- const branchContent = inner.slice(branchOpen + 1, branchEnd).trim();
611
- const space = branchContent.search(/\s+/);
612
- if (space !== -1) {
613
- const testExpr = branchContent.slice(0, space);
614
- const bodyExpr = branchContent.slice(space).trim();
615
- const rest = lineContent.slice(condEnd + 1);
616
- const fixed = indent + '(if ' + testExpr + ' ' + bodyExpr + ')' + rest;
617
- if (fixed !== lineContent) {
618
- lines[idx] = fixed;
619
- fixedLines.add(idx);
620
- }
621
- }
622
- }
623
- }
624
- }
625
- }
626
- break;
627
- }
628
- case 'self_compare': {
629
- const fixed = lineContent.replace(/\((?:[=/<>]+|eq|equal)\s+(\S+)\s+\1\s*\)/g, 'T');
630
- if (fixed !== lineContent) {
631
- lines[idx] = fixed;
632
- fixedLines.add(idx);
633
- }
634
- break;
635
- }
636
- case 'while_constant': {
637
- const trimmed = lines[idx].trim();
638
- const nilMatch = trimmed.match(/^\(\s*while\s+nil\s+(.*)\)\s*$/);
639
- if (nilMatch) {
640
- lines[idx] = lines[idx].replace(trimmed, 'nil');
641
- }
642
- break;
643
- }
644
- case 'redundant_list': {
645
- const fixed = lineContent.replace(/\(\s*list\s*\)/g, 'nil');
646
- if (fixed !== lineContent) {
647
- lines[idx] = fixed;
648
- fixedLines.add(idx);
649
- }
650
- break;
651
- }
652
- }
653
- }
654
- }
655
- return lines.join('\n');
656
- }
329
+ var index_1 = require("./fixes/index");
330
+ Object.defineProperty(exports, "applyFixes", { enumerable: true, get: function () { return index_1.applyFixesFromProviders; } });
331
+ Object.defineProperty(exports, "FIXABLE_RULES", { enumerable: true, get: function () { return index_1.FIXABLE_RULES; } });
657
332
  function parseIgnoreFile(rootDir) {
658
333
  const ignoreFile = path.join(rootDir, '.atlisp-lint-ignore');
659
334
  if (!fs.existsSync(ignoreFile))
package/dist/types.d.ts CHANGED
@@ -24,6 +24,7 @@ export interface DangerousCallsConfig {
24
24
  command_shell: Severity;
25
25
  startapp: Severity;
26
26
  vl_registry_write: Severity;
27
+ eval: Severity;
27
28
  }
28
29
  export interface ModuleRegistrationConfig extends CheckConfig {
29
30
  patterns: string[];
package/dist/utils.d.ts CHANGED
@@ -7,6 +7,10 @@ export declare function codeLines(content: string): Generator<{
7
7
  text: string;
8
8
  stripped: string;
9
9
  }>;
10
+ /** Find matching closing paren (string/comment aware) */
11
+ export declare function findMatchingParen(s: string, start: number): number;
12
+ /** Extract the next s-expression form starting at position */
13
+ export declare function extractForm(s: string, start: number): [string, number];
10
14
  /** Extract string literals from a line */
11
15
  export declare function extractStrings(line: string): string[];
12
16
  //# sourceMappingURL=utils.d.ts.map
package/dist/utils.js CHANGED
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.stripLine = stripLine;
4
4
  exports.stripLineRaw = stripLineRaw;
5
5
  exports.codeLines = codeLines;
6
+ exports.findMatchingParen = findMatchingParen;
7
+ exports.extractForm = extractForm;
6
8
  exports.extractStrings = extractStrings;
7
9
  /** Strip string contents and comments from a line of AutoLISP code */
8
10
  function stripLine(line) {
@@ -67,6 +69,47 @@ function* codeLines(content) {
67
69
  };
68
70
  }
69
71
  }
72
+ /** Find matching closing paren (string/comment aware) */
73
+ function findMatchingParen(s, start) {
74
+ if (s[start] !== '(')
75
+ return start;
76
+ let depth = 0;
77
+ for (let i = start; i < s.length; i++) {
78
+ if (s[i] === '"') {
79
+ i++;
80
+ while (i < s.length && s[i] !== '"') {
81
+ if (s[i] === '\\')
82
+ i++;
83
+ i++;
84
+ }
85
+ continue;
86
+ }
87
+ if (s[i] === '(')
88
+ depth++;
89
+ else if (s[i] === ')') {
90
+ depth--;
91
+ if (depth === 0)
92
+ return i;
93
+ }
94
+ }
95
+ return s.length - 1;
96
+ }
97
+ /** Extract the next s-expression form starting at position */
98
+ function extractForm(s, start) {
99
+ let i = start;
100
+ while (i < s.length && s[i] === ' ')
101
+ i++;
102
+ if (i >= s.length)
103
+ return ['', i];
104
+ if (s[i] !== '(') {
105
+ const m = s.slice(i).match(/^\S+/);
106
+ if (m)
107
+ return [m[0], i + m[0].length];
108
+ return ['', i];
109
+ }
110
+ const end = findMatchingParen(s, i);
111
+ return [s.slice(i, end + 1), end + 1];
112
+ }
70
113
  /** Extract string literals from a line */
71
114
  function extractStrings(line) {
72
115
  const strings = [];
package/dist/validate.js CHANGED
@@ -89,6 +89,18 @@ const VALID_RULES = [
89
89
  'redundant_entmake',
90
90
  'missing_princ',
91
91
  'global_function_naming',
92
+ 'command_s',
93
+ 'zerop_usage',
94
+ 'progn_in_body',
95
+ 'if_progn_both',
96
+ 'redundant_cond',
97
+ 'selection_set_leak',
98
+ 'empty_branch_cond',
99
+ 'function_reference',
100
+ 'progn_in_cond',
101
+ 'redundant_car_cdr',
102
+ 'equal_nil',
103
+ 'redundant_and_or',
92
104
  ];
93
105
  const VALID_SEVERITIES = ['off', 'info', 'warn', 'error'];
94
106
  function validateConfig(config) {