@atlisp/lint 0.1.3 → 0.1.4

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 (262) hide show
  1. package/README.md +5 -2
  2. package/atlisp-lint.default.json +29 -29
  3. package/bin/atlisp-lint +1 -1
  4. package/dist/checks/bare-names.js +4 -1
  5. package/dist/checks/cl-syntax.js +4 -1
  6. package/dist/checks/commented-code.d.ts +3 -0
  7. package/dist/checks/commented-code.js +46 -0
  8. package/dist/checks/constant-condition.d.ts +5 -0
  9. package/dist/checks/constant-condition.js +88 -0
  10. package/dist/checks/dangerous-calls.d.ts +2 -0
  11. package/dist/checks/dangerous-calls.js +70 -29
  12. package/dist/checks/dangling-defun.d.ts +6 -0
  13. package/dist/checks/dangling-defun.js +85 -0
  14. package/dist/checks/double-not.d.ts +5 -0
  15. package/dist/checks/double-not.js +30 -0
  16. package/dist/checks/empty-branch.d.ts +5 -0
  17. package/dist/checks/empty-branch.js +34 -0
  18. package/dist/checks/empty-comments.d.ts +3 -0
  19. package/dist/checks/empty-comments.js +26 -0
  20. package/dist/checks/encoding.js +13 -4
  21. package/dist/checks/function-complexity.d.ts +2 -0
  22. package/dist/checks/function-complexity.js +89 -45
  23. package/dist/checks/line-length.js +4 -1
  24. package/dist/checks/misplaced-else.d.ts +5 -0
  25. package/dist/checks/misplaced-else.js +31 -0
  26. package/dist/checks/missing-doc.js +6 -3
  27. package/dist/checks/missing-export.d.ts +13 -0
  28. package/dist/checks/missing-export.js +94 -0
  29. package/dist/checks/module-reg.js +9 -4
  30. package/dist/checks/multiple-setq.d.ts +5 -0
  31. package/dist/checks/multiple-setq.js +51 -0
  32. package/dist/checks/namespace-header.js +16 -6
  33. package/dist/checks/open-close.d.ts +2 -0
  34. package/dist/checks/open-close.js +18 -13
  35. package/dist/checks/parameter-naming.d.ts +2 -0
  36. package/dist/checks/parameter-naming.js +26 -14
  37. package/dist/checks/parens.js +8 -3
  38. package/dist/checks/quote-vs-function.d.ts +5 -0
  39. package/dist/checks/quote-vs-function.js +50 -0
  40. package/dist/checks/recursive-call.d.ts +5 -0
  41. package/dist/checks/recursive-call.js +49 -0
  42. package/dist/checks/redundant-cond.d.ts +5 -0
  43. package/dist/checks/redundant-cond.js +50 -0
  44. package/dist/checks/redundant-let.d.ts +5 -0
  45. package/dist/checks/redundant-let.js +31 -0
  46. package/dist/checks/redundant-nil-else.d.ts +5 -0
  47. package/dist/checks/redundant-nil-else.js +32 -0
  48. package/dist/checks/redundant-progn.d.ts +2 -0
  49. package/dist/checks/redundant-progn.js +18 -9
  50. package/dist/checks/redundant-quotes.d.ts +5 -0
  51. package/dist/checks/redundant-quotes.js +33 -0
  52. package/dist/checks/redundant-setq.d.ts +5 -0
  53. package/dist/checks/redundant-setq.js +34 -0
  54. package/dist/checks/self-compare.d.ts +5 -0
  55. package/dist/checks/self-compare.js +37 -0
  56. package/dist/checks/single-arg-and-or.d.ts +5 -0
  57. package/dist/checks/single-arg-and-or.js +32 -0
  58. package/dist/checks/token-in-url.js +4 -1
  59. package/dist/checks/trailing-paren.d.ts +3 -0
  60. package/dist/checks/trailing-paren.js +44 -0
  61. package/dist/checks/trailing-ws.js +4 -1
  62. package/dist/checks/unused-let.d.ts +5 -0
  63. package/dist/checks/unused-let.js +57 -0
  64. package/dist/checks/unused-local-fun.d.ts +5 -0
  65. package/dist/checks/unused-local-fun.js +62 -0
  66. package/dist/checks/unused-package-dep.d.ts +3 -0
  67. package/dist/checks/unused-package-dep.js +93 -0
  68. package/dist/checks/unused-param.d.ts +5 -0
  69. package/dist/checks/unused-param.js +63 -0
  70. package/dist/checks/unused-variable.d.ts +2 -0
  71. package/dist/checks/unused-variable.js +70 -42
  72. package/dist/checks/variable-shadow.d.ts +5 -0
  73. package/dist/checks/variable-shadow.js +66 -0
  74. package/dist/checks/vlax.js +8 -3
  75. package/dist/config.d.ts +1 -2
  76. package/dist/config.js +3 -35
  77. package/dist/disable.js +4 -1
  78. package/dist/formatters.d.ts +1 -2
  79. package/dist/formatters.js +8 -61
  80. package/dist/index.js +131 -201
  81. package/dist/locale.js +83 -91
  82. package/dist/project.d.ts +3 -0
  83. package/dist/project.js +138 -0
  84. package/dist/runner.d.ts +5 -11
  85. package/dist/runner.js +260 -71
  86. package/dist/sbcl.js +16 -11
  87. package/dist/worker.js +10 -5
  88. package/lib/lint-sbcl.lisp +19 -54
  89. package/package.json +11 -4
  90. package/LICENSE +0 -21
  91. package/dist/atlisp-lint.default.json +0 -90
  92. package/dist/cache.d.ts.map +0 -1
  93. package/dist/cache.js.map +0 -1
  94. package/dist/checks/append-single.d.ts +0 -3
  95. package/dist/checks/append-single.d.ts.map +0 -1
  96. package/dist/checks/append-single.js +0 -17
  97. package/dist/checks/append-single.js.map +0 -1
  98. package/dist/checks/arg-count.d.ts +0 -3
  99. package/dist/checks/arg-count.d.ts.map +0 -1
  100. package/dist/checks/arg-count.js +0 -120
  101. package/dist/checks/arg-count.js.map +0 -1
  102. package/dist/checks/bare-names.d.ts.map +0 -1
  103. package/dist/checks/bare-names.js.map +0 -1
  104. package/dist/checks/cl-syntax.d.ts.map +0 -1
  105. package/dist/checks/cl-syntax.js.map +0 -1
  106. package/dist/checks/comment-style.d.ts +0 -3
  107. package/dist/checks/comment-style.d.ts.map +0 -1
  108. package/dist/checks/comment-style.js +0 -21
  109. package/dist/checks/comment-style.js.map +0 -1
  110. package/dist/checks/cond-simplify.d.ts +0 -3
  111. package/dist/checks/cond-simplify.d.ts.map +0 -1
  112. package/dist/checks/cond-simplify.js +0 -42
  113. package/dist/checks/cond-simplify.js.map +0 -1
  114. package/dist/checks/dangerous-calls.d.ts.map +0 -1
  115. package/dist/checks/dangerous-calls.js.map +0 -1
  116. package/dist/checks/dynamic-doc.d.ts +0 -3
  117. package/dist/checks/dynamic-doc.d.ts.map +0 -1
  118. package/dist/checks/dynamic-doc.js +0 -21
  119. package/dist/checks/dynamic-doc.js.map +0 -1
  120. package/dist/checks/empty-catch.d.ts +0 -3
  121. package/dist/checks/empty-catch.d.ts.map +0 -1
  122. package/dist/checks/empty-catch.js +0 -31
  123. package/dist/checks/empty-catch.js.map +0 -1
  124. package/dist/checks/encoding.d.ts.map +0 -1
  125. package/dist/checks/encoding.js.map +0 -1
  126. package/dist/checks/eq-usage.d.ts +0 -3
  127. package/dist/checks/eq-usage.d.ts.map +0 -1
  128. package/dist/checks/eq-usage.js +0 -22
  129. package/dist/checks/eq-usage.js.map +0 -1
  130. package/dist/checks/error-handling.d.ts +0 -3
  131. package/dist/checks/error-handling.d.ts.map +0 -1
  132. package/dist/checks/error-handling.js +0 -53
  133. package/dist/checks/error-handling.js.map +0 -1
  134. package/dist/checks/extra-parens.d.ts +0 -3
  135. package/dist/checks/extra-parens.d.ts.map +0 -1
  136. package/dist/checks/extra-parens.js +0 -42
  137. package/dist/checks/extra-parens.js.map +0 -1
  138. package/dist/checks/function-complexity.d.ts.map +0 -1
  139. package/dist/checks/function-complexity.js.map +0 -1
  140. package/dist/checks/function-order.d.ts +0 -3
  141. package/dist/checks/function-order.d.ts.map +0 -1
  142. package/dist/checks/function-order.js +0 -33
  143. package/dist/checks/function-order.js.map +0 -1
  144. package/dist/checks/global-naming.d.ts +0 -3
  145. package/dist/checks/global-naming.d.ts.map +0 -1
  146. package/dist/checks/global-naming.js +0 -51
  147. package/dist/checks/global-naming.js.map +0 -1
  148. package/dist/checks/index.d.ts +0 -3
  149. package/dist/checks/index.d.ts.map +0 -1
  150. package/dist/checks/index.js +0 -108
  151. package/dist/checks/index.js.map +0 -1
  152. package/dist/checks/lambda-syntax.d.ts +0 -3
  153. package/dist/checks/lambda-syntax.d.ts.map +0 -1
  154. package/dist/checks/lambda-syntax.js +0 -22
  155. package/dist/checks/lambda-syntax.js.map +0 -1
  156. package/dist/checks/line-length.d.ts.map +0 -1
  157. package/dist/checks/line-length.js.map +0 -1
  158. package/dist/checks/long-function-call.d.ts +0 -3
  159. package/dist/checks/long-function-call.d.ts.map +0 -1
  160. package/dist/checks/long-function-call.js +0 -48
  161. package/dist/checks/long-function-call.js.map +0 -1
  162. package/dist/checks/loop-optimization.d.ts +0 -3
  163. package/dist/checks/loop-optimization.d.ts.map +0 -1
  164. package/dist/checks/loop-optimization.js +0 -17
  165. package/dist/checks/loop-optimization.js.map +0 -1
  166. package/dist/checks/magic-number.d.ts +0 -3
  167. package/dist/checks/magic-number.d.ts.map +0 -1
  168. package/dist/checks/magic-number.js +0 -21
  169. package/dist/checks/magic-number.js.map +0 -1
  170. package/dist/checks/missing-doc.d.ts.map +0 -1
  171. package/dist/checks/missing-doc.js.map +0 -1
  172. package/dist/checks/mixed-indent.d.ts +0 -3
  173. package/dist/checks/mixed-indent.d.ts.map +0 -1
  174. package/dist/checks/mixed-indent.js +0 -19
  175. package/dist/checks/mixed-indent.js.map +0 -1
  176. package/dist/checks/module-reg.d.ts.map +0 -1
  177. package/dist/checks/module-reg.js.map +0 -1
  178. package/dist/checks/namespace-header.d.ts.map +0 -1
  179. package/dist/checks/namespace-header.js.map +0 -1
  180. package/dist/checks/no-return.d.ts +0 -3
  181. package/dist/checks/no-return.d.ts.map +0 -1
  182. package/dist/checks/no-return.js +0 -45
  183. package/dist/checks/no-return.js.map +0 -1
  184. package/dist/checks/nth-usage.d.ts +0 -3
  185. package/dist/checks/nth-usage.d.ts.map +0 -1
  186. package/dist/checks/nth-usage.js +0 -17
  187. package/dist/checks/nth-usage.js.map +0 -1
  188. package/dist/checks/open-close.d.ts.map +0 -1
  189. package/dist/checks/open-close.js.map +0 -1
  190. package/dist/checks/parameter-naming.d.ts.map +0 -1
  191. package/dist/checks/parameter-naming.js.map +0 -1
  192. package/dist/checks/parens.d.ts.map +0 -1
  193. package/dist/checks/parens.js.map +0 -1
  194. package/dist/checks/quote-style.d.ts +0 -3
  195. package/dist/checks/quote-style.d.ts.map +0 -1
  196. package/dist/checks/quote-style.js +0 -22
  197. package/dist/checks/quote-style.js.map +0 -1
  198. package/dist/checks/redundant-if.d.ts +0 -3
  199. package/dist/checks/redundant-if.d.ts.map +0 -1
  200. package/dist/checks/redundant-if.js +0 -17
  201. package/dist/checks/redundant-if.js.map +0 -1
  202. package/dist/checks/redundant-progn.d.ts.map +0 -1
  203. package/dist/checks/redundant-progn.js.map +0 -1
  204. package/dist/checks/setq-multiple.d.ts +0 -3
  205. package/dist/checks/setq-multiple.d.ts.map +0 -1
  206. package/dist/checks/setq-multiple.js +0 -17
  207. package/dist/checks/setq-multiple.js.map +0 -1
  208. package/dist/checks/shadow-builtin.d.ts +0 -3
  209. package/dist/checks/shadow-builtin.d.ts.map +0 -1
  210. package/dist/checks/shadow-builtin.js +0 -29
  211. package/dist/checks/shadow-builtin.js.map +0 -1
  212. package/dist/checks/strcat-usage.d.ts +0 -3
  213. package/dist/checks/strcat-usage.d.ts.map +0 -1
  214. package/dist/checks/strcat-usage.js +0 -22
  215. package/dist/checks/strcat-usage.js.map +0 -1
  216. package/dist/checks/token-in-url.d.ts.map +0 -1
  217. package/dist/checks/token-in-url.js.map +0 -1
  218. package/dist/checks/trailing-ws.d.ts.map +0 -1
  219. package/dist/checks/trailing-ws.js.map +0 -1
  220. package/dist/checks/type-check.d.ts +0 -3
  221. package/dist/checks/type-check.d.ts.map +0 -1
  222. package/dist/checks/type-check.js +0 -26
  223. package/dist/checks/type-check.js.map +0 -1
  224. package/dist/checks/unused-variable.d.ts.map +0 -1
  225. package/dist/checks/unused-variable.js.map +0 -1
  226. package/dist/checks/vlax.d.ts.map +0 -1
  227. package/dist/checks/vlax.js.map +0 -1
  228. package/dist/config.d.ts.map +0 -1
  229. package/dist/config.js.map +0 -1
  230. package/dist/disable.d.ts.map +0 -1
  231. package/dist/disable.js.map +0 -1
  232. package/dist/formatters.d.ts.map +0 -1
  233. package/dist/formatters.js.map +0 -1
  234. package/dist/index.d.ts.map +0 -1
  235. package/dist/index.js.map +0 -1
  236. package/dist/lib/lint-sbcl.lisp +0 -161
  237. package/dist/locale.d.ts.map +0 -1
  238. package/dist/locale.js.map +0 -1
  239. package/dist/rules.d.ts +0 -9
  240. package/dist/rules.d.ts.map +0 -1
  241. package/dist/rules.js +0 -58
  242. package/dist/rules.js.map +0 -1
  243. package/dist/runner.d.ts.map +0 -1
  244. package/dist/runner.js.map +0 -1
  245. package/dist/sbcl.d.ts.map +0 -1
  246. package/dist/sbcl.js.map +0 -1
  247. package/dist/stub-packages.json +0 -41
  248. package/dist/types.d.ts.map +0 -1
  249. package/dist/types.js.map +0 -1
  250. package/dist/utils.d.ts.map +0 -1
  251. package/dist/utils.js.map +0 -1
  252. package/dist/validate.d.ts +0 -8
  253. package/dist/validate.d.ts.map +0 -1
  254. package/dist/validate.js +0 -59
  255. package/dist/validate.js.map +0 -1
  256. package/dist/watch.d.ts +0 -9
  257. package/dist/watch.d.ts.map +0 -1
  258. package/dist/watch.js +0 -109
  259. package/dist/watch.js.map +0 -1
  260. package/dist/worker.d.ts.map +0 -1
  261. package/dist/worker.js.map +0 -1
  262. package/pre-commit/hook.sh +0 -4
