@atlisp/lint 0.1.22 → 0.1.24

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 (46) hide show
  1. package/dist/atlisp-lint.default.json +90 -0
  2. package/dist/checks/arg-count.js +1 -1
  3. package/dist/checks/assoc-without-cdr.js +1 -1
  4. package/dist/checks/cond-duplicate.js +1 -1
  5. package/dist/checks/constant-condition.js +3 -3
  6. package/dist/checks/dangerous-calls.js +1 -1
  7. package/dist/checks/dangling-defun.js +1 -1
  8. package/dist/checks/double-not.js +1 -1
  9. package/dist/checks/empty-branch.js +1 -1
  10. package/dist/checks/empty-catch.js +1 -1
  11. package/dist/checks/function-complexity.js +1 -1
  12. package/dist/checks/global-naming.js +1 -1
  13. package/dist/checks/identical-branches.js +1 -1
  14. package/dist/checks/misplaced-else.js +1 -1
  15. package/dist/checks/multiple-setq.js +1 -1
  16. package/dist/checks/no-return.js +1 -1
  17. package/dist/checks/open-close.js +1 -1
  18. package/dist/checks/parameter-naming.js +1 -1
  19. package/dist/checks/quote-vs-function.js +1 -1
  20. package/dist/checks/recursive-call.js +1 -1
  21. package/dist/checks/redundant-let.js +1 -1
  22. package/dist/checks/redundant-nil-else.js +1 -1
  23. package/dist/checks/redundant-progn.js +1 -1
  24. package/dist/checks/redundant-quotes.js +1 -1
  25. package/dist/checks/redundant-setq.js +1 -1
  26. package/dist/checks/self-compare.js +1 -1
  27. package/dist/checks/setq-single-arg.js +1 -1
  28. package/dist/checks/single-arg-and-or.js +1 -1
  29. package/dist/checks/undeclared-setq.js +1 -1
  30. package/dist/checks/unused-catch-result.js +1 -1
  31. package/dist/checks/unused-let.js +1 -1
  32. package/dist/checks/unused-local-fun.js +1 -1
  33. package/dist/checks/unused-package-dep.js +1 -1
  34. package/dist/checks/unused-param.js +1 -1
  35. package/dist/checks/unused-variable.js +1 -1
  36. package/dist/checks/variable-shadow.js +1 -1
  37. package/dist/checks/while-constant.js +1 -1
  38. package/dist/formatters.d.ts +0 -1
  39. package/dist/formatters.js +0 -16
  40. package/dist/lib/lint-sbcl.lisp +161 -0
  41. package/dist/project.js +5 -5
  42. package/dist/runner.d.ts +1 -1
  43. package/dist/runner.js +207 -93
  44. package/dist/stub-packages.json +41 -0
  45. package/dist/visitor-runner.js +5 -9
  46. package/package.json +2 -2
package/dist/runner.js CHANGED
@@ -33,8 +33,8 @@ 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
37
  exports.runChecks = runChecks;
37
- exports.runChecksWithVisitorWrapper = runChecksWithVisitorWrapper;
38
38
  exports.lintFiles = lintFiles;
39
39
  exports.lintFilesParallel = lintFilesParallel;
40
40
  exports.applyFixes = applyFixes;
