@atlisp/lint 0.1.5 → 0.1.6

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 (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -58
  3. package/atlisp-lint.default.json +31 -1
  4. package/dist/atlisp-lint.default.json +90 -0
  5. package/dist/cache.d.ts +2 -2
  6. package/dist/cache.js +6 -6
  7. package/dist/checks/append-single.d.ts +3 -0
  8. package/dist/checks/append-single.js +17 -0
  9. package/dist/checks/arg-count.d.ts +3 -0
  10. package/dist/checks/arg-count.js +123 -0
  11. package/dist/checks/assoc-without-cdr.d.ts +5 -0
  12. package/dist/checks/assoc-without-cdr.js +32 -0
  13. package/dist/checks/comment-style.d.ts +3 -0
  14. package/dist/checks/comment-style.js +24 -0
  15. package/dist/checks/cond-duplicate.d.ts +5 -0
  16. package/dist/checks/cond-duplicate.js +52 -0
  17. package/dist/checks/cond-simplify.d.ts +3 -0
  18. package/dist/checks/cond-simplify.js +45 -0
  19. package/dist/checks/constant-condition.js +4 -4
  20. package/dist/checks/dangerous-calls.js +2 -2
  21. package/dist/checks/dangling-defun.d.ts +3 -0
  22. package/dist/checks/dangling-defun.js +10 -28
  23. package/dist/checks/double-not.js +1 -1
  24. package/dist/checks/duplicate-defun.d.ts +6 -0
  25. package/dist/checks/duplicate-defun.js +50 -0
  26. package/dist/checks/dynamic-doc.d.ts +3 -0
  27. package/dist/checks/dynamic-doc.js +21 -0
  28. package/dist/checks/empty-branch.js +1 -1
  29. package/dist/checks/empty-catch.d.ts +3 -0
  30. package/dist/checks/empty-catch.js +34 -0
  31. package/dist/checks/eq-usage.d.ts +3 -0
  32. package/dist/checks/eq-usage.js +25 -0
  33. package/dist/checks/error-handling.d.ts +3 -0
  34. package/dist/checks/error-handling.js +56 -0
  35. package/dist/checks/extra-parens.d.ts +3 -0
  36. package/dist/checks/extra-parens.js +45 -0
  37. package/dist/checks/function-complexity.js +1 -1
  38. package/dist/checks/function-order.d.ts +3 -0
  39. package/dist/checks/function-order.js +33 -0
  40. package/dist/checks/global-naming.d.ts +3 -0
  41. package/dist/checks/global-naming.js +62 -0
  42. package/dist/checks/identical-branches.d.ts +5 -0
  43. package/dist/checks/identical-branches.js +54 -0
  44. package/dist/checks/index.d.ts +3 -0
  45. package/dist/checks/index.js +117 -0
  46. package/dist/checks/lambda-syntax.d.ts +3 -0
  47. package/dist/checks/lambda-syntax.js +25 -0
  48. package/dist/checks/long-function-call.d.ts +3 -0
  49. package/dist/checks/long-function-call.js +54 -0
  50. package/dist/checks/loop-optimization.d.ts +3 -0
  51. package/dist/checks/loop-optimization.js +17 -0
  52. package/dist/checks/magic-number.d.ts +3 -0
  53. package/dist/checks/magic-number.js +21 -0
  54. package/dist/checks/misplaced-else.js +1 -1
  55. package/dist/checks/missing-export.js +2 -2
  56. package/dist/checks/mixed-indent.d.ts +3 -0
  57. package/dist/checks/mixed-indent.js +19 -0
  58. package/dist/checks/module-reg.js +1 -1
  59. package/dist/checks/multiple-setq.js +1 -1
  60. package/dist/checks/no-return.d.ts +3 -0
  61. package/dist/checks/no-return.js +51 -0
  62. package/dist/checks/nth-usage.d.ts +3 -0
  63. package/dist/checks/nth-usage.js +17 -0
  64. package/dist/checks/quote-style.d.ts +3 -0
  65. package/dist/checks/quote-style.js +25 -0
  66. package/dist/checks/quote-vs-function.js +1 -1
  67. package/dist/checks/recursive-call.js +1 -1
  68. package/dist/checks/redundant-if.d.ts +3 -0
  69. package/dist/checks/redundant-if.js +17 -0
  70. package/dist/checks/redundant-let.js +1 -1
  71. package/dist/checks/redundant-nil-else.js +1 -1
  72. package/dist/checks/redundant-progn.js +1 -1
  73. package/dist/checks/redundant-quotes.js +1 -1
  74. package/dist/checks/redundant-setq.js +1 -1
  75. package/dist/checks/self-compare.js +1 -1
  76. package/dist/checks/setq-multiple.d.ts +3 -0
  77. package/dist/checks/setq-multiple.js +17 -0
  78. package/dist/checks/setq-single-arg.d.ts +5 -0
  79. package/dist/checks/setq-single-arg.js +30 -0
  80. package/dist/checks/shadow-builtin.d.ts +3 -0
  81. package/dist/checks/shadow-builtin.js +77 -0
  82. package/dist/checks/single-arg-and-or.js +1 -1
  83. package/dist/checks/strcat-usage.d.ts +3 -0
  84. package/dist/checks/strcat-usage.js +25 -0
  85. package/dist/checks/type-check.d.ts +3 -0
  86. package/dist/checks/type-check.js +26 -0
  87. package/dist/checks/unused-let.js +1 -1
  88. package/dist/checks/unused-package-dep.js +3 -3
  89. package/dist/checks/variable-shadow.js +1 -1
  90. package/dist/checks/while-constant.d.ts +5 -0
  91. package/dist/checks/while-constant.js +40 -0
  92. package/dist/config.d.ts +1 -0
  93. package/dist/config.js +70 -2
  94. package/dist/disable.js +1 -1
  95. package/dist/formatters.d.ts +1 -0
  96. package/dist/formatters.js +18 -2
  97. package/dist/index.js +53 -13
  98. package/dist/lib/lint-sbcl.lisp +161 -0
  99. package/dist/locale.js +24 -0
  100. package/dist/presets.d.ts +4 -0
  101. package/dist/presets.js +158 -0
  102. package/dist/project.js +37 -6
  103. package/dist/rules.d.ts +9 -0
  104. package/dist/rules.js +238 -0
  105. package/dist/runner.d.ts +2 -0
  106. package/dist/runner.js +198 -11
  107. package/dist/sbcl.js +1 -1
  108. package/dist/stub-packages.json +41 -0
  109. package/dist/types.d.ts +6 -0
  110. package/dist/validate.d.ts +8 -0
  111. package/dist/validate.js +125 -0
  112. package/dist/watch.d.ts +9 -0
  113. package/dist/watch.js +113 -0
  114. package/package.json +1 -1
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkAssocWithoutCdr = checkAssocWithoutCdr;
4
+ exports.checkAssocWithoutCdrAst = checkAssocWithoutCdrAst;
5
+ const locale_1 = require("../locale");
6
+ const parser_1 = require("@atlisp/parser");
7
+ function checkAssocWithoutCdr(content, file) {
8
+ const ast = (0, parser_1.parseAst)(content);
9
+ return checkAssocWithoutCdrAst(ast, file);
10
+ }
11
+ function checkAssocWithoutCdrAst(ast, file) {
12
+ const issues = [];
13
+ const assocNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'assoc'));
14
+ for (const node of assocNodes) {
15
+ if (!node.parent)
16
+ continue;
17
+ // Check if parent is not a cdr or cadr call
18
+ const parent = node.parent;
19
+ const isWrapped = (0, parser_1.astIsList)(parent, 'cdr') || (0, parser_1.astIsList)(parent, 'cadr');
20
+ if (!isWrapped) {
21
+ issues.push({
22
+ file,
23
+ line: node.pos.line,
24
+ severity: 'warn',
25
+ rule: 'assoc_without_cdr',
26
+ message: (0, locale_1.t)('assoc_without_cdr'),
27
+ });
28
+ }
29
+ }
30
+ return issues;
31
+ }
32
+ //# sourceMappingURL=assoc-without-cdr.js.map
@@ -0,0 +1,3 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkCommentStyle(content: string, file: string): Issue[];
3
+ //# sourceMappingURL=comment-style.d.ts.map
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkCommentStyle = checkCommentStyle;
4
+ const locale_1 = require("../locale");
5
+ function checkCommentStyle(content, file) {
6
+ const issues = [];
7
+ const lines = content.split('\n');
8
+ for (let i = 0; i < lines.length; i++) {
9
+ const line = lines[i];
10
+ const trimmed = line.trim();
11
+ // Detect ; not followed by space (except ;; and ;|)
12
+ if (/^;[^ ;|]/.test(trimmed)) {
13
+ issues.push({
14
+ file,
15
+ line: i + 1,
16
+ severity: 'warn',
17
+ rule: 'comment_style',
18
+ message: (0, locale_1.t)('comment_style'),
19
+ });
20
+ }
21
+ }
22
+ return issues;
23
+ }
24
+ //# sourceMappingURL=comment-style.js.map
@@ -0,0 +1,5 @@
1
+ import { Issue } from '../types';
2
+ import { AstNode } from '@atlisp/parser';
3
+ export declare function checkCondDuplicate(content: string, file: string): Issue[];
4
+ export declare function checkCondDuplicateAst(ast: AstNode, file: string): Issue[];
5
+ //# sourceMappingURL=cond-duplicate.d.ts.map
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkCondDuplicate = checkCondDuplicate;
4
+ exports.checkCondDuplicateAst = checkCondDuplicateAst;
5
+ const locale_1 = require("../locale");
6
+ const parser_1 = require("@atlisp/parser");
7
+ function condKey(node) {
8
+ if (node.type === 'symbol')
9
+ return `s:${node.name}`;
10
+ if (node.type === 'number')
11
+ return `n:${node.value}`;
12
+ if (node.type === 'string')
13
+ return `str:${node.value}`;
14
+ if (node.type === 'list') {
15
+ const children = node.children || [];
16
+ return `list(${children.map(condKey).join(',')})`;
17
+ }
18
+ return '';
19
+ }
20
+ function checkCondDuplicate(content, file) {
21
+ const ast = (0, parser_1.parseAst)(content);
22
+ return checkCondDuplicateAst(ast, file);
23
+ }
24
+ function checkCondDuplicateAst(ast, file) {
25
+ const issues = [];
26
+ const condNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'cond'));
27
+ for (const node of condNodes) {
28
+ if (!node.children)
29
+ continue;
30
+ const seen = new Map();
31
+ for (let i = 1; i < node.children.length; i++) {
32
+ const clause = node.children[i];
33
+ if (!clause.children || clause.children.length < 1)
34
+ continue;
35
+ const condition = clause.children[0];
36
+ const key = condKey(condition);
37
+ if (seen.has(key)) {
38
+ issues.push({
39
+ file,
40
+ line: node.pos.line,
41
+ severity: 'warn',
42
+ rule: 'cond_duplicate',
43
+ message: (0, locale_1.t)('cond_duplicate'),
44
+ });
45
+ break;
46
+ }
47
+ seen.set(key, i);
48
+ }
49
+ }
50
+ return issues;
51
+ }
52
+ //# sourceMappingURL=cond-duplicate.js.map
@@ -0,0 +1,3 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkCondSimplify(content: string, file: string): Issue[];
3
+ //# sourceMappingURL=cond-simplify.d.ts.map
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkCondSimplify = checkCondSimplify;
4
+ const locale_1 = require("../locale");
5
+ const utils_1 = require("../utils");
6
+ function checkCondSimplify(content, file) {
7
+ const issues = [];
8
+ const lines = content.split('\n');
9
+ for (let i = 0; i < lines.length; i++) {
10
+ const stripped = (0, utils_1.stripLine)(lines[i]);
11
+ // Match (cond (t ...)) or (cond (expr ...)) - single branch cond
12
+ // A single-branch cond typically looks like: (cond (expr body)) on one line
13
+ const m = stripped.match(/\(cond\s+\(/);
14
+ if (m) {
15
+ // Count how many top-level branches inside this cond
16
+ const condStart = stripped.indexOf('(cond');
17
+ const afterCond = stripped.slice(condStart + 5).trim();
18
+ let branchCount = 0;
19
+ let parenDepth = 0;
20
+ for (const ch of afterCond) {
21
+ if (ch === '(') {
22
+ if (parenDepth === 0)
23
+ branchCount++;
24
+ parenDepth++;
25
+ }
26
+ else if (ch === ')') {
27
+ parenDepth--;
28
+ if (parenDepth < 0)
29
+ break;
30
+ }
31
+ }
32
+ if (branchCount === 1) {
33
+ issues.push({
34
+ file,
35
+ line: i + 1,
36
+ severity: 'warn',
37
+ rule: 'cond_simplify',
38
+ message: (0, locale_1.t)('cond_simplify'),
39
+ });
40
+ }
41
+ }
42
+ }
43
+ return issues;
44
+ }
45
+ //# sourceMappingURL=cond-simplify.js.map
@@ -14,7 +14,7 @@ function checkConstantConditionAst(ast, file) {
14
14
  const issues = [];
15
15
  // Check if / while with constant condition
16
16
  for (const form of CONDITION_FORMS) {
17
- const nodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, form));
17
+ const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, form));
18
18
  for (const node of nodes) {
19
19
  if (!node.children || node.children.length < 2)
20
20
  continue;
@@ -31,7 +31,7 @@ function checkConstantConditionAst(ast, file) {
31
31
  }
32
32
  }