package/dist/runner.js CHANGED
@@ -33,62 +33,218 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.parseIgnoreFile = parseIgnoreFile;
36
+ exports.runChecks = runChecks;
37
+ exports.runChecksWithVisitor = runChecksWithVisitor;
37
38
  exports.lintFiles = lintFiles;
38
39
  exports.lintFilesParallel = lintFilesParallel;
39
40
  exports.fixFile = fixFile;
40
- exports.clearCache = clearCache;
41
41
  const fs = __importStar(require("fs"));
42
42
  const path = __importStar(require("path"));
43
43
  const worker_threads_1 = require("worker_threads");
44
44
  const os = __importStar(require("os"));
45
- const index_1 = require("./checks/index");
45
+ const encoding_1 = require("./checks/encoding");
46
+ const parens_1 = require("./checks/parens");
47
+ const cl_syntax_1 = require("./checks/cl-syntax");
48
+ const dangerous_calls_1 = require("./checks/dangerous-calls");
49
+ const vlax_1 = require("./checks/vlax");
50
+ const token_in_url_1 = require("./checks/token-in-url");
51
+ const open_close_1 = require("./checks/open-close");
52
+ const bare_names_1 = require("./checks/bare-names");
53
+ const module_reg_1 = require("./checks/module-reg");
54
+ const namespace_header_1 = require("./checks/namespace-header");
55
+ const trailing_ws_1 = require("./checks/trailing-ws");
56
+ const line_length_1 = require("./checks/line-length");
57
+ const function_complexity_1 = require("./checks/function-complexity");
58
+ const parameter_naming_1 = require("./checks/parameter-naming");
59
+ const unused_variable_1 = require("./checks/unused-variable");
60
+ const missing_doc_1 = require("./checks/missing-doc");
61
+ const unused_param_1 = require("./checks/unused-param");
62
+ const constant_condition_1 = require("./checks/constant-condition");
63
+ const redundant_progn_1 = require("./checks/redundant-progn");
64
+ const empty_branch_1 = require("./checks/empty-branch");
65
+ const unused_let_1 = require("./checks/unused-let");
66
+ const recursive_call_1 = require("./checks/recursive-call");
67
+ const variable_shadow_1 = require("./checks/variable-shadow");
68
+ const redundant_cond_1 = require("./checks/redundant-cond");
69
+ const unused_local_fun_1 = require("./checks/unused-local-fun");
70
+ const multiple_setq_1 = require("./checks/multiple-setq");
71
+ const redundant_quotes_1 = require("./checks/redundant-quotes");
72
+ const trailing_paren_1 = require("./checks/trailing-paren");
73
+ const empty_comments_1 = require("./checks/empty-comments");
74
+ const redundant_setq_1 = require("./checks/redundant-setq");
75
+ const redundant_nil_else_1 = require("./checks/redundant-nil-else");
76
+ const single_arg_and_or_1 = require("./checks/single-arg-and-or");
77
+ const redundant_let_1 = require("./checks/redundant-let");
78
+ const self_compare_1 = require("./checks/self-compare");
79
+ const misplaced_else_1 = require("./checks/misplaced-else");
80
+ const quote_vs_function_1 = require("./checks/quote-vs-function");
81
+ const commented_code_1 = require("./checks/commented-code");
82
+ const double_not_1 = require("./checks/double-not");
46
83
  const config_1 = require("./config");