@@ -99,12 +99,17 @@ function runChecks(content, file, config) {
99
99
  const severity = checks[rule];
100
100
  if (!severity || severity === 'off')
101
101
  return;
102
- const results = fn();
103
- for (const r of results) {
104
- if (!(0, disable_1.isDisabled)(r.rule, r.line, disableMap)) {
105
- issues.push(r);
102
+ try {
103
+ const results = fn();
104
+ for (const r of results) {
105
+ if (!(0, disable_1.isDisabled)(r.rule, r.line, disableMap)) {
106
+ issues.push(r);
107
+ }
106
108
  }
107
109
  }
110
+ catch {
111
+ // Check failed, continue with remaining checks
112
+ }
108
113
  }
109
114
  function addIssues(ruleIssues) {
110
115
  for (const r of ruleIssues) {
@@ -172,17 +177,17 @@ function runChecks(content, file, config) {
172
177
  ]);
173
178
  const needsAst = Array.from(astRules).some(r => checks[r] !== 'off');
174
179
  if (needsAst) {
175
- const ast = (0, parser_1.parseAst)(content);
176
- const visitorIssues = (0, visitor_runner_1.runChecksWithVisitor)(ast, file, config, disableMap);
177
- addIssues(visitorIssues);
180
+ try {
181
+ const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
182
+ const visitorIssues = (0, visitor_runner_1.runChecksWithVisitor)(ast, file, config, disableMap);
183
+ addIssues(visitorIssues);
184
+ }
185
+ catch {
186
+ // AST-based checks are skipped, text-based checks above still report issues
187
+ }
178
188
  }
179
189
  return issues;
180
190
  }
181
- function runChecksWithVisitorWrapper(content, file, config) {
182
- const disableMap = (0, disable_1.parseDisableComments)(content);
183
- const ast = (0, parser_1.parseAst)(content);
184
- return (0, visitor_runner_1.runChecksWithVisitor)(ast, file, config, disableMap);
185
- }
186
191
  function lintFiles(files, config, rootDir) {
187
192
  (0, locale_1.setLocale)(config.locale || 'zh');
188
193
  const allIssues = [];
@@ -308,8 +313,19 @@ function extractForm(s, start) {
308
313
  const end = findMatchingParen(s, i);
309
314
  return [s.slice(i, end + 1), end + 1];
310
315
  }
316
+ // ===== FIX APPLICATION =====
317
+ exports.FIXABLE_RULES = new Set([
318
+ 'redundant_quotes', 'double_not', 'redundant_nil_else',
319
+ 'single_arg_and_or', 'redundant_setq', 'redundant_progn',
320
+ 'redundant_let', 'quote_style', 'redundant_if', 'misplaced_else',
321
+ 'setq_multiple', 'append_single', 'nth_usage', 'setq_single_arg',
322
+ 'extra_parens', 'empty_branch',
323
+ 'comment_style', 'eq_usage', 'cond_simplify', 'self_compare',
324
+ 'while_constant', 'redundant_list',
325
+ ]);
311
326
  function applyFixes(issues, content, filepath) {
312
327
  const lines = content.split('\n');
328
+ const fixedLines = new Set();
313
329
  const fixesByRule = new Map();
314
330
  for (const iss of issues) {
315
331
  if (iss.file !== filepath)
@@ -318,14 +334,7 @@ function applyFixes(issues, content, filepath) {
318
334
  set.add(iss.line);
319
335
  fixesByRule.set(iss.rule, set);
320
336
  }
321
- const fixableRules = new Set([
322
- 'redundant_quotes', 'double_not', 'redundant_nil_else',
323
- 'single_arg_and_or', 'redundant_setq', 'redundant_progn',
324
- 'redundant_let', 'quote_style', 'redundant_if', 'misplaced_else',
325
- 'setq_multiple', 'append_single', 'nth_usage', 'setq_single_arg',
326
- 'extra_parens', 'empty_branch',
327
- ]);
328
- let result = content;
337
+ const fixableRules = exports.FIXABLE_RULES;
329
338
  for (const [rule, lineSet] of fixesByRule) {
330
339
  if (!fixableRules.has(rule))
331
340
  continue;
@@ -334,30 +343,38 @@ function applyFixes(issues, content, filepath) {
334
343
  const idx = line - 1;
335
344
  if (idx < 0 || idx >= lines.length)
336
345
  continue;
337
- const lineContent = result.split('\n')[idx];
346
+ const lineContent = lines[idx];
338
347
  switch (rule) {
339
348
  case 'redundant_quotes': {
340
349
  const fixed = lineContent.replace(/''(\S)/g, "'$1");
341
- if (fixed !== lineContent)
342
- result = replaceLine(result, idx, fixed);
350
+ if (fixed !== lineContent) {
351
+ lines[idx] = fixed;
352
+ fixedLines.add(idx);
353
+ }
343
354
  break;
344
355
  }
345
356
  case 'double_not': {
346
357
  const fixed = lineContent.replace(/\(not\s+\(not\s+/g, '(not ');
347
- if (fixed !== lineContent)
348
- result = replaceLine(result, idx, fixed);
358
+ if (fixed !== lineContent) {
359
+ lines[idx] = fixed;
360
+ fixedLines.add(idx);
361
+ }
349
362
  break;
350
363
  }
351
364
  case 'redundant_nil_else': {
352
365
  const fixed = lineContent.replace(/\s+nil\s*\)$/, ')');
353
- if (fixed !== lineContent)
354
- result = replaceLine(result, idx, fixed);
366
+ if (fixed !== lineContent) {
367
+ lines[idx] = fixed;
368
+ fixedLines.add(idx);
369
+ }
355
370
  break;
356
371
  }
357
372
  case 'single_arg_and_or': {
358
373
  const m = lineContent.match(/\((and|or)\s+([^)\s]+)\s*\)/);
359
- if (m)
360
- result = replaceLine(result, idx, m[2]);
374
+ if (m) {
375
+ lines[idx] = m[2];
376
+ fixedLines.add(idx);
377
+ }
361
378
  break;
362
379
  }
363
380
  case 'redundant_progn': {
@@ -369,16 +386,20 @@ function applyFixes(issues, content, filepath) {
369
386
  const inner = lineContent.slice(proIdx + 7, close);
370
387
  const rest = lineContent.slice(close + 1);
371
388
  const fixed = beforePro + inner + rest;
372
- if (fixed !== lineContent)
373
- result = replaceLine(result, idx, fixed);
389
+ if (fixed !== lineContent) {
390
+ lines[idx] = fixed;
391
+ fixedLines.add(idx);
392
+ }
374
393
  }
375
394
  }
376
395
  break;
377
396
  }
378
397
  case 'redundant_let': {
379
398
  const fixed = lineContent.replace(/\(let\s+\(\)\s*/, '(progn ');
380
- if (fixed !== lineContent)
381
- result = replaceLine(result, idx, fixed);
399
+ if (fixed !== lineContent) {
400
+ lines[idx] = fixed;
401
+ fixedLines.add(idx);
402
+ }
382
403
  break;
383
404
  }
384
405
  case 'quote_style': {
@@ -390,8 +411,10 @@ function applyFixes(issues, content, filepath) {
390
411
  const quoted = lineContent.slice(qIdx + 7, close);
391
412
  const rest = lineContent.slice(close + 1);
392
413
  const fixed = beforeQ + "'" + quoted + rest;
393
- if (fixed !== lineContent)
394
- result = replaceLine(result, idx, fixed);
414
+ if (fixed !== lineContent) {
415
+ lines[idx] = fixed;
416
+ fixedLines.add(idx);
417
+ }
395
418
  }
396
419
  }
397
420
  break;
@@ -405,8 +428,10 @@ function applyFixes(issues, content, filepath) {
405
428
  const inner = lineContent.slice(pIdx + 7, close);
406
429
  const rest = lineContent.slice(close + 1);
407
430
  const fixed = beforeP + inner + rest;
408
- if (fixed !== lineContent)
409
- result = replaceLine(result, idx, fixed);
431
+ if (fixed !== lineContent) {
432
+ lines[idx] = fixed;
433
+ fixedLines.add(idx);
434
+ }
410
435
  }
411
436
  }
412
437
  break;
@@ -431,18 +456,18 @@ function applyFixes(issues, content, filepath) {
431
456
  if (thenForm && elseForm) {
432
457
  const rest = lineContent.slice(ifClose + 1);
433
458
  const fixed = indent + '(if ' + condVar + ' ' + elseForm + ' ' + thenForm + ')' + rest;
434
- if (fixed !== lineContent)
435
- result = replaceLine(result, idx, fixed);
459
+ if (fixed !== lineContent) {
460
+ lines[idx] = fixed;
461
+ fixedLines.add(idx);
462
+ }
436
463
  }
437
464
  }
438
465
  break;
439
466
  }
440
467
  case 'redundant_setq': {
441
- const allLines = result.split('\n');
442
- const trimmed = allLines[idx].trim();
468
+ const trimmed = lines[idx].trim();
443
469
  if (/^\(setq\s+(\S+)\s+\1\s*\)$/.test(trimmed)) {
444
- allLines.splice(idx, 1);
445
- result = allLines.join('\n');
470
+ lines.splice(idx, 1);
446
471
  }
447
472
  break;
448
473
  }
@@ -451,12 +476,15 @@ function applyFixes(issues, content, filepath) {
451
476
  if (fixed !== lineContent) {
452
477
  const merged = lineContent.replace(/^\(\s*setq\s+/, '(setq ');
453
478
  if (merged.includes('setq') && !merged.match(/\)\(/)) {
454
- result = replaceLine(result, idx, merged);
479
+ lines[idx] = merged;
480
+ fixedLines.add(idx);
455
481
  }
456
482
  else {
457
483
  const simplified = lineContent.replace(/\)\(setq\s+/g, '\n');
458
- if (simplified !== lineContent)
459
- result = replaceLine(result, idx, simplified);
484
+ if (simplified !== lineContent) {
485
+ lines[idx] = simplified;
486
+ fixedLines.add(idx);
487
+ }
460
488
  }
461
489
  }
462
490
  break;
@@ -470,8 +498,10 @@ function applyFixes(issues, content, filepath) {
470
498
  const listMatch = inner.match(/^\(list\s+(.*)\)\s*$/);
471
499
  if (listMatch) {
472
500
  const fixed = lineContent.slice(0, aIdx) + '(cons ' + listMatch[1] + ')' + lineContent.slice(close + 1);
473
- if (fixed !== lineContent)
474
- result = replaceLine(result, idx, fixed);
501
+ if (fixed !== lineContent) {
502
+ lines[idx] = fixed;
503
+ fixedLines.add(idx);
504
+ }
475
505
  }
476
506
  }
477
507
  }
@@ -479,38 +509,106 @@ function applyFixes(issues, content, filepath) {
479
509
  }
480
510
  case 'nth_usage': {
481
511
  const fixed = lineContent.replace(/\(nth\s+0\s+/g, '(car ');
482
- if (fixed !== lineContent)
483
- result = replaceLine(result, idx, fixed);
512
+ if (fixed !== lineContent) {
513
+ lines[idx] = fixed;
514
+ fixedLines.add(idx);
515
+ }
484
516
  break;
485
517
  }
486
518
  case 'setq_single_arg': {
487
- const allLines = result.split('\n');
488
- if (/^\s*\(setq\s+\S+\s*\)\s*$/.test(allLines[idx])) {
489
- allLines.splice(idx, 1);
490
- result = allLines.join('\n');
519
+ if (/^\s*\(setq\s+\S+\s*\)\s*$/.test(lines[idx])) {
520
+ lines.splice(idx, 1);
491
521
  }
492
522
  break;
493
523
  }
494
524
  case 'extra_parens': {
495
525
  const fixed = lineContent.replace(/\){2,}/g, ')');
496
- if (fixed !== lineContent)
497
- result = replaceLine(result, idx, fixed);
526
+ if (fixed !== lineContent) {
527
+ lines[idx] = fixed;
528
+ fixedLines.add(idx);
529
+ }
498
530
  break;
499
531
  }
500
532
  case 'empty_branch': {
501
533
  const fixed = lineContent.replace(/\(if\s+\S+\s*\)/g, m => m.slice(0, -1) + ' nil)');
502
- if (fixed !== lineContent)
503
- result = replaceLine(result, idx, fixed);
534
+ if (fixed !== lineContent) {
535
+ lines[idx] = fixed;
536
+ fixedLines.add(idx);
537
+ }
538
+ break;
539
+ }
540
+ case 'comment_style': {
541
+ const fixed = lineContent.replace(/^(\s*;)([^ ;|].*)$/gm, '$1 $2');
542
+ if (fixed !== lineContent) {
543
+ lines[idx] = fixed;
544
+ fixedLines.add(idx);
545
+ }
546
+ break;
547
+ }
548
+ case 'eq_usage': {
549
+ const fixed = lineContent.replace(/\(eq\s+(\S+)\s+(\d+)\s*\)/g, '(= $1 $2)');
550
+ if (fixed !== lineContent) {
551
+ lines[idx] = fixed;
552
+ fixedLines.add(idx);
553
+ }
554
+ break;
555
+ }
556
+ case 'cond_simplify': {
557
+ const condIdx = lineContent.indexOf('(cond ');
558
+ if (condIdx !== -1) {
559
+ const condEnd = findMatchingParen(lineContent, condIdx);
560
+ if (condEnd > condIdx) {
561
+ const inner = lineContent.slice(condIdx + 6, condEnd).trim();
562
+ const branchOpen = inner.indexOf('(');
563
+ if (branchOpen !== -1) {
564
+ const branchEnd = findMatchingParen(inner, branchOpen);
565
+ if (branchEnd > branchOpen) {
566
+ const indent = lineContent.slice(0, condIdx);
567
+ const branchContent = inner.slice(branchOpen + 1, branchEnd).trim();
568
+ const space = branchContent.search(/\s+/);
569
+ if (space !== -1) {
570
+ const testExpr = branchContent.slice(0, space);
571
+ const bodyExpr = branchContent.slice(space).trim();
572
+ const rest = lineContent.slice(condEnd + 1);
573
+ const fixed = indent + '(if ' + testExpr + ' ' + bodyExpr + ')' + rest;
574
+ if (fixed !== lineContent) {
575
+ lines[idx] = fixed;
576
+ fixedLines.add(idx);
577
+ }
578
+ }
579
+ }
580
+ }
581
+ }
582
+ }
583
+ break;
584
+ }
585
+ case 'self_compare': {
586
+ const fixed = lineContent.replace(/\((?:[=/<>]+|eq|equal)\s+(\S+)\s+\1\s*\)/g, 'T');
587
+ if (fixed !== lineContent) {
588
+ lines[idx] = fixed;
589
+ fixedLines.add(idx);
590
+ }
591
+ break;
592
+ }
593
+ case 'while_constant': {
594
+ const trimmed = lines[idx].trim();
595
+ const nilMatch = trimmed.match(/^\(\s*while\s+nil\s+(.*)\)\s*$/);
596
+ if (nilMatch) {
597
+ lines[idx] = lines[idx].replace(trimmed, 'nil');
598
+ }
599
+ break;
600
+ }
601
+ case 'redundant_list': {
602
+ const fixed = lineContent.replace(/\(\s*list\s*\)/g, 'nil');
603
+ if (fixed !== lineContent) {
604
+ lines[idx] = fixed;
605
+ fixedLines.add(idx);
606
+ }
504
607
  break;
505
608
  }
506
609
  }
507
610
  }
508
611
  }
509
- return result;
510
- }
511
- function replaceLine(content, lineIdx, newLine) {
512
- const lines = content.split('\n');
513
- lines[lineIdx] = newLine;
514
612
  return lines.join('\n');
515
613
  }
516
614
  function parseIgnoreFile(rootDir) {
@@ -527,23 +625,51 @@ const UTF8_BOM = 0xfeff;
527
625
  function fixFile(filepath) {
528
626
  const fixes = [];
529
627
  let content = fs.readFileSync(filepath, 'utf-8');
530
- const wsFixed = content
531
- .split('\n')
532
- .map(l => l.replace(/[ \t]+$/, ''))
533
- .join('\n');
534
- if (wsFixed !== content) {
535
- content = wsFixed;
536
- fixes.push('trailing_whitespace');
537
- }
628
+ // 1. Remove BOM
538
629
  if (content.charCodeAt(0) === UTF8_BOM) {
539
630
  content = content.slice(1);
540
631
  fixes.push('encoding (BOM)');
541
632
  }
633
+ // 2. Single line pass: trailing whitespace + empty comment lines
634
+ const origLines = content.split('\n');
635
+ let wsChanged = false;
636
+ let ecChanged = false;
637
+ const processed = origLines.map(l => {
638
+ const stripped = l.replace(/[ \t]+$/, '');
639
+ if (stripped !== l)
640
+ wsChanged = true;
641
+ if (/^\s*;\s*$/.test(stripped)) {
642
+ ecChanged = true;
643
+ return '';
644
+ }
645
+ return stripped;
646
+ });
647
+ if (wsChanged)
648
+ fixes.push('trailing_whitespace');
649
+ if (ecChanged)
650
+ fixes.push('empty_comment');
651
+ if (wsChanged || ecChanged)
652
+ content = processed.join('\n');
653
+ // 3. Count parens (string/comment aware), fix trailing excess
542
654
  let openCount = 0;
543
655
  let closeCount = 0;
544
- let fixContent = content;
545
- for (let i = 0; i < fixContent.length; i++) {
546
- const ch = fixContent[i];
656
+ let inStr = false;
657
+ for (let i = 0; i < content.length; i++) {
658
+ const ch = content[i];
659
+ if (ch === '"') {
660
+ inStr = !inStr;
661
+ continue;
662
+ }
663
+ if (inStr) {
664
+ if (ch === '\\')
665
+ i++;
666
+ continue;
667
+ }
668
+ if (ch === ';') {
669
+ const nl = content.indexOf('\n', i);
670
+ i = nl !== -1 ? nl : content.length;
671
+ continue;
672
+ }
547
673
  if (ch === '(')
548
674
  openCount++;
549
675
  else if (ch === ')')
@@ -551,29 +677,17 @@ function fixFile(filepath) {
551
677
  }
552
678
  if (closeCount > openCount) {
553
679
  let excess = closeCount - openCount;
554
- while (excess > 0 && fixContent.endsWith(')')) {
555
- fixContent = fixContent.slice(0, -1);
680
+ let fixed = content;
681
+ while (excess > 0 && fixed.endsWith(')')) {
682
+ fixed = fixed.slice(0, -1);
556
683
  excess--;
557
684
  }
558
- fixContent = fixContent.replace(/\n[ \t]*$/, '');
559
- if (fixContent !== content) {
560
- content = fixContent;
685
+ fixed = fixed.replace(/\n[ \t]*$/, '');
686
+ if (fixed !== content) {
687
+ content = fixed;
561
688
  fixes.push('trailing_paren');
562
689
  }
563
690
  }
564
- const lines = content.split('\n');
565
- let changed = false;
566
- const fixedLines = lines.map(l => {
567
- if (/^\s*;\s*$/.test(l)) {
568
- changed = true;
569
- return '';
570
- }
571
- return l;
572
- });
573
- if (changed) {
574
- content = fixedLines.join('\n');
575
- fixes.push('empty_comment');
576
- }
577
691
  if (fixes.length > 0) {
578
692
  fs.writeFileSync(filepath, content, 'utf-8');
579
693
  }
@@ -0,0 +1,41 @@
1
+ ((AUTOLISP ())
2
+ (@ (COMMON-LISP AUTOLISP))
3
+ (C ())
4
+ (JSON ())
5
+ (SIDEBAR ())
6
+ (FUN ())
7
+ (DCL ())
8
+ (BASE ())
9
+ (STRING ())
10
+ (BLOCK ())
11
+ (ENTITY ())
12
+ (LAYER ())
13
+ (LAYOUT ())
14
+ (CURVE ())
15
+ (LIST ())
16
+ (M ())
17
+ (P ())
18
+ (UI ())
19
+ (SYS ())
20
+ (EXCEL ())
21
+ (WORD ())
22
+ (RE ())
23
+ (VECTRA ())
24
+ (DOS ())
25
+ (AT-PM ())
26
+ (QRENCODE ())
27
+ (NETWORK ())
28
+ (PKGMAN ())
29
+ (DATETIME ())
30
+ (DICT ())
31
+ (@NLP ())
32
+ (AT-SIDEBAR ())
33
+ (S ())
34
+ (VL ())
35
+ (VLR ())
36
+ (VLAX ())
37
+ (THEME ())
38
+ (AJAX ())
39
+ (ATLISP ())
40
+ (LOG ())
41
+ (ATLISP-CORE ()))
@@ -17,7 +17,7 @@ function runChecksWithVisitor(ast, file, config, disableMap) {
17
17
  openCount: 0,
18
18
  closeCount: 0,
19
19
  setqDefs: new Map(),
20
- symbolRefs: new Set(),
20
+ symbolRefs: new Map(),
21
21
  condKeys: new Map(),
22
22
  defunScopes: [],
23
23
  letScopes: [],
@@ -379,9 +379,9 @@ function runChecksWithVisitor(ast, file, config, disableMap) {
379
379
  const seen = new Set();
380
380
  for (let i = 1; i < node.children.length; i++) {
381
381
  const clause = node.children[i];
382
- if (clause.type !== 'list' || !clause.children || clause.children.length < 2)
382
+ if (clause.type !== 'list' || !clause.children || clause.children.length < 1)
383
383
  continue;
384
- const condTest = clause.children[1];
384
+ const condTest = clause.children[0];
385
385
  const key = getCondKey(condTest);
386
386
  if (seen.has(key)) {
387
387
  addIssue('cond_duplicate', clause.pos.line, (0, locale_1.t)('cond_duplicate'));
@@ -574,7 +574,7 @@ function runChecksWithVisitor(ast, file, config, disableMap) {
574
574
  if (checks['unused_variable'] !== 'off') {
575
575
  visitor.on('*', node => {
576
576
  if (node.type === 'symbol' && node.name) {
577
- state.symbolRefs.add(node.name);
577
+ state.symbolRefs.set(node.name, (state.symbolRefs.get(node.name) || 0) + 1);
578
578
  }
579
579
  });
580
580
  }
@@ -672,11 +672,7 @@ function runChecksWithVisitor(ast, file, config, disableMap) {
672
672
  if (checks['unused_variable'] !== 'off') {
673
673
  for (const [name, def] of state.setqDefs) {
674
674
  if (state.setqDefs.size > 1) {
675
- let refCount = 0;
676
- for (const ref of state.symbolRefs) {
677
- if (ref === name)
678
- refCount++;
679
- }
675
+ const refCount = state.symbolRefs.get(name) || 0;
680
676
  if (refCount <= 1) {
681
677
  addIssue('unused_variable', def.line, (0, locale_1.t)('unused_variable', name));
682
678
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlisp/lint",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "AutoLISP static analysis tool — parens, security, conventions, SBCL syntax validation",
5
5
  "keywords": [
6
6
  "CAD",
@@ -37,7 +37,7 @@
37
37
  "prepack": "npm run build"
38
38
  },
39
39
  "dependencies": {
40
- "@atlisp/parser": "^0.1.2"
40
+ "@atlisp/parser": "^0.1.3"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/jest": "^29",