@atlisp/lint 0.1.16 → 0.1.19
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/README.md +3 -4
- package/atlisp-lint.default.json +12 -5
- package/dist/checks/bare-names.js +2 -0
- package/dist/checks/constant-condition.js +3 -0
- package/dist/checks/empty-catch.js +68 -23
- package/dist/checks/entget-in-loop.d.ts +3 -0
- package/dist/checks/entget-in-loop.js +56 -0
- package/dist/checks/if-without-else.d.ts +4 -0
- package/dist/checks/if-without-else.js +31 -0
- package/dist/checks/long-function-call.js +1 -1
- package/dist/checks/promise-handler.d.ts +4 -0
- package/dist/checks/promise-handler.js +53 -0
- package/dist/checks/redundant-list.d.ts +4 -0
- package/dist/checks/redundant-list.js +22 -0
- package/dist/checks/string-concat-loop.d.ts +3 -0
- package/dist/checks/string-concat-loop.js +59 -0
- package/dist/checks/undeclared-setq.js +2 -0
- package/dist/checks/unused-catch-result.d.ts +3 -0
- package/dist/checks/unused-catch-result.js +28 -0
- package/dist/config.js +11 -4
- package/dist/formatters-html.d.ts +3 -0
- package/dist/formatters-html.js +91 -0
- package/dist/formatters-sarif.d.ts +3 -0
- package/dist/formatters-sarif.js +55 -0
- package/dist/index.js +41 -2
- package/dist/locale.js +23 -7
- package/dist/presets.js +16 -2
- package/dist/project.js +180 -2
- package/dist/rules.js +8 -1
- package/dist/runner.d.ts +1 -2
- package/dist/runner.js +106 -224
- package/dist/validate.js +8 -1
- package/dist/visitor-runner.d.ts +4 -0
- package/dist/visitor-runner.js +709 -0
- package/package.json +1 -1
package/dist/runner.js
CHANGED
|
@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.runChecks = runChecks;
|
|
37
|
-
exports.
|
|
37
|
+
exports.runChecksWithVisitorWrapper = runChecksWithVisitorWrapper;
|
|
38
38
|
exports.lintFiles = lintFiles;
|
|
39
39
|
exports.lintFilesParallel = lintFilesParallel;
|
|
40
40
|
exports.applyFixes = applyFixes;
|
|
@@ -47,46 +47,14 @@ const os = __importStar(require("os"));
|
|
|
47
47
|
const encoding_1 = require("./checks/encoding");
|
|
48
48
|
const parens_1 = require("./checks/parens");
|
|
49
49
|
const cl_syntax_1 = require("./checks/cl-syntax");
|
|
50
|
-
const dangerous_calls_1 = require("./checks/dangerous-calls");
|
|
51
50
|
const vlax_1 = require("./checks/vlax");
|
|
52
51
|
const token_in_url_1 = require("./checks/token-in-url");
|
|
53
|
-
const open_close_1 = require("./checks/open-close");
|
|
54
52
|
const bare_names_1 = require("./checks/bare-names");
|
|
55
53
|
const module_reg_1 = require("./checks/module-reg");
|
|
56
54
|
const namespace_header_1 = require("./checks/namespace-header");
|
|
57
55
|
const trailing_ws_1 = require("./checks/trailing-ws");
|
|
58
56
|
const line_length_1 = require("./checks/line-length");
|
|
59
|
-
const function_complexity_1 = require("./checks/function-complexity");
|
|
60
|
-
const parameter_naming_1 = require("./checks/parameter-naming");
|
|
61
|
-
const unused_variable_1 = require("./checks/unused-variable");
|
|
62
57
|
const missing_doc_1 = require("./checks/missing-doc");
|
|
63
|
-
const unused_param_1 = require("./checks/unused-param");
|
|
64
|
-
const constant_condition_1 = require("./checks/constant-condition");
|
|
65
|
-
const redundant_progn_1 = require("./checks/redundant-progn");
|
|
66
|
-
const empty_branch_1 = require("./checks/empty-branch");
|
|
67
|
-
const unused_let_1 = require("./checks/unused-let");
|
|
68
|
-
const recursive_call_1 = require("./checks/recursive-call");
|
|
69
|
-
const variable_shadow_1 = require("./checks/variable-shadow");
|
|
70
|
-
const redundant_cond_1 = require("./checks/redundant-cond");
|
|
71
|
-
const unused_local_fun_1 = require("./checks/unused-local-fun");
|
|
72
|
-
const multiple_setq_1 = require("./checks/multiple-setq");
|
|
73
|
-
const redundant_quotes_1 = require("./checks/redundant-quotes");
|
|
74
|
-
const trailing_paren_1 = require("./checks/trailing-paren");
|
|
75
|
-
const empty_comments_1 = require("./checks/empty-comments");
|
|
76
|
-
const redundant_setq_1 = require("./checks/redundant-setq");
|
|
77
|
-
const redundant_nil_else_1 = require("./checks/redundant-nil-else");
|
|
78
|
-
const single_arg_and_or_1 = require("./checks/single-arg-and-or");
|
|
79
|
-
const redundant_let_1 = require("./checks/redundant-let");
|
|
80
|
-
const self_compare_1 = require("./checks/self-compare");
|
|
81
|
-
const misplaced_else_1 = require("./checks/misplaced-else");
|
|
82
|
-
const quote_vs_function_1 = require("./checks/quote-vs-function");
|
|
83
|
-
const commented_code_1 = require("./checks/commented-code");
|
|
84
|
-
const double_not_1 = require("./checks/double-not");
|
|
85
|
-
const setq_single_arg_1 = require("./checks/setq-single-arg");
|
|
86
|
-
const assoc_without_cdr_1 = require("./checks/assoc-without-cdr");
|
|
87
|
-
const identical_branches_1 = require("./checks/identical-branches");
|
|
88
|
-
const while_constant_1 = require("./checks/while-constant");
|
|
89
|
-
const cond_duplicate_1 = require("./checks/cond-duplicate");
|
|
90
58
|
const error_handling_1 = require("./checks/error-handling");
|
|
91
59
|
const global_naming_1 = require("./checks/global-naming");
|
|
92
60
|
const extra_parens_1 = require("./checks/extra-parens");
|
|
@@ -113,53 +81,22 @@ const type_check_1 = require("./checks/type-check");
|
|
|
113
81
|
const redundant_if_1 = require("./checks/redundant-if");
|
|
114
82
|
const undeclared_setq_1 = require("./checks/undeclared-setq");
|
|
115
83
|
const format_indent_1 = require("./checks/format-indent");
|
|
84
|
+
const trailing_paren_1 = require("./checks/trailing-paren");
|
|
85
|
+
const empty_comments_1 = require("./checks/empty-comments");
|
|
86
|
+
const commented_code_1 = require("./checks/commented-code");
|
|
87
|
+
const unused_catch_result_1 = require("./checks/unused-catch-result");
|
|
88
|
+
const string_concat_loop_1 = require("./checks/string-concat-loop");
|
|
116
89
|
const config_1 = require("./config");
|
|
117
90
|
const locale_1 = require("./locale");
|
|
118
91
|
const disable_1 = require("./disable");
|
|
119
92
|
const parser_1 = require("@atlisp/parser");
|
|
93
|
+
const visitor_runner_1 = require("./visitor-runner");
|
|
120
94
|
function runChecks(content, file, config) {
|
|
121
95
|
const issues = [];
|
|
122
96
|
const disableMap = (0, disable_1.parseDisableComments)(content);
|
|
123
|
-
// Deferred AST parsing: only parse if any AST-based checks are enabled
|
|
124
|
-
const astRules = new Set([
|
|
125
|
-
'quit_exit',
|
|
126
|
-
'open_without_close',
|
|
127
|
-
'function_complexity',
|
|
128
|
-
'parameter_naming',
|
|
129
|
-
'unused_variable',
|
|
130
|
-
'unused_parameter',
|
|
131
|
-
'constant_condition',
|
|
132
|
-
'redundant_progn',
|
|
133
|
-
'empty_branch',
|
|
134
|
-
'unused_let_binding',
|
|
135
|
-
'recursive_call',
|
|
136
|
-
'variable_shadow',
|
|
137
|
-
'redundant_cond',
|
|
138
|
-
'unused_local_fun',
|
|
139
|
-
'multiple_setq',
|
|
140
|
-
'redundant_quotes',
|
|
141
|
-
'redundant_setq',
|
|
142
|
-
'redundant_nil_else',
|
|
143
|
-
'single_arg_and_or',
|
|
144
|
-
'redundant_let',
|
|
145
|
-
'self_compare',
|
|
146
|
-
'misplaced_else',
|
|
147
|
-
'quote_vs_function',
|
|
148
|
-
'double_not',
|
|
149
|
-
'setq_single_arg',
|
|
150
|
-
'assoc_without_cdr',
|
|
151
|
-
'identical_branches',
|
|
152
|
-
'while_constant',
|
|
153
|
-
'cond_duplicate',
|
|
154
|
-
]);
|
|
155
97
|
const checks = config.checks;
|
|
156
|
-
const needsAst = Array.from(astRules).some(r => checks[r] !== 'off');
|
|
157
|
-
let ast;
|
|
158
|
-
if (needsAst) {
|
|
159
|
-
ast = (0, parser_1.parseAst)(content);
|
|
160
|
-
}
|
|
161
98
|
function addIfEnabled(rule, fn) {
|
|
162
|
-
const severity =
|
|
99
|
+
const severity = checks[rule];
|
|
163
100
|
if (!severity || severity === 'off')
|
|
164
101
|
return;
|
|
165
102
|
const results = fn();
|
|
@@ -169,49 +106,28 @@ function runChecks(content, file, config) {
|
|
|
169
106
|
}
|
|
170
107
|
}
|
|
171
108
|
}
|
|
109
|
+
function addIssues(ruleIssues) {
|
|
110
|
+
for (const r of ruleIssues) {
|
|
111
|
+
if (!(0, disable_1.isDisabled)(r.rule, r.line, disableMap)) {
|
|
112
|
+
issues.push(r);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Non-AST checks (run before AST parsing)
|
|
172
117
|
addIfEnabled('encoding', () => (0, encoding_1.checkEncoding)(content, file));
|
|
173
118
|
addIfEnabled('parens', () => (0, parens_1.checkParens)(content, file));
|
|
174
119
|
addIfEnabled('cl_syntax', () => (0, cl_syntax_1.checkClSyntax)(content, file, config.cl_syntax.keywords));
|
|
175
|
-
addIfEnabled('quit_exit', () => (0, dangerous_calls_1.checkDangerousCallsAst)(ast, file, config.dangerous_calls));
|
|
176
120
|
addIfEnabled('vlax_without_loading', () => (0, vlax_1.checkVlaxWithoutLoading)(content, file));
|
|
177
121
|
addIfEnabled('token_in_url', () => (0, token_in_url_1.checkTokenInUrl)(content, file));
|
|
178
|
-
addIfEnabled('open_without_close', () => (0, open_close_1.checkOpenWithoutCloseAst)(ast, file));
|
|
179
122
|
addIfEnabled('bare_function_names', () => (0, bare_names_1.checkBareFunctionNames)(content, file, config.bare_function_names.allowlist, config.bare_function_names.namespace_pattern));
|
|
180
123
|
addIfEnabled('module_registration', () => (0, module_reg_1.checkModuleRegistration)(content, file, file, config.module_registration));
|
|
181
124
|
addIfEnabled('namespace_header', () => (0, namespace_header_1.checkNamespaceHeader)(content, file, config.namespace_header));
|
|
182
125
|
addIfEnabled('line_length', () => (0, line_length_1.checkLineLength)(content, file, config.line_length.max, config.line_length.tab_width));
|
|
183
|
-
addIfEnabled('function_complexity', () => (0, function_complexity_1.checkFunctionComplexityAst)(ast, file, config.function_complexity.max_lines, config.function_complexity.max_nesting));
|
|
184
|
-
addIfEnabled('parameter_naming', () => (0, parameter_naming_1.checkParameterNamingAst)(ast, file));
|
|
185
|
-
addIfEnabled('unused_variable', () => (0, unused_variable_1.checkUnusedVariableAst)(ast, file));
|
|
186
126
|
addIfEnabled('missing_doc', () => (0, missing_doc_1.checkMissingDoc)(content, file));
|
|
187
127
|
addIfEnabled('trailing_whitespace', () => (0, trailing_ws_1.checkTrailingWhitespace)(content, file));
|
|
188
|
-
addIfEnabled('unused_parameter', () => (0, unused_param_1.checkUnusedParameterAst)(ast, file));
|
|
189
|
-
addIfEnabled('constant_condition', () => (0, constant_condition_1.checkConstantConditionAst)(ast, file));
|
|
190
|
-
addIfEnabled('redundant_progn', () => (0, redundant_progn_1.checkRedundantPrognAst)(ast, file));
|
|
191
|
-
addIfEnabled('empty_branch', () => (0, empty_branch_1.checkEmptyBranchAst)(ast, file));
|
|
192
|
-
addIfEnabled('unused_let_binding', () => (0, unused_let_1.checkUnusedLetBindingAst)(ast, file));
|
|
193
|
-
addIfEnabled('recursive_call', () => (0, recursive_call_1.checkRecursiveCallAst)(ast, file));
|
|
194
|
-
addIfEnabled('variable_shadow', () => (0, variable_shadow_1.checkVariableShadowAst)(ast, file));
|
|
195
|
-
addIfEnabled('redundant_cond', () => (0, redundant_cond_1.checkRedundantCondAst)(ast, file));
|
|
196
|
-
addIfEnabled('unused_local_fun', () => (0, unused_local_fun_1.checkUnusedLocalFunAst)(ast, file));
|
|
197
|
-
addIfEnabled('multiple_setq', () => (0, multiple_setq_1.checkMultipleSetqAst)(ast, file));
|
|
198
|
-
addIfEnabled('redundant_quotes', () => (0, redundant_quotes_1.checkRedundantQuotesAst)(ast, file));
|
|
199
128
|
addIfEnabled('trailing_paren', () => (0, trailing_paren_1.checkTrailingParen)(content, file));
|
|
200
129
|
addIfEnabled('empty_comment', () => (0, empty_comments_1.checkEmptyComments)(content, file));
|
|
201
|
-
addIfEnabled('redundant_setq', () => (0, redundant_setq_1.checkRedundantSetqAst)(ast, file));
|
|
202
|
-
addIfEnabled('redundant_nil_else', () => (0, redundant_nil_else_1.checkRedundantNilElseAst)(ast, file));
|
|
203
|
-
addIfEnabled('single_arg_and_or', () => (0, single_arg_and_or_1.checkSingleArgAndOrAst)(ast, file));
|
|
204
|
-
addIfEnabled('redundant_let', () => (0, redundant_let_1.checkRedundantLetAst)(ast, file));
|
|
205
|
-
addIfEnabled('self_compare', () => (0, self_compare_1.checkSelfCompareAst)(ast, file));
|
|
206
|
-
addIfEnabled('misplaced_else', () => (0, misplaced_else_1.checkMisplacedElseAst)(ast, file));
|
|
207
|
-
addIfEnabled('quote_vs_function', () => (0, quote_vs_function_1.checkQuoteVsFunctionAst)(ast, file));
|
|
208
130
|
addIfEnabled('commented_code', () => (0, commented_code_1.checkCommentedCode)(content, file));
|
|
209
|
-
addIfEnabled('double_not', () => (0, double_not_1.checkDoubleNotAst)(ast, file));
|
|
210
|
-
addIfEnabled('setq_single_arg', () => (0, setq_single_arg_1.checkSetqSingleArgAst)(ast, file));
|
|
211
|
-
addIfEnabled('assoc_without_cdr', () => (0, assoc_without_cdr_1.checkAssocWithoutCdrAst)(ast, file));
|
|
212
|
-
addIfEnabled('identical_branches', () => (0, identical_branches_1.checkIdenticalBranchesAst)(ast, file));
|
|
213
|
-
addIfEnabled('while_constant', () => (0, while_constant_1.checkWhileConstantAst)(ast, file));
|
|
214
|
-
addIfEnabled('cond_duplicate', () => (0, cond_duplicate_1.checkCondDuplicateAst)(ast, file));
|
|
215
131
|
addIfEnabled('error_handling', () => (0, error_handling_1.checkErrorHandling)(content, file));
|
|
216
132
|
addIfEnabled('global_naming', () => (0, global_naming_1.checkGlobalNaming)(content, file));
|
|
217
133
|
addIfEnabled('extra_parens', () => (0, extra_parens_1.checkExtraParens)(content, file));
|
|
@@ -238,97 +154,34 @@ function runChecks(content, file, config) {
|
|
|
238
154
|
addIfEnabled('redundant_if', () => (0, redundant_if_1.checkRedundantIf)(content, file));
|
|
239
155
|
addIfEnabled('undeclared_setq', () => (0, undeclared_setq_1.checkUndeclaredSetq)(content, file));
|
|
240
156
|
addIfEnabled('format_indent', () => (0, format_indent_1.checkFormatIndent)(content, file, config.line_length.tab_width));
|
|
157
|
+
// New checks
|
|
158
|
+
addIfEnabled('unused_catch_result', () => (0, unused_catch_result_1.checkUnusedCatchResult)(content, file));
|
|
159
|
+
addIfEnabled('string_concat_loop', () => (0, string_concat_loop_1.checkStringConcatInLoop)(content, file));
|
|
160
|
+
// AST-based checks via single-pass visitor
|
|
161
|
+
const astRules = new Set([
|
|
162
|
+
'quit_exit', 'open_without_close', 'function_complexity',
|
|
163
|
+
'parameter_naming', 'unused_variable', 'unused_parameter',
|
|
164
|
+
'constant_condition', 'redundant_progn', 'empty_branch',
|
|
165
|
+
'unused_let_binding', 'recursive_call', 'variable_shadow',
|
|
166
|
+
'unused_local_fun', 'multiple_setq', 'redundant_quotes',
|
|
167
|
+
'redundant_setq', 'redundant_nil_else', 'single_arg_and_or',
|
|
168
|
+
'redundant_let', 'self_compare', 'misplaced_else',
|
|
169
|
+
'quote_vs_function', 'double_not', 'setq_single_arg',
|
|
170
|
+
'assoc_without_cdr', 'identical_branches', 'while_constant',
|
|
171
|
+
'cond_duplicate',
|
|
172
|
+
]);
|
|
173
|
+
const needsAst = Array.from(astRules).some(r => checks[r] !== 'off');
|
|
174
|
+
if (needsAst) {
|
|
175
|
+
const ast = (0, parser_1.parseAst)(content);
|
|
176
|
+
const visitorIssues = (0, visitor_runner_1.runChecksWithVisitor)(ast, file, config, disableMap);
|
|
177
|
+
addIssues(visitorIssues);
|
|
178
|
+
}
|
|
241
179
|
return issues;
|
|
242
180
|
}
|
|
243
|
-
|
|
244
|
-
function runChecksWithVisitor(content, file, config) {
|
|
245
|
-
const issues = [];
|
|
181
|
+
function runChecksWithVisitorWrapper(content, file, config) {
|
|
246
182
|
const disableMap = (0, disable_1.parseDisableComments)(content);
|
|
247
183
|
const ast = (0, parser_1.parseAst)(content);
|
|
248
|
-
|
|
249
|
-
const checks = config.checks;
|
|
250
|
-
function addIssue(rule, line, severity, message) {
|
|
251
|
-
if (severity === 'off')
|
|
252
|
-
return;
|
|
253
|
-
if (!(0, disable_1.isDisabled)(rule, line, disableMap)) {
|
|
254
|
-
issues.push({ file, line, severity: severity, rule, message });
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
// Register all AST-based checks as visitor handlers
|
|
258
|
-
if (checks['redundant_quotes'] !== 'off') {
|
|
259
|
-
visitor.on('quote', node => {
|
|
260
|
-
if (node.children &&
|
|
261
|
-
node.children.length >= 2 &&
|
|
262
|
-
node.children[1].type === 'list' &&
|
|
263
|
-
node.children[1].children &&
|
|
264
|
-
node.children[1].children.length > 0 &&
|
|
265
|
-
node.children[1].children[0].type === 'symbol' &&
|
|
266
|
-
node.children[1].children[0].name === 'quote') {
|
|
267
|
-
addIssue('redundant_quotes', node.pos.line, checks['redundant_quotes'], (0, locale_1.t)('redundant_quotes'));
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
if (checks['double_not'] !== 'off') {
|
|
272
|
-
visitor.on('not', node => {
|
|
273
|
-
if (node.children &&
|
|
274
|
-
node.children.length >= 2 &&
|
|
275
|
-
node.children[1].type === 'list' &&
|
|
276
|
-
node.children[1].children &&
|
|
277
|
-
node.children[1].children.length > 0 &&
|
|
278
|
-
node.children[1].children[0].type === 'symbol' &&
|
|
279
|
-
node.children[1].children[0].name === 'not') {
|
|
280
|
-
addIssue('double_not', node.pos.line, checks['double_not'], (0, locale_1.t)('double_not'));
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
if (checks['misplaced_else'] !== 'off') {
|
|
285
|
-
visitor.on('if', node => {
|
|
286
|
-
if (node.children && node.children.length === 4) {
|
|
287
|
-
const cond = node.children[1];
|
|
288
|
-
if (cond.type === 'list' &&
|
|
289
|
-
cond.children &&
|
|
290
|
-
cond.children.length >= 2 &&
|
|
291
|
-
cond.children[0].type === 'symbol' &&
|
|
292
|
-
cond.children[0].name === 'not') {
|
|
293
|
-
const innerName = cond.children[1].name || '';
|
|
294
|
-
addIssue('misplaced_else', node.pos.line, checks['misplaced_else'], (0, locale_1.t)('misplaced_else', innerName));
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
if (checks['quote_vs_function'] !== 'off') {
|
|
300
|
-
const higherOrder = new Set([
|
|
301
|
-
'mapcar',
|
|
302
|
-
'apply',
|
|
303
|
-
'lambda',
|
|
304
|
-
'vl-sort',
|
|
305
|
-
'vl-sort-i',
|
|
306
|
-
'vl-remove-if',
|
|
307
|
-
'vl-remove-if-not',
|
|
308
|
-
'vl-member-if',
|
|
309
|
-
'vl-some',
|
|
310
|
-
'vl-every',
|
|
311
|
-
]);
|
|
312
|
-
visitor.on('quote', node => {
|
|
313
|
-
if (node.children &&
|
|
314
|
-
node.children.length >= 2 &&
|
|
315
|
-
node.children[1].type === 'list' &&
|
|
316
|
-
node.children[1].children &&
|
|
317
|
-
node.children[1].children.length > 0 &&
|
|
318
|
-
node.children[1].children[0].type === 'symbol' &&
|
|
319
|
-
node.children[1].children[0].name === 'lambda') {
|
|
320
|
-
const parent = node.parent;
|
|
321
|
-
if (parent && parent.children && parent.children.length > 0) {
|
|
322
|
-
const pName = (parent.children[0].type === 'symbol' && parent.children[0].name) || '';
|
|
323
|
-
if (higherOrder.has(pName)) {
|
|
324
|
-
addIssue('quote_vs_function', node.pos.line, checks['quote_vs_function'], (0, locale_1.t)('quote_vs_function', pName));
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
visitor.run(ast);
|
|
331
|
-
return issues;
|
|
184
|
+
return (0, visitor_runner_1.runChecksWithVisitor)(ast, file, config, disableMap);
|
|
332
185
|
}
|
|
333
186
|
function lintFiles(files, config, rootDir) {
|
|
334
187
|
(0, locale_1.setLocale)(config.locale || 'zh');
|
|
@@ -349,7 +202,6 @@ function lintFiles(files, config, rootDir) {
|
|
|
349
202
|
});
|
|
350
203
|
continue;
|
|
351
204
|
}
|
|
352
|
-
// Per-directory config override
|
|
353
205
|
const override = (0, config_1.findOverrides)(filepath);
|
|
354
206
|
const effectiveConfig = override ? (0, config_1.mergeOverrides)(config, override) : config;
|
|
355
207
|
allIssues.push(...runChecks(content, relPath, effectiveConfig));
|
|
@@ -417,7 +269,6 @@ function lintFilesParallel(files, config, rootDir) {
|
|
|
417
269
|
resolve([]);
|
|
418
270
|
});
|
|
419
271
|
}
|
|
420
|
-
/** Find the matching close paren for the first complete form starting at `start` */
|
|
421
272
|
function findMatchingParen(s, start) {
|
|
422
273
|
if (s[start] !== '(')
|
|
423
274
|
return start;
|
|
@@ -442,7 +293,6 @@ function findMatchingParen(s, start) {
|
|
|
442
293
|
}
|
|
443
294
|
return s.length - 1;
|
|
444
295
|
}
|
|
445
|
-
/** Extract the next top-level form(s) from a string starting at `start`. Returns [form, endIdx]. */
|
|
446
296
|
function extractForm(s, start) {
|
|
447
297
|
let i = start;
|
|
448
298
|
while (i < s.length && s[i] === ' ')
|
|
@@ -468,18 +318,12 @@ function applyFixes(issues, content, filepath) {
|
|
|
468
318
|
set.add(iss.line);
|
|
469
319
|
fixesByRule.set(iss.rule, set);
|
|
470
320
|
}
|
|
471
|
-
// Apply fixes from higher line numbers first to preserve offsets
|
|
472
321
|
const fixableRules = new Set([
|
|
473
|
-
'redundant_quotes',
|
|
474
|
-
'
|
|
475
|
-
'
|
|
476
|
-
'
|
|
477
|
-
'
|
|
478
|
-
'redundant_progn',
|
|
479
|
-
'redundant_let',
|
|
480
|
-
'quote_style',
|
|
481
|
-
'redundant_if',
|
|
482
|
-
'misplaced_else',
|
|
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',
|
|
483
327
|
]);
|
|
484
328
|
let result = content;
|
|
485
329
|
for (const [rule, lineSet] of fixesByRule) {
|
|
@@ -493,39 +337,30 @@ function applyFixes(issues, content, filepath) {
|
|
|
493
337
|
const lineContent = result.split('\n')[idx];
|
|
494
338
|
switch (rule) {
|
|
495
339
|
case 'redundant_quotes': {
|
|
496
|
-
// Replace ''x with 'x on this line
|
|
497
340
|
const fixed = lineContent.replace(/''(\S)/g, "'$1");
|
|
498
|
-
if (fixed !== lineContent)
|
|
341
|
+
if (fixed !== lineContent)
|
|
499
342
|
result = replaceLine(result, idx, fixed);
|
|
500
|
-
}
|
|
501
343
|
break;
|
|
502
344
|
}
|
|
503
345
|
case 'double_not': {
|
|
504
|
-
// Replace (not (not ...)) with (not ...)
|
|
505
346
|
const fixed = lineContent.replace(/\(not\s+\(not\s+/g, '(not ');
|
|
506
|
-
if (fixed !== lineContent)
|
|
347
|
+
if (fixed !== lineContent)
|
|
507
348
|
result = replaceLine(result, idx, fixed);
|
|
508
|
-
}
|
|
509
349
|
break;
|
|
510
350
|
}
|
|
511
351
|
case 'redundant_nil_else': {
|
|
512
|
-
// Replace (if ... nil) with (if ...)
|
|
513
352
|
const fixed = lineContent.replace(/\s+nil\s*\)$/, ')');
|
|
514
|
-
if (fixed !== lineContent)
|
|
353
|
+
if (fixed !== lineContent)
|
|
515
354
|
result = replaceLine(result, idx, fixed);
|
|
516
|
-
}
|
|
517
355
|
break;
|
|
518
356
|
}
|
|
519
357
|
case 'single_arg_and_or': {
|
|
520
|
-
// Replace (and x) with x, (or x) with x
|
|
521
358
|
const m = lineContent.match(/\((and|or)\s+([^)\s]+)\s*\)/);
|
|
522
|
-
if (m)
|
|
359
|
+
if (m)
|
|
523
360
|
result = replaceLine(result, idx, m[2]);
|
|
524
|
-
}
|
|
525
361
|
break;
|
|
526
362
|
}
|
|
527
363
|
case 'redundant_progn': {
|
|
528
|
-
// (progn <single-form>) → <single-form>
|
|
529
364
|
const proIdx = lineContent.indexOf('(progn ');
|
|
530
365
|
if (proIdx !== -1) {
|
|
531
366
|
const beforePro = lineContent.slice(0, proIdx);
|
|
@@ -541,14 +376,12 @@ function applyFixes(issues, content, filepath) {
|
|
|
541
376
|
break;
|
|
542
377
|
}
|
|
543
378
|
case 'redundant_let': {
|
|
544
|
-
// (let () body...) → (progn body...)
|
|
545
379
|
const fixed = lineContent.replace(/\(let\s+\(\)\s*/, '(progn ');
|
|
546
380
|
if (fixed !== lineContent)
|
|
547
381
|
result = replaceLine(result, idx, fixed);
|
|
548
382
|
break;
|
|
549
383
|
}
|
|
550
384
|
case 'quote_style': {
|
|
551
|
-
// (quote x) → 'x
|
|
552
385
|
const qIdx = lineContent.indexOf('(quote ');
|
|
553
386
|
if (qIdx !== -1) {
|
|
554
387
|
const beforeQ = lineContent.slice(0, qIdx);
|
|
@@ -564,7 +397,6 @@ function applyFixes(issues, content, filepath) {
|
|
|
564
397
|
break;
|
|
565
398
|
}
|
|
566
399
|
case 'redundant_if': {
|
|
567
|
-
// (if/when/unless (progn ...) ...) → remove inner progn
|
|
568
400
|
const pIdx = lineContent.indexOf('(progn ');
|
|
569
401
|
if (pIdx !== -1) {
|
|
570
402
|
const beforeP = lineContent.slice(0, pIdx);
|
|
@@ -580,7 +412,6 @@ function applyFixes(issues, content, filepath) {
|
|
|
580
412
|
break;
|
|
581
413
|
}
|
|
582
414
|
case 'misplaced_else': {
|
|
583
|
-
// (if (not x) then else) → (if x else then)
|
|
584
415
|
const ifStart = lineContent.indexOf('(if (not ');
|
|
585
416
|
if (ifStart !== -1) {
|
|
586
417
|
const notIdx = lineContent.indexOf('(not ', ifStart);
|
|
@@ -607,7 +438,6 @@ function applyFixes(issues, content, filepath) {
|
|
|
607
438
|
break;
|
|
608
439
|
}
|
|
609
440
|
case 'redundant_setq': {
|
|
610
|
-
// Remove (setq x x) lines
|
|
611
441
|
const allLines = result.split('\n');
|
|
612
442
|
const trimmed = allLines[idx].trim();
|
|
613
443
|
if (/^\(setq\s+(\S+)\s+\1\s*\)$/.test(trimmed)) {
|
|
@@ -616,6 +446,63 @@ function applyFixes(issues, content, filepath) {
|
|
|
616
446
|
}
|
|
617
447
|
break;
|
|
618
448
|
}
|
|
449
|
+
case 'setq_multiple': {
|
|
450
|
+
const fixed = lineContent.replace(/\)\(setq\s+/g, ' ');
|
|
451
|
+
if (fixed !== lineContent) {
|
|
452
|
+
const merged = lineContent.replace(/^\(\s*setq\s+/, '(setq ');
|
|
453
|
+
if (merged.includes('setq') && !merged.match(/\)\(/)) {
|
|
454
|
+
result = replaceLine(result, idx, merged);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
const simplified = lineContent.replace(/\)\(setq\s+/g, '\n');
|
|
458
|
+
if (simplified !== lineContent)
|
|
459
|
+
result = replaceLine(result, idx, simplified);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
case 'append_single': {
|
|
465
|
+
const aIdx = lineContent.indexOf('(append ');
|
|
466
|
+
if (aIdx !== -1) {
|
|
467
|
+
const close = findMatchingParen(lineContent, aIdx);
|
|
468
|
+
if (close > aIdx) {
|
|
469
|
+
const inner = lineContent.slice(aIdx + 8, close);
|
|
470
|
+
const listMatch = inner.match(/^\(list\s+(.*)\)\s*$/);
|
|
471
|
+
if (listMatch) {
|
|
472
|
+
const fixed = lineContent.slice(0, aIdx) + '(cons ' + listMatch[1] + ')' + lineContent.slice(close + 1);
|
|
473
|
+
if (fixed !== lineContent)
|
|
474
|
+
result = replaceLine(result, idx, fixed);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
case 'nth_usage': {
|
|
481
|
+
const fixed = lineContent.replace(/\(nth\s+0\s+/g, '(car ');
|
|
482
|
+
if (fixed !== lineContent)
|
|
483
|
+
result = replaceLine(result, idx, fixed);
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
case 'setq_single_arg': {
|
|
487
|
+
const allLines = result.split('\n');
|
|
488
|
+
if (/^\s*\(setq\s+\S+\s*\)\s*$/.test(allLines[idx])) {
|
|
489
|
+
allLines.splice(idx, 1);
|
|
490
|
+
result = allLines.join('\n');
|
|
491
|
+
}
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
case 'extra_parens': {
|
|
495
|
+
const fixed = lineContent.replace(/\){2,}/g, ')');
|
|
496
|
+
if (fixed !== lineContent)
|
|
497
|
+
result = replaceLine(result, idx, fixed);
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
case 'empty_branch': {
|
|
501
|
+
const fixed = lineContent.replace(/\(if\s+\S+\s*\)/g, m => m.slice(0, -1) + ' nil)');
|
|
502
|
+
if (fixed !== lineContent)
|
|
503
|
+
result = replaceLine(result, idx, fixed);
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
619
506
|
}
|
|
620
507
|
}
|
|
621
508
|
}
|
|
@@ -640,7 +527,6 @@ const UTF8_BOM = 0xfeff;
|
|
|
640
527
|
function fixFile(filepath) {
|
|
641
528
|
const fixes = [];
|
|
642
529
|
let content = fs.readFileSync(filepath, 'utf-8');
|
|
643
|
-
// Fix trailing whitespace
|
|
644
530
|
const wsFixed = content
|
|
645
531
|
.split('\n')
|
|
646
532
|
.map(l => l.replace(/[ \t]+$/, ''))
|
|
@@ -649,12 +535,10 @@ function fixFile(filepath) {
|
|
|
649
535
|
content = wsFixed;
|
|
650
536
|
fixes.push('trailing_whitespace');
|
|
651
537
|
}
|
|
652
|
-
// Fix UTF-8 BOM
|
|
653
538
|
if (content.charCodeAt(0) === UTF8_BOM) {
|
|
654
539
|
content = content.slice(1);
|
|
655
540
|
fixes.push('encoding (BOM)');
|
|
656
541
|
}
|
|
657
|
-
// Fix trailing parens: remove excess ')' from end of content
|
|
658
542
|
let openCount = 0;
|
|
659
543
|
let closeCount = 0;
|
|
660
544
|
let fixContent = content;
|
|
@@ -671,14 +555,12 @@ function fixFile(filepath) {
|
|
|
671
555
|
fixContent = fixContent.slice(0, -1);
|
|
672
556
|
excess--;
|
|
673
557
|
}
|
|
674
|
-
// Also trim trailing whitespace after removing parens
|
|
675
558
|
fixContent = fixContent.replace(/\n[ \t]*$/, '');
|
|
676
559
|
if (fixContent !== content) {
|
|
677
560
|
content = fixContent;
|
|
678
561
|
fixes.push('trailing_paren');
|
|
679
562
|
}
|
|
680
563
|
}
|
|
681
|
-
// Fix empty comments: remove lines that are only ';' with optional whitespace
|
|
682
564
|
const lines = content.split('\n');
|
|
683
565
|
let changed = false;
|
|
684
566
|
const fixedLines = lines.map(l => {
|
package/dist/validate.js
CHANGED
|
@@ -50,7 +50,6 @@ const VALID_RULES = [
|
|
|
50
50
|
'quote_style',
|
|
51
51
|
'quote_vs_function',
|
|
52
52
|
'recursive_call',
|
|
53
|
-
'redundant_cond',
|
|
54
53
|
'redundant_if',
|
|
55
54
|
'redundant_let',
|
|
56
55
|
'redundant_nil_else',
|
|
@@ -78,6 +77,14 @@ const VALID_RULES = [
|
|
|
78
77
|
'while_constant',
|
|
79
78
|
'vl_registry_write',
|
|
80
79
|
'vlax_without_loading',
|
|
80
|
+
'unused_catch_result',
|
|
81
|
+
'string_concat_loop',
|
|
82
|
+
'if_without_else',
|
|
83
|
+
'redundant_list',
|
|
84
|
+
'entget_in_loop',
|
|
85
|
+
'promise_handler',
|
|
86
|
+
'module_cycle',
|
|
87
|
+
'arg_count_project',
|
|
81
88
|
];
|
|
82
89
|
const VALID_SEVERITIES = ['off', 'warn', 'error'];
|
|
83
90
|
function validateConfig(config) {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Issue, LintConfig } from './types';
|
|
2
|
+
import { AstNode } from '@atlisp/parser';
|
|
3
|
+
export declare function runChecksWithVisitor(ast: AstNode, file: string, config: LintConfig, disableMap: Map<string, Set<number>>): Issue[];
|
|
4
|
+
//# sourceMappingURL=visitor-runner.d.ts.map
|