33
33
  // Check (cond (T ...))
34
- const condNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'cond'));
34
+ const condNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'cond'));
35
35
  for (const condNode of condNodes) {
36
36
  if (!condNode.children)
37
37
  continue;
@@ -52,7 +52,7 @@ function checkConstantConditionAst(ast, file) {
52
52
  }
53
53
  }
54
54
  // Check (or T ...) — first arg is T, always short-circuits to T
55
- const orNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'or'));
55
+ const orNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'or'));
56
56
  for (const node of orNodes) {
57
57
  if (!node.children || node.children.length < 2)
58
58
  continue;
@@ -68,7 +68,7 @@ function checkConstantConditionAst(ast, file) {
68
68
  }
69
69
  }
70
70
  // Check (and nil ...) — first arg is nil, always short-circuits to nil
71
- const andNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'and'));
71
+ const andNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'and'));
72
72
  for (const node of andNodes) {
73
73
  if (!node.children || node.children.length < 2)
74
74
  continue;
@@ -47,7 +47,7 @@ function checkDangerousCallsAst(ast, file, config) {
47
47
  continue;
48
48
  if (pattern.commandShell) {
49
49
  // Find (command "shell" ...) calls
50
- const commandCalls = (0, parser_1.astFindAll)(ast, (n) => isFunctionCall(n, 'command'));
50
+ const commandCalls = (0, parser_1.astFindAll)(ast, n => isFunctionCall(n, 'command'));
51
51
  for (const call of commandCalls) {
52
52
  if (isInQuote(call))
53
53
  continue;
@@ -67,7 +67,7 @@ function checkDangerousCallsAst(ast, file, config) {
67
67
  }
68
68
  else if (pattern.functionCall) {
69
69
  // Find actual function calls
70
- const calls = (0, parser_1.astFindAll)(ast, (n) => isFunctionCall(n, pattern.rule));
70
+ const calls = (0, parser_1.astFindAll)(ast, n => isFunctionCall(n, pattern.rule));
71
71
  for (const call of calls) {
72
72
  if (isInQuote(call))
73
73
  continue;
@@ -2,5 +2,8 @@ import { Issue } from '../types';
2
2
  export declare function checkDanglingDefun(file: string, allDefuns: Map<string, {
3
3
  file: string;
4
4
  line: number;
5
+ }[]>, allReferences: Map<string, {
6
+ file: string;
7
+ line: number;
5
8
  }[]>): Issue[];
6
9
  //# sourceMappingURL=dangling-defun.d.ts.map
@@ -37,46 +37,28 @@ exports.checkDanglingDefun = checkDanglingDefun;
37
37
  const locale_1 = require("../locale");
38
38
  const parser_1 = require("@atlisp/parser");
39
39
  const fs = __importStar(require("fs"));
40
- function checkDanglingDefun(file, allDefuns) {
40
+ function checkDanglingDefun(file, allDefuns, allReferences) {
41
41
  const issues = [];
42
42
  const content = fs.readFileSync(file, 'utf-8');
43
43
  const ast = (0, parser_1.parseAst)(content);
44
44
  const localDefuns = [];
45
- const defunNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
45
+ const defunNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
46
46
  for (const node of defunNodes) {
47
47
  if (node.children && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
48
- localDefuns.push(node.children[1].name);
48
+ localDefuns.push({ name: node.children[1].name, line: node.pos.line });
49
49
  }
50
50
  }
51
- const calledSymbols = new Set();
52
- const allCalls = (0, parser_1.astFindAll)(ast, (n) => {
53
- if (n.type !== 'list' || !n.children || n.children.length === 0)
54
- return false;
55
- return (0, parser_1.astIsSymbol)(n.children[0]);
56
- });
57
- for (const call of allCalls) {
58
- const name = call.children[0].name;
59
- if (name !== 'defun' && name !== 'defun-q' && !name.startsWith('c:') && !name.includes(':')) {
60
- calledSymbols.add(name);
61
- }
62
- }
63
- for (const defunName of localDefuns) {
64
- let isCalled = calledSymbols.has(defunName);
65
- if (!isCalled) {
66
- for (const [, defs] of allDefuns) {
67
- if (defs.some((d) => d.file !== file)) {
68
- isCalled = true;
69
- break;
70
- }
71
- }
72
- }
73
- if (!isCalled) {
51
+ for (const defun of localDefuns) {
52
+ const refs = allReferences.get(defun.name) || [];
53
+ const calledElsewhere = refs.some(r => r.file !== file);
54
+ const calledLocally = refs.some(r => r.file === file);
55
+ if (!calledLocally && !calledElsewhere) {
74
56
  issues.push({
75
57
  file,
76
- line: defunNodes.find((n) => n.children && n.children.length >= 2 && (0, parser_1.astIsSymbol)(n.children[1]) && n.children[1].name === defunName).pos.line,
58
+ line: defun.line,
77
59
  severity: 'warn',
78
60
  rule: 'dangling_defun',
79
- message: (0, locale_1.t)('dangling_defun', defunName),
61
+ message: (0, locale_1.t)('dangling_defun', defun.name),
80
62
  });
81
63
  }
82
64
  }
@@ -10,7 +10,7 @@ function checkDoubleNot(content, file) {
10
10
  }
11
11
  function checkDoubleNotAst(ast, file) {
12
12
  const issues = [];
13
- const notNodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'not'));
13
+ const notNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'not'));
14
14
  for (const node of notNodes) {
15
15
  if (!node.children || node.children.length < 2)
16
16
  continue;
@@ -0,0 +1,6 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkDuplicateDefun(filepath: string, allDefuns: Map<string, {
3
+ file: string;
4
+ line: number;
5
+ }[]>): Issue[];
6
+ //# sourceMappingURL=duplicate-defun.d.ts.map
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkDuplicateDefun = checkDuplicateDefun;
4
+ const locale_1 = require("../locale");
5
+ function checkDuplicateDefun(filepath, allDefuns) {
6
+ const issues = [];
7
+ const defunsInFile = new Map();
8
+ // Collect defuns in this file
9
+ for (const [name, locations] of allDefuns) {
10
+ for (const loc of locations) {
11
+ if (loc.file === filepath) {
12
+ const lines = defunsInFile.get(name) || [];
13
+ lines.push(loc.line);
14
+ defunsInFile.set(name, lines);
15
+ }
16
+ }
17
+ }
18
+ // Check for duplicates within this file
19
+ for (const [name, lines] of defunsInFile) {
20
+ if (lines.length > 1) {
21
+ for (const line of lines.slice(1)) {
22
+ issues.push({
23
+ file: filepath,
24
+ line,
25
+ severity: 'warn',
26
+ rule: 'duplicate_defun',
27
+ message: (0, locale_1.t)('duplicate_defun', name),
28
+ });
29
+ }
30
+ }
31
+ }
32
+ // Check for duplicates across files
33
+ for (const [name, locations] of allDefuns) {
34
+ const matchingFiles = locations.filter(l => l.file !== filepath);
35
+ if (matchingFiles.length > 0) {
36
+ const thisFileLoc = locations.find(l => l.file === filepath);
37
+ if (thisFileLoc) {
38
+ issues.push({
39
+ file: filepath,
40
+ line: thisFileLoc.line,
41
+ severity: 'warn',
42
+ rule: 'duplicate_defun',
43
+ message: (0, locale_1.t)('duplicate_defun', name),
44
+ });
45
+ }
46
+ }
47
+ }
48
+ return issues;
49
+ }
50
+ //# sourceMappingURL=duplicate-defun.js.map
@@ -0,0 +1,3 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkDynamicDoc(content: string, file: string): Issue[];
3
+ //# sourceMappingURL=dynamic-doc.d.ts.map
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkDynamicDoc = checkDynamicDoc;
4
+ const locale_1 = require("../locale");
5
+ const utils_1 = require("../utils");
6
+ function checkDynamicDoc(content, file) {
7
+ const issues = [];
8
+ const lines = content.split('\n');
9
+ for (let i = 0; i < lines.length; i++) {
10
+ const stripped = (0, utils_1.stripLine)(lines[i]);
11
+ const m = stripped.match(/\(defun\s+(C:\S+)\b/);
12
+ if (m) {
13
+ const body = lines.slice(i + 1).join('\n');
14
+ if (!body.includes('(princ)')) {
15
+ issues.push({ file, line: i + 1, severity: 'warn', rule: 'dynamic_doc', message: (0, locale_1.t)('dynamic_doc', m[1]) });
16
+ }
17
+ }
18
+ }
19
+ return issues;
20
+ }
21
+ //# sourceMappingURL=dynamic-doc.js.map
@@ -13,7 +13,7 @@ function checkEmptyBranch(content, file) {
13
13
  function checkEmptyBranchAst(ast, file) {
14
14
  const issues = [];
15
15
  for (const form of BRANCH_FORMS) {
16
- const nodes = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, form));
16
+ const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, form));
17
17
  for (const node of nodes) {
18
18
  if (!node.children)
19
19
  continue;
@@ -0,0 +1,3 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkEmptyCatch(content: string, file: string): Issue[];
3
+ //# sourceMappingURL=empty-catch.d.ts.map
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkEmptyCatch = checkEmptyCatch;
4
+ const locale_1 = require("../locale");
5
+ const utils_1 = require("../utils");
6
+ function checkEmptyCatch(content, file) {
7
+ const issues = [];
8
+ const lines = content.split('\n');
9
+ for (let i = 0; i < lines.length; i++) {
10
+ const stripped = (0, utils_1.stripLine)(lines[i]);
11
+ // Detect (vl-catch-all-apply ...) not inside (vl-catch-all-error-p ...) check
12
+ if (stripped.includes('vl-catch-all-apply')) {
13
+ // Check subsequent lines for error-p check
14
+ let hasErrorCheck = false;
15
+ for (let j = i + 1; j < Math.min(i + 5, lines.length); j++) {
16
+ if ((0, utils_1.stripLine)(lines[j]).includes('vl-catch-all-error-p')) {
17
+ hasErrorCheck = true;
18
+ break;
19
+ }
20
+ }
21
+ if (!hasErrorCheck) {
22
+ issues.push({
23
+ file,
24
+ line: i + 1,
25
+ severity: 'warn',
26
+ rule: 'empty_catch',
27
+ message: (0, locale_1.t)('empty_catch'),
28
+ });
29
+ }
30
+ }
31
+ }
32
+ return issues;
33
+ }
34
+ //# sourceMappingURL=empty-catch.js.map
@@ -0,0 +1,3 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkEqUsage(content: string, file: string): Issue[];
3
+ //# sourceMappingURL=eq-usage.d.ts.map
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkEqUsage = checkEqUsage;
4
+ const locale_1 = require("../locale");
5
+ const utils_1 = require("../utils");
6
+ function checkEqUsage(content, file) {
7
+ const issues = [];
8
+ const lines = content.split('\n');
9
+ for (let i = 0; i < lines.length; i++) {
10
+ const stripped = (0, utils_1.stripLine)(lines[i]);
11
+ // Detect (eq num ...) where num is literal number
12
+ const m = stripped.match(/\(eq\s+\S+\s+(\d+)/);
13
+ if (m) {
14
+ issues.push({
15
+ file,
16
+ line: i + 1,
17
+ severity: 'warn',
18
+ rule: 'eq_usage',
19
+ message: (0, locale_1.t)('eq_usage'),
20
+ });
21
+ }
22
+ }
23
+ return issues;
24
+ }
25
+ //# sourceMappingURL=eq-usage.js.map
@@ -0,0 +1,3 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkErrorHandling(content: string, file: string): Issue[];
3
+ //# sourceMappingURL=error-handling.d.ts.map
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkErrorHandling = checkErrorHandling;
4
+ const locale_1 = require("../locale");
5
+ const utils_1 = require("../utils");
6
+ function checkErrorHandling(content, file) {
7
+ const issues = [];
8
+ const lines = content.split('\n');
9
+ let inDefun = false;
10
+ let defunName = '';
11
+ let defunStart = 0;
12
+ let defunText = '';
13
+ let hasErrorHandler = false;
14
+ for (let i = 0; i < lines.length; i++) {
15
+ const stripped = (0, utils_1.stripLine)(lines[i]);
16
+ if (!inDefun) {
17
+ const m = stripped.match(/\(defun\s+(\S+)/);
18
+ if (m) {
19
+ inDefun = true;
20
+ defunName = m[1];
21
+ defunStart = i + 1;
22
+ defunText = '';
23
+ hasErrorHandler = false;
24
+ }
25
+ }
26
+ if (inDefun) {
27
+ defunText += stripped + '\n';
28
+ if (stripped.includes('*error*'))
29
+ hasErrorHandler = true;
30
+ // Check for depth returning to 0 (end of defun)
31
+ let depth = 0;
32
+ for (const ch of stripped) {
33
+ if (ch === '(')
34
+ depth++;
35
+ else if (ch === ')')
36
+ depth--;
37
+ }
38
+ if (depth === 0 && defunText.trim().length > 0 && defunName) {
39
+ // End of defun - check if it uses command but no *error*
40
+ if (/\(command\b/.test(defunText) && !hasErrorHandler && !defunName.startsWith('c:')) {
41
+ issues.push({
42
+ file,
43
+ line: defunStart,
44
+ severity: 'warn',
45
+ rule: 'error_handling',
46
+ message: (0, locale_1.t)('error_handling', defunName),
47
+ });
48
+ }
49
+ inDefun = false;
50
+ defunName = '';
51
+ }
52
+ }
53
+ }
54
+ return issues;
55
+ }
56
+ //# sourceMappingURL=error-handling.js.map
@@ -0,0 +1,3 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkExtraParens(content: string, file: string): Issue[];
3
+ //# sourceMappingURL=extra-parens.d.ts.map
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkExtraParens = checkExtraParens;
4
+ const locale_1 = require("../locale");
5
+ const utils_1 = require("../utils");
6
+ function checkExtraParens(content, file) {
7
+ const issues = [];
8
+ const lines = content.split('\n');
9
+ let runningDepth = 0;
10
+ for (let i = 0; i < lines.length; i++) {
11
+ const stripped = (0, utils_1.stripLine)(lines[i]);
12
+ const closeOnly = stripped.trim();
13
+ // Empty line resets nothing; just continue tracking depth
14
+ if (!closeOnly)
15
+ continue;
16
+ // Count parens on this line
17
+ let lineOpen = 0, lineClose = 0;
18
+ for (const ch of stripped) {
19
+ if (ch === '(')
20
+ lineOpen++;
21
+ else if (ch === ')')
22
+ lineClose++;
23
+ }
24
+ // Check if this line is pure closing parens
25
+ if (/^\)+$/.test(closeOnly)) {
26
+ // Only flag if these closing parens exceed the running depth
27
+ // (i.e., they are unmatched / extra)
28
+ if (lineClose > runningDepth + lineOpen) {
29
+ const extra = lineClose - runningDepth - lineOpen;
30
+ issues.push({
31
+ file,
32
+ line: i + 1,
33
+ severity: 'warn',
34
+ rule: 'extra_parens',
35
+ message: (0, locale_1.t)('extra_parens', extra),
36
+ });
37
+ }
38
+ }
39
+ runningDepth += lineOpen - lineClose;
40
+ if (runningDepth < 0)
41
+ runningDepth = 0;
42
+ }
43
+ return issues;
44
+ }
45
+ //# sourceMappingURL=extra-parens.js.map
@@ -41,7 +41,7 @@ function checkFunctionComplexityAst(ast, file, maxLines, maxNesting) {
41
41
  }
42
42
  }
43
43
  // Check lambda forms: (lambda (args) body...)
44
- const lambdas = (0, parser_1.astFindAll)(ast, (n) => (0, parser_1.astIsList)(n, 'lambda'));
44
+ const lambdas = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'lambda'));
45
45
  for (const lam of lambdas) {
46
46
  if (!lam.children || !lam.end)
47
47
  continue;
@@ -0,0 +1,3 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkFunctionOrder(content: string, file: string): Issue[];
3
+ //# sourceMappingURL=function-order.d.ts.map
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkFunctionOrder = checkFunctionOrder;
4
+ const locale_1 = require("../locale");
5
+ const utils_1 = require("../utils");
6
+ function checkFunctionOrder(content, file) {
7
+ const issues = [];
8
+ const lines = content.split('\n');
9
+ const defined = new Set();
10
+ for (let i = 0; i < lines.length; i++) {
11
+ const stripped = (0, utils_1.stripLine)(lines[i]);
12
+ const defunM = stripped.match(/\(defun\s+(\S+)/);
13
+ if (defunM) {
14
+ const name = defunM[1];
15
+ if (name.includes('/') || name.startsWith('c:'))
16
+ continue;
17
+ defined.add(name);
18
+ continue;
19
+ }
20
+ const callM = stripped.match(/\((\S+)\s+/);
21
+ if (callM) {
22
+ const name = callM[1];
23
+ if (name.startsWith('(') || name === 'defun' || name.startsWith('@') || /^[a-z]+:/.test(name))
24
+ continue;
25
+ if (!defined.has(name) && name.length > 1 && /^[a-z]/.test(name)) {
26
+ defined.add(name);
27
+ issues.push({ file, line: i + 1, severity: 'warn', rule: 'function_order', message: (0, locale_1.t)('function_order', name) });
28
+ }
29
+ }
30
+ }
31
+ return issues;
32
+ }
33
+ //# sourceMappingURL=function-order.js.map
@@ -0,0 +1,3 @@
1
+ import { Issue } from '../types';
2
+ export declare function checkGlobalNaming(content: string, file: string): Issue[];
3
+ //# sourceMappingURL=global-naming.d.ts.map