47
84
  const locale_1 = require("./locale");
48
- const cache_1 = require("./cache");
49
- function parseIgnoreFile(rootDir) {
50
- const ignorePath = path.join(rootDir, '.atlisp-lint-ignore');
51
- if (!fs.existsSync(ignorePath))
52
- return [];
53
- const patterns = [];
54
- for (const line of fs.readFileSync(ignorePath, 'utf-8').split('\n')) {
55
- const trimmed = line.trim();
56
- if (!trimmed || trimmed.startsWith('#'))
57
- continue;
58
- try {
59
- const str = trimmed
60
- .replace(/\./g, '\\.')
61
- .replace(/\*\*/g, '.*')
62
- .replace(/\*/g, '[^/]*');
63
- patterns.push(new RegExp(`^${str}$`));
85
+ const disable_1 = require("./disable");
86
+ const parser_1 = require("@atlisp/parser");
87
+ function runChecks(content, file, config) {
88
+ const issues = [];
89
+ const disableMap = (0, disable_1.parseDisableComments)(content);
90
+ // Parse AST once for all checks that need it
91
+ const ast = (0, parser_1.parseAst)(content);
92
+ function addIfEnabled(rule, fn) {
93
+ const severity = config.checks[rule];
94
+ if (severity === 'off')
95
+ return;
96
+ const results = fn();
97
+ for (const r of results) {
98
+ if (!(0, disable_1.isDisabled)(r.rule, r.line, disableMap)) {
99
+ issues.push(r);
100
+ }
64
101
  }
65
- catch { /* skip invalid patterns */ }
66
102
  }
67
- return patterns;
103
+ addIfEnabled('encoding', () => (0, encoding_1.checkEncoding)(content, file));
104
+ addIfEnabled('parens', () => (0, parens_1.checkParens)(content, file));
105
+ addIfEnabled('cl_syntax', () => (0, cl_syntax_1.checkClSyntax)(content, file, config.cl_syntax.keywords));
106
+ addIfEnabled('quit_exit', () => (0, dangerous_calls_1.checkDangerousCallsAst)(ast, file, config.dangerous_calls));
107
+ addIfEnabled('vlax_without_loading', () => (0, vlax_1.checkVlaxWithoutLoading)(content, file));
108
+ addIfEnabled('token_in_url', () => (0, token_in_url_1.checkTokenInUrl)(content, file));
109
+ addIfEnabled('open_without_close', () => (0, open_close_1.checkOpenWithoutCloseAst)(ast, file));
110
+ addIfEnabled('bare_function_names', () => (0, bare_names_1.checkBareFunctionNames)(content, file, config.bare_function_names.allowlist, config.bare_function_names.namespace_pattern));
111
+ addIfEnabled('module_registration', () => (0, module_reg_1.checkModuleRegistration)(content, file, file, config.module_registration));
112
+ addIfEnabled('namespace_header', () => (0, namespace_header_1.checkNamespaceHeader)(content, file, config.namespace_header));
113
+ addIfEnabled('line_length', () => (0, line_length_1.checkLineLength)(content, file, config.line_length.max, config.line_length.tab_width));
114
+ addIfEnabled('function_complexity', () => (0, function_complexity_1.checkFunctionComplexityAst)(ast, file, config.function_complexity.max_lines, config.function_complexity.max_nesting));
115
+ addIfEnabled('parameter_naming', () => (0, parameter_naming_1.checkParameterNamingAst)(ast, file));
116
+ addIfEnabled('unused_variable', () => (0, unused_variable_1.checkUnusedVariableAst)(ast, file));
117
+ addIfEnabled('missing_doc', () => (0, missing_doc_1.checkMissingDoc)(content, file));
118
+ addIfEnabled('trailing_whitespace', () => (0, trailing_ws_1.checkTrailingWhitespace)(content, file));
119
+ addIfEnabled('unused_parameter', () => (0, unused_param_1.checkUnusedParameterAst)(ast, file));
120
+ addIfEnabled('constant_condition', () => (0, constant_condition_1.checkConstantConditionAst)(ast, file));
121
+ addIfEnabled('redundant_progn', () => (0, redundant_progn_1.checkRedundantPrognAst)(ast, file));
122
+ addIfEnabled('empty_branch', () => (0, empty_branch_1.checkEmptyBranchAst)(ast, file));
123
+ addIfEnabled('unused_let_binding', () => (0, unused_let_1.checkUnusedLetBindingAst)(ast, file));
124
+ addIfEnabled('recursive_call', () => (0, recursive_call_1.checkRecursiveCallAst)(ast, file));
125
+ addIfEnabled('variable_shadow', () => (0, variable_shadow_1.checkVariableShadowAst)(ast, file));
126
+ addIfEnabled('redundant_cond', () => (0, redundant_cond_1.checkRedundantCondAst)(ast, file));
127
+ addIfEnabled('unused_local_fun', () => (0, unused_local_fun_1.checkUnusedLocalFunAst)(ast, file));
128
+ addIfEnabled('multiple_setq', () => (0, multiple_setq_1.checkMultipleSetqAst)(ast, file));
129
+ addIfEnabled('redundant_quotes', () => (0, redundant_quotes_1.checkRedundantQuotesAst)(ast, file));
130
+ addIfEnabled('trailing_paren', () => (0, trailing_paren_1.checkTrailingParen)(content, file));
131
+ addIfEnabled('empty_comment', () => (0, empty_comments_1.checkEmptyComments)(content, file));
132
+ addIfEnabled('redundant_setq', () => (0, redundant_setq_1.checkRedundantSetqAst)(ast, file));
133
+ addIfEnabled('redundant_nil_else', () => (0, redundant_nil_else_1.checkRedundantNilElseAst)(ast, file));
134
+ addIfEnabled('single_arg_and_or', () => (0, single_arg_and_or_1.checkSingleArgAndOrAst)(ast, file));
135
+ addIfEnabled('redundant_let', () => (0, redundant_let_1.checkRedundantLetAst)(ast, file));
136
+ addIfEnabled('self_compare', () => (0, self_compare_1.checkSelfCompareAst)(ast, file));
137
+ addIfEnabled('misplaced_else', () => (0, misplaced_else_1.checkMisplacedElseAst)(ast, file));
138
+ addIfEnabled('quote_vs_function', () => (0, quote_vs_function_1.checkQuoteVsFunctionAst)(ast, file));
139
+ addIfEnabled('commented_code', () => (0, commented_code_1.checkCommentedCode)(content, file));
140
+ addIfEnabled('double_not', () => (0, double_not_1.checkDoubleNotAst)(ast, file));
141
+ return issues;
68
142
  }
69
- function lintFiles(files, config, rootDir, options) {
143
+ /** Single-pass visitor-based runChecks using AstVisitor */
144
+ function runChecksWithVisitor(content, file, config) {
145
+ const issues = [];
146
+ const disableMap = (0, disable_1.parseDisableComments)(content);
147
+ const ast = (0, parser_1.parseAst)(content);
148
+ const visitor = new parser_1.AstVisitor();
149
+ const checks = config.checks;
150
+ function addIssue(rule, line, severity, message) {
151
+ if (severity === 'off')
152
+ return;
153
+ if (!(0, disable_1.isDisabled)(rule, line, disableMap)) {
154
+ issues.push({ file, line, severity: severity, rule, message });
155
+ }
156
+ }
157
+ // Register all AST-based checks as visitor handlers
158
+ if (checks['redundant_quotes'] !== 'off') {
159
+ visitor.on('quote', (node) => {
160
+ if (node.children &&
161
+ node.children.length >= 2 &&
162
+ node.children[1].type === 'list' &&
163
+ node.children[1].children &&
164
+ node.children[1].children.length > 0 &&
165
+ node.children[1].children[0].type === 'symbol' &&
166
+ node.children[1].children[0].name === 'quote') {
167
+ addIssue('redundant_quotes', node.pos.line, checks['redundant_quotes'], (0, locale_1.t)('redundant_quotes'));
168
+ }
169
+ });
170
+ }
171
+ if (checks['double_not'] !== 'off') {
172
+ visitor.on('not', (node) => {
173
+ if (node.children &&
174
+ node.children.length >= 2 &&
175
+ node.children[1].type === 'list' &&
176
+ node.children[1].children &&
177
+ node.children[1].children.length > 0 &&
178
+ node.children[1].children[0].type === 'symbol' &&
179
+ node.children[1].children[0].name === 'not') {
180
+ addIssue('double_not', node.pos.line, checks['double_not'], (0, locale_1.t)('double_not'));
181
+ }
182
+ });
183
+ }
184
+ if (checks['misplaced_else'] !== 'off') {
185
+ visitor.on('if', (node) => {
186
+ if (node.children && node.children.length === 4) {
187
+ const cond = node.children[1];
188
+ if (cond.type === 'list' &&
189
+ cond.children &&
190
+ cond.children.length >= 2 &&
191
+ cond.children[0].type === 'symbol' &&
192
+ cond.children[0].name === 'not') {
193
+ const innerName = cond.children[1].name || '';
194
+ addIssue('misplaced_else', node.pos.line, checks['misplaced_else'], (0, locale_1.t)('misplaced_else', innerName));
195
+ }
196
+ }
197
+ });
198
+ }
199
+ if (checks['quote_vs_function'] !== 'off') {
200
+ const higherOrder = new Set([
201
+ 'mapcar',
202
+ 'apply',
203
+ 'lambda',
204
+ 'vl-sort',
205
+ 'vl-sort-i',
206
+ 'vl-remove-if',
207
+ 'vl-remove-if-not',
208
+ 'vl-member-if',
209
+ 'vl-some',
210
+ 'vl-every',
211
+ ]);
212
+ visitor.on('quote', (node) => {
213
+ if (node.children &&
214
+ node.children.length >= 2 &&
215
+ node.children[1].type === 'list' &&
216
+ node.children[1].children &&
217
+ node.children[1].children.length > 0 &&
218
+ node.children[1].children[0].type === 'symbol' &&
219
+ node.children[1].children[0].name === 'lambda') {
220
+ const parent = node.parent;
221
+ if (parent && parent.children && parent.children.length > 0) {
222
+ const pName = (parent.children[0].type === 'symbol' && parent.children[0].name) || '';
223
+ if (higherOrder.has(pName)) {
224
+ addIssue('quote_vs_function', node.pos.line, checks['quote_vs_function'], (0, locale_1.t)('quote_vs_function', pName));
225
+ }
226
+ }
227
+ }
228
+ });
229
+ }
230
+ visitor.run(ast);
231
+ return issues;
232
+ }
233
+ function lintFiles(files, config, rootDir) {
70
234
  (0, locale_1.setLocale)(config.locale || 'zh');
71
235
  const allIssues = [];
72
- const ignorePatterns = options?.ignorePatterns || [];
73
- const useCache = options?.useCache ?? false;
74
- for (let idx = 0; idx < files.length; idx++) {
75
- const filepath = files[idx];
236
+ for (const filepath of files) {
76
237
  const relPath = path.relative(rootDir, filepath);
77
- // Check ignore patterns
78
- const relForward = relPath.replace(/\\/g, '/');
79
- if (ignorePatterns.some(re => re.test(relForward)))
80
- continue;
81
- // Check cache
82
- if (useCache && (0, cache_1.isCached)(filepath, rootDir))
83
- continue;
84
- options?.onProgress?.(idx + 1, files.length);
85
238
  let content;
86
239
  try {
87
240
  content = fs.readFileSync(filepath, 'utf-8');
88
241
  }
89
242
  catch {
90
243
  allIssues.push({
91
- file: relPath, line: 1, severity: 'error', rule: 'read',
244
+ file: relPath,
245
+ line: 1,
246
+ severity: 'error',
247
+ rule: 'read',
92
248
  message: (0, locale_1.t)('runner.read_error'),
93
249
  });
94
250
  continue;
@@ -96,61 +252,63 @@ function lintFiles(files, config, rootDir, options) {
96
252
  // Per-directory config override
97
253
  const override = (0, config_1.findOverrides)(filepath);
98
254
  const effectiveConfig = override ? (0, config_1.mergeOverrides)(config, override) : config;
99
- const fileIssues = (0, index_1.runChecks)(content, relPath, effectiveConfig);
100
- // Mark as cached if no issues found
101
- if (useCache && fileIssues.length === 0) {
102
- (0, cache_1.markCached)(filepath, rootDir);
103
- }
104
- allIssues.push(...fileIssues);
255
+ allIssues.push(...runChecks(content, relPath, effectiveConfig));
105
256
  }
106
257
  return allIssues;
107
258
  }
108
- function lintFilesParallel(files, config, rootDir, options) {
259
+ function lintFilesParallel(files, config, rootDir) {
109
260
  (0, locale_1.setLocale)(config.locale || 'zh');
110
- const ignorePatterns = options?.ignorePatterns || [];
111
261
  return new Promise((resolve) => {
112
262
  const allIssues = [];
113
263
  let completed = 0;
114
264
  const maxWorkers = Math.min(os.cpus().length, files.length);
115
265
  let nextIndex = 0;
116
266
  function startWorker() {
117
- if (nextIndex >= files.length) {
118
- if (completed >= files.length)
119
- resolve(allIssues);
267
+ if (nextIndex >= files.length)
120
268
  return;
121
- }
122
269
  const idx = nextIndex++;
123
270
  const filepath = files[idx];
124
271
  const relPath = path.relative(rootDir, filepath);
125
- // Check ignore patterns
126
- const relForward = relPath.replace(/\\/g, '/');
127
- if (ignorePatterns.some(re => re.test(relForward))) {
128
- completed++;
129
- startWorker();
130
- if (completed >= files.length)
131
- resolve(allIssues);
132
- return;
133
- }
134
272
  const worker = new worker_threads_1.Worker(path.join(__dirname, 'worker.js'), {
135
273
  workerData: { filepath, relPath, config },
136
- eval: false,
137
274
  });
138
275
  worker.on('message', (msg) => {
139
276
  allIssues.push(...msg.issues);
140
277
  completed++;
141
- options?.onProgress?.(completed, files.length);
142
278
  worker.terminate();
143
279
  startWorker();
144
280
  if (completed >= files.length)
145
281
  resolve(allIssues);
146
282
  });
147
- worker.on('error', () => {
283
+ worker.on('error', (err) => {
284
+ allIssues.push({
285
+ file: relPath,
286
+ line: 1,
287
+ severity: 'error',
288
+ rule: 'worker',
289
+ message: err.message,
290
+ });
148
291
  completed++;
149
292
  worker.terminate();
150
293
  startWorker();
151
294
  if (completed >= files.length)
152
295
  resolve(allIssues);
153
296
  });
297
+ worker.on('exit', (code) => {
298
+ if (code !== 0) {
299
+ allIssues.push({
300
+ file: relPath,
301
+ line: 1,
302
+ severity: 'error',
303
+ rule: 'worker',
304
+ message: `Worker exited with code ${code}`,
305
+ });
306
+ completed++;
307
+ startWorker();
308
+ if (completed >= files.length)
309
+ resolve(allIssues);
310
+ }
311
+ });
154
312
  }
155
313
  for (let i = 0; i < maxWorkers; i++) {
156
314
  startWorker();
@@ -159,34 +317,65 @@ function lintFilesParallel(files, config, rootDir, options) {
159
317
  resolve([]);
160
318
  });
161
319
  }
320
+ const UTF8_BOM = 0xfeff;
162
321
  function fixFile(filepath) {
163
322
  const fixes = [];
164
323
  let content = fs.readFileSync(filepath, 'utf-8');
165
324
  // Fix trailing whitespace
166
- const wsFixed = content.split('\n').map(l => l.replace(/[ \t]+$/, '')).join('\n');
325
+ const wsFixed = content
326
+ .split('\n')
327
+ .map((l) => l.replace(/[ \t]+$/, ''))
328
+ .join('\n');
167
329
  if (wsFixed !== content) {
168
330
  content = wsFixed;
169
331
  fixes.push('trailing_whitespace');
170
332
  }
171
333
  // Fix UTF-8 BOM
172
- if (content.charCodeAt(0) === 0xFEFF) {
334
+ if (content.charCodeAt(0) === UTF8_BOM) {
173
335
  content = content.slice(1);
174
336
  fixes.push('encoding (BOM)');
175
337
  }
176
- // Fix comment style: ;text -> ; text
177
- const commentFixed = content.split('\n').map(l => {
178
- return l.replace(/^(\s*);([^ ;|])/gm, '$1; $2');
179
- }).join('\n');
180
- if (commentFixed !== content) {
181
- content = commentFixed;
182
- fixes.push('comment_style');
338
+ // Fix trailing parens: remove excess ')' from end of content
339
+ let openCount = 0;
340
+ let closeCount = 0;
341
+ let fixContent = content;
342
+ for (let i = 0; i < fixContent.length; i++) {
343
+ const ch = fixContent[i];
344
+ if (ch === '(')
345
+ openCount++;
346
+ else if (ch === ')')
347
+ closeCount++;
348
+ }
349
+ if (closeCount > openCount) {
350
+ let excess = closeCount - openCount;
351
+ while (excess > 0 && fixContent.endsWith(')')) {
352
+ fixContent = fixContent.slice(0, -1);
353
+ excess--;
354
+ }
355
+ // Also trim trailing whitespace after removing parens
356
+ fixContent = fixContent.replace(/\n[ \t]*$/, '');
357
+ if (fixContent !== content) {
358
+ content = fixContent;
359
+ fixes.push('trailing_paren');
360
+ }
361
+ }
362
+ // Fix empty comments: remove lines that are only ';' with optional whitespace
363
+ const lines = content.split('\n');
364
+ let changed = false;
365
+ const fixedLines = lines.map((l) => {
366
+ if (/^\s*;\s*$/.test(l)) {
367
+ changed = true;
368
+ return '';
369
+ }
370
+ return l;
371
+ });
372
+ if (changed) {
373
+ content = fixedLines.join('\n');
374
+ fixes.push('empty_comment');
183
375
  }
184
376
  if (fixes.length > 0) {
185
377
  fs.writeFileSync(filepath, content, 'utf-8');
186
378
  }
187
379
  return fixes;
188
380
  }
189
- function clearCache(rootDir) {
190
- (0, cache_1.clearCache)(rootDir);
191
- }
192
381
  //# sourceMappingURL=runner.js.map
package/dist/sbcl.js CHANGED
@@ -60,22 +60,26 @@ function runSbclLint(srcDir, sbclConfig, stubPackagesPath) {
60
60
  // Locate lint-sbcl.lisp bundled with the package
61
61
  const scriptPath = path.join(__dirname, '..', 'lib', 'lint-sbcl.lisp');
62
62
  if (!fs.existsSync(scriptPath)) {
63
- return [{
64
- file: 'internal', line: 1, severity: 'error', rule: 'sbcl',
63
+ return [
64
+ {
65
+ file: 'internal',
66
+ line: 1,
67
+ severity: 'error',
68
+ rule: 'sbcl',
65
69
  message: (0, locale_1.t)('sbcl.script_not_found', scriptPath),
66
- }];
70
+ },
71
+ ];
67
72
  }
68
73
  try {
69
74
  // Convert arrays to Lisp-readable format: ("a" "b")
70
- const toLispList = (arr) => '(' + arr.map(s => `"${s}"`).join(' ') + ')';
71
- const locale = (0, locale_1.getLocale)();
75
+ const toLispList = (arr) => '(' + arr.map((s) => `"${s}"`).join(' ') + ')';
72
76
  const result = (0, child_process_1.execFileSync)(sbcl, [
73
- '--script', scriptPath,
77
+ '--script',
78
+ scriptPath,
74
79
  srcDir,
75
80
  stubPackagesPath,
76
81
  toLispList(sbclConfig.walk_exclude),
77
82
  toLispList(sbclConfig.defmacro_allow_files),
78
- locale,
79
83
  ], {
80
84
  encoding: 'utf-8',
81
85
  maxBuffer: 50 * 1024 * 1024,
@@ -111,9 +115,7 @@ function parseSbclOutput(output, srcDir) {
111
115
  const severity = sev === 'ERROR' ? 'error' : 'warn';
112
116
  const lineno = lineStr ? parseInt(lineStr, 10) : 1;
113
117
  // Resolve relative path against srcDir
114
- const resolved = filePath.includes(':')
115
- ? filePath
116
- : filePath.replace(/\\/g, '/');
118
+ const resolved = filePath.includes(':') ? filePath : filePath.replace(/\\/g, '/');
117
119
  issues.push({
118
120
  file: resolved,
119
121
  line: lineno,
@@ -128,7 +130,10 @@ function parseSbclOutput(output, srcDir) {
128
130
  if (sev) {
129
131
  const msg = trimmed.replace(/^\[(ERROR|WARN|NOTE)\]\s*/, '');
130
132
  issues.push({
131
- file: srcDir, line: 1, severity: sev, rule: 'sbcl',
133
+ file: srcDir,
134
+ line: 1,
135
+ severity: sev,
136
+ rule: 'sbcl',
132
137
  message: msg,
133
138
  });
134
139
  }
package/dist/worker.js CHANGED
@@ -3,23 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const worker_threads_1 = require("worker_threads");
4
4
  const fs_1 = require("fs");
5
5
  const locale_1 = require("./locale");
6
- const index_1 = require("./checks/index");
6
+ const runner_1 = require("./runner");
7
7
  if (worker_threads_1.parentPort && worker_threads_1.workerData) {
8
8
  const input = worker_threads_1.workerData;
9
9
  (0, locale_1.setLocale)(input.config.locale || 'zh');
10
10
  try {
11
11
  const content = (0, fs_1.readFileSync)(input.filepath, 'utf-8');
12
- const issues = (0, index_1.runChecks)(content, input.relPath, input.config);
12
+ const issues = (0, runner_1.runChecks)(content, input.relPath, input.config);
13
13
  const output = { filepath: input.filepath, issues };
14
14
  worker_threads_1.parentPort.postMessage(output);
15
15
  }
16
16
  catch {
17
17
  const output = {
18
18
  filepath: input.filepath,
19
- issues: [{
20
- file: input.relPath, line: 1, severity: 'error', rule: 'read',
19
+ issues: [
20
+ {
21
+ file: input.relPath,
22
+ line: 1,
23
+ severity: 'error',
24
+ rule: 'read',
21
25
  message: 'Cannot read file',
22
- }],
26
+ },
27
+ ],
23
28
  };
24
29
  worker_threads_1.parentPort.postMessage(output);
25
30
  }
@@ -1,49 +1,14 @@
1
1
  ;; @lisp SBCL Lint — syntax validation via SBCL
2
- ;; Usage: sbcl --script lint-sbcl.lisp <src-dir> <stub-packages.json> <walk-exclude-json> <defmacro-allow-files-json> <locale>
2
+ ;; Usage: sbcl --script lint-sbcl.lisp <src-dir> <stub-packages.json> <walk-exclude-json> <defmacro-allow-files-json>
3
3
 
4
4
  (require :uiop)
5
5
 
6
6
  (defparameter *errors* 0)
7
7
  (defparameter *warnings* 0)
8
8
  (defparameter *checked* 0)
9
- (defparameter *locale* "zh")
10
-
11
- ;; ── i18n ──────────────────────────────────────────────────────────────
12
- (defun i18n (key)
13
- (cdr (assoc key
14
- (if (string= *locale* "zh")
15
- '((:not-found . "not found symbol")
16
- (:syntax-error . "syntax error")
17
- (:vec-syntax . "#( vector syntax")
18
- (:char-syntax . "# backslash char literal")
19
- (:eval-syntax . "#. read-time eval")
20
- (:defmacro . "defmacro")
21
- (:trailing-ws . "trailing whitespace")
22
- (:ok . "OK")
23
- (:fail . "FAIL")
24
- (:header . " @lisp SBCL Lint")
25
- (:source . " Source")
26
- (:found . " Found ~d .lsp files")
27
- (:scanned . " Scanned")
28
- (:errors . " Errors")
29
- (:warnings . " Warnings"))
30
- '((:not-found . "not found symbol")
31
- (:syntax-error . "syntax error")
32
- (:vec-syntax . "#( vector syntax")
33
- (:char-syntax . "# backslash char literal")
34
- (:eval-syntax . "#. read-time eval")
35
- (:defmacro . "defmacro")
36
- (:trailing-ws . "trailing whitespace")
37
- (:ok . "OK")
38
- (:fail . "FAIL")
39
- (:header . " @lisp SBCL Lint")
40
- (:source . " Source")
41
- (:found . " Found ~d .lsp files")
42
- (:scanned . " Scanned")
43
- (:errors . " Errors")
44
- (:warnings . " Warnings"))))))
45
9
 
46
10
  ;; ── Stub packages from external file ──────────────────────────────────
11
+ ;; File format: alist ((name use*) ...) — valid Lisp data
47
12
  (defun load-stub-packages (file-path)
48
13
  (flet ((ensure-pkg (name &optional use-list)
49
14
  (unless (find-package name)
@@ -87,10 +52,11 @@
87
52
  (let ((msg (princ-to-string e)))
88
53
  (cond
89
54
  ((search "not found" msg)
90
- (format t "~% [NOTE] ~a: ~a~%" rel (i18n :not-found))
55
+ (format t "~% [NOTE] ~a: ~a~%" rel
56
+ (subseq msg 0 (min 80 (length msg))))
91
57
  (incf *warnings*))
92
58
  (t
93
- (format t "~a~% [ERROR] ~a: ~a~%" (i18n :fail) rel (i18n :syntax-error))
59
+ (format t "FAIL~% [ERROR] ~a: ~a~%" rel e)
94
60
  (incf *errors*)
95
61
  (return-from check-file))))))
96
62
  ;; Trailing whitespace
@@ -101,7 +67,7 @@
101
67
  when (and (> (length line) 0)
102
68
  (find (char line (1- (length line))) '(#\Space #\Tab)))
103
69
  do (progn
104
- (format t "~% [WARN] ~a line ~d: ~a~%" rel lineno (i18n :trailing-ws))
70
+ (format t "~% [WARN] ~a line ~d: trailing whitespace~%" rel lineno)
105
71
  (incf *warnings*))))
106
72
  ;; CL-ism checks: raw content scan
107
73
  (with-open-file (s path :direction :input)
@@ -109,19 +75,19 @@
109
75
  (file-position s 0)
110
76
  (read-sequence content s)
111
77
  (when (search "#(" content)
112
- (format t "~% [WARN] ~a: ~a~%" rel (i18n :vec-syntax))
78
+ (format t "~% [WARN] ~a: #( vector syntax (not valid in AutoLISP)~%" rel)
113
79
  (incf *warnings*))
114
80
  (when (search "#\\" content)
115
- (format t "~% [WARN] ~a: ~a~%" rel (i18n :char-syntax))
81
+ (format t "~% [WARN] ~a: #\\ character literal syntax (not valid in AutoLISP)~%" rel)
116
82
  (incf *warnings*))
117
83
  (when (search "#." content)
118
- (format t "~% [WARN] ~a: ~a~%" rel (i18n :eval-syntax))
84
+ (format t "~% [WARN] ~a: #. read-time eval syntax (not valid in AutoLISP)~%" rel)
119
85
  (incf *warnings*))
120
86
  (when (and (search "defmacro" content)
121
87
  (not (some (lambda (s) (search s fullname)) defmacro-allow-files)))
122
- (format t "~% [WARN] ~a: ~a~%" rel (i18n :defmacro))
88
+ (format t "~% [WARN] ~a: defmacro (not valid in AutoLISP)~%" rel)
123
89
  (incf *warnings*))))
124
- (format t "~a~%" (i18n :ok))))
90
+ (format t "OK~%")))
125
91
 
126
92
  ;; ── Main ──────────────────────────────────────────────────────────────
127
93
  (defun main ()
@@ -133,26 +99,25 @@
133
99
  '(".vscode" "test" "experiment" "tools" ".git")))
134
100
  (defmacro-allow (if (fourth args)
135
101
  (read-from-string (fourth args))
136
- '("compat-cl")))
137
- (locale (fifth args)))
138
- (when locale (setf *locale* locale))
102
+ '("compat-cl"))))
103
+ ;; Load stub packages for CAD runtime symbols
139
104
  (load-stub-packages stub-file)
140
105
 
141
106
  (format t "~%==================================================~%")
142
- (format t "~a~%" (i18n :header))
143
- (format t "~a: ~a~%" (i18n :source) (namestring (pathname src-dir)))
107
+ (format t " @lisp SBCL Lint~%")
108
+ (format t " Source: ~a~%" (namestring (pathname src-dir)))
144
109
  (format t "==================================================~%~%")
145
110
  (setf *errors* 0 *warnings* 0 *checked* 0)
146
111
 
147
112
  (let ((files (walk-tree src-dir walk-exclude)))
148
- (format t "~a~%~%" (format nil (i18n :found) (length files)))
113
+ (format t " Found ~d .lsp files~%~%" (length files))
149
114
  (dolist (f files)
150
115
  (check-file f src-dir defmacro-allow)))
151
116
 
152
117
  (format t "~%==================================================~%")
153
- (format t "~a: ~d~%" (i18n :scanned) *checked*)
154
- (format t "~a: ~d~%" (i18n :errors) *errors*)
155
- (format t "~a: ~d~%" (i18n :warnings) *warnings*)
118
+ (format t " Scanned: ~d files~%" *checked*)
119
+ (format t " Errors: ~d~%" *errors*)
120
+ (format t " Warnings: ~d~%" *warnings*)
156
121
  (format t "==================================================~%~%")
157
122
  (if (> *errors* 0)
158
123
  (uiop:quit 1)