@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.
- package/dist/atlisp-lint.default.json +90 -0
- package/dist/checks/arg-count.js +1 -1
- package/dist/checks/assoc-without-cdr.js +1 -1
- package/dist/checks/cond-duplicate.js +1 -1
- package/dist/checks/constant-condition.js +3 -3
- package/dist/checks/dangerous-calls.js +1 -1
- package/dist/checks/dangling-defun.js +1 -1
- package/dist/checks/double-not.js +1 -1
- package/dist/checks/empty-branch.js +1 -1
- package/dist/checks/empty-catch.js +1 -1
- package/dist/checks/function-complexity.js +1 -1
- package/dist/checks/global-naming.js +1 -1
- package/dist/checks/identical-branches.js +1 -1
- package/dist/checks/misplaced-else.js +1 -1
- package/dist/checks/multiple-setq.js +1 -1
- package/dist/checks/no-return.js +1 -1
- package/dist/checks/open-close.js +1 -1
- package/dist/checks/parameter-naming.js +1 -1
- package/dist/checks/quote-vs-function.js +1 -1
- package/dist/checks/recursive-call.js +1 -1
- 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-single-arg.js +1 -1
- package/dist/checks/single-arg-and-or.js +1 -1
- package/dist/checks/undeclared-setq.js +1 -1
- package/dist/checks/unused-catch-result.js +1 -1
- package/dist/checks/unused-let.js +1 -1
- package/dist/checks/unused-local-fun.js +1 -1
- package/dist/checks/unused-package-dep.js +1 -1
- package/dist/checks/unused-param.js +1 -1
- package/dist/checks/unused-variable.js +1 -1
- package/dist/checks/variable-shadow.js +1 -1
- package/dist/checks/while-constant.js +1 -1
- package/dist/formatters.d.ts +0 -1
- package/dist/formatters.js +0 -16
- package/dist/lib/lint-sbcl.lisp +161 -0
- package/dist/project.js +5 -5
- package/dist/runner.d.ts +1 -1
- package/dist/runner.js +207 -93
- package/dist/stub-packages.json +41 -0
- package/dist/visitor-runner.js +5 -9
- 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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
442
|
-
const trimmed = allLines[idx].trim();
|
|
468
|
+
const trimmed = lines[idx].trim();
|
|
443
469
|
if (/^\(setq\s+(\S+)\s+\1\s*\)$/.test(trimmed)) {
|
|
444
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
488
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
545
|
-
for (let i = 0; i <
|
|
546
|
-
const ch =
|
|
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
|
-
|
|
555
|
-
|
|
680
|
+
let fixed = content;
|
|
681
|
+
while (excess > 0 && fixed.endsWith(')')) {
|
|
682
|
+
fixed = fixed.slice(0, -1);
|
|
556
683
|
excess--;
|
|
557
684
|
}
|
|
558
|
-
|
|
559
|
-
if (
|
|
560
|
-
content =
|
|
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 ()))
|
package/dist/visitor-runner.js
CHANGED
|
@@ -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
|
|
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 <
|
|
382
|
+
if (clause.type !== 'list' || !clause.children || clause.children.length < 1)
|
|
383
383
|
continue;
|
|
384
|
-
const condTest = clause.children[
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
40
|
+
"@atlisp/parser": "^0.1.3"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/jest": "^29",
|