@atlisp/lint 0.2.16 → 0.2.18
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 +1 -1
- package/atlisp-lint.default.json +1 -1
- package/dist/checks/arg-count.d.ts +2 -1
- package/dist/checks/arg-count.js +37 -19
- package/dist/checks/builtin-arg-count.d.ts +2 -1
- package/dist/checks/builtin-arg-count.js +3 -2
- package/dist/checks/cond-duplicate.js +5 -6
- package/dist/checks/constant-condition.js +9 -9
- package/dist/checks/dangerous-calls.js +1 -1
- package/dist/checks/dangling-defun.d.ts +0 -7
- package/dist/checks/dangling-defun.js +0 -63
- package/dist/checks/double-not.js +1 -1
- package/dist/checks/empty-branch.js +1 -1
- package/dist/checks/empty-catch.d.ts +2 -1
- package/dist/checks/empty-catch.js +8 -7
- package/dist/checks/function-complexity.js +5 -18
- package/dist/checks/global-naming.js +1 -1
- package/dist/checks/identical-branches.js +3 -3
- package/dist/checks/if-without-else.js +1 -1
- package/dist/checks/misplaced-else.js +2 -2
- package/dist/checks/missing-export.js +3 -3
- package/dist/checks/missing-princ.js +1 -1
- package/dist/checks/multiple-setq.js +1 -1
- package/dist/checks/no-return.d.ts +2 -1
- package/dist/checks/no-return.js +4 -3
- package/dist/checks/nth-usage.js +1 -1
- package/dist/checks/parameter-naming.js +2 -2
- package/dist/checks/quote-vs-function.js +1 -1
- package/dist/checks/recursive-call.js +1 -1
- package/dist/checks/redundant-let.js +1 -1
- package/dist/checks/redundant-nil-else.js +1 -1
- package/dist/checks/redundant-progn.js +1 -1
- package/dist/checks/redundant-setq.js +1 -1
- package/dist/checks/self-compare.js +1 -1
- package/dist/checks/setq-single-arg.js +3 -2
- package/dist/checks/single-arg-and-or.js +1 -1
- package/dist/checks/undeclared-setq.d.ts +2 -1
- package/dist/checks/undeclared-setq.js +6 -5
- package/dist/checks/unused-let.js +3 -3
- package/dist/checks/unused-local-fun.js +2 -2
- package/dist/checks/unused-package-dep.d.ts +0 -1
- package/dist/checks/unused-package-dep.js +6 -93
- package/dist/checks/unused-param.js +2 -2
- package/dist/checks/unused-variable.js +2 -2
- package/dist/checks/variable-shadow.js +3 -3
- package/dist/checks/while-constant.js +1 -1
- package/dist/cli/collector.js +26 -19
- package/dist/config.d.ts +1 -1
- package/dist/config.js +18 -9
- package/dist/fixes/index.d.ts +4 -0
- package/dist/fixes/index.js +843 -140
- package/dist/formatter.d.ts +0 -1
- package/dist/formatter.js +141 -31
- package/dist/index.js +59 -51
- package/dist/project.d.ts +1 -1
- package/dist/project.js +98 -112
- package/dist/runner.d.ts +1 -1
- package/dist/runner.js +17 -8
- package/dist/utils.js +21 -30
- package/dist/validate.js +3 -3
- package/dist/visitor-runner.d.ts +0 -28
- package/dist/visitor-runner.js +3 -809
- package/dist/visitors/equal-nil.d.ts +2 -2
- package/dist/visitors/equal-nil.js +5 -4
- package/dist/visitors/index.d.ts +1 -1
- package/dist/visitors/progn-in-cond.d.ts +2 -2
- package/dist/visitors/progn-in-cond.js +5 -4
- package/dist/visitors/redundant-and-or.d.ts +2 -2
- package/dist/visitors/redundant-and-or.js +3 -3
- package/dist/visitors/redundant-car-cdr.d.ts +2 -2
- package/dist/visitors/redundant-car-cdr.js +3 -3
- package/dist/visitors/register.d.ts +6 -0
- package/dist/visitors/register.js +625 -0
- package/dist/visitors/shared.d.ts +40 -0
- package/dist/visitors/shared.js +129 -0
- package/dist/visitors/types.d.ts +2 -7
- package/dist/visitors/types.js +0 -69
- package/dist/watch.js +33 -12
- package/dist/worker.js +1 -1
- package/package.json +2 -2
package/dist/project.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.lintProject = lintProject;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const parser_1 = require("@atlisp/parser");
|
|
40
|
+
const fsp = __importStar(require("fs/promises"));
|
|
40
41
|
const dangling_defun_1 = require("./checks/dangling-defun");
|
|
41
42
|
const missing_export_1 = require("./checks/missing-export");
|
|
42
43
|
const unused_package_dep_1 = require("./checks/unused-package-dep");
|
|
@@ -45,72 +46,52 @@ const builtin_arg_count_1 = require("./checks/builtin-arg-count");
|
|
|
45
46
|
const config_1 = require("./config");
|
|
46
47
|
const locale_1 = require("./locale");
|
|
47
48
|
const locale_2 = require("./locale");
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
const shared_1 = require("./visitors/shared");
|
|
50
|
+
function collectProjectMetadata(ast) {
|
|
51
|
+
const defuns = [];
|
|
52
|
+
const refs = [];
|
|
53
|
+
const allLists = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n));
|
|
54
|
+
for (const node of allLists) {
|
|
55
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length === 0)
|
|
56
|
+
continue;
|
|
57
|
+
const car = node.children[0];
|
|
58
|
+
if (!(0, parser_1.astIsSymbol)(car))
|
|
59
|
+
continue;
|
|
60
|
+
const name = car.name;
|
|
61
|
+
if ((name === 'defun' || name === 'defun-q') && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
|
|
62
|
+
defuns.push({ name: node.children[1].name, line: node.pos.line });
|
|
54
63
|
}
|
|
55
|
-
}
|
|
56
|
-
return results;
|
|
57
|
-
}
|
|
58
|
-
function collectReferencesFromAst(ast) {
|
|
59
|
-
const results = [];
|
|
60
|
-
const allCalls = (0, parser_1.astFindAll)(ast, n => {
|
|
61
|
-
if (n.type !== 'list' || !n.children || n.children.length === 0)
|
|
62
|
-
return false;
|
|
63
|
-
return (0, parser_1.astIsSymbol)(n.children[0]);
|
|
64
|
-
});
|
|
65
|
-
for (const call of allCalls) {
|
|
66
|
-
const name = call.children[0].name;
|
|
67
64
|
if (name !== 'defun' && name !== 'defun-q' && !name.startsWith('c:') && !name.includes(':')) {
|
|
68
|
-
|
|
65
|
+
refs.push({ name, line: node.pos.line });
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
|
-
return
|
|
68
|
+
return { defuns, refs };
|
|
72
69
|
}
|
|
73
70
|
function countFunctionArgsFromAst(ast) {
|
|
74
71
|
const result = new Map();
|
|
75
72
|
const defunNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'defun') || (0, parser_1.astIsList)(n, 'defun-q'));
|
|
76
73
|
for (const node of defunNodes) {
|
|
77
|
-
if (!node
|
|
74
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 3)
|
|
78
75
|
continue;
|
|
79
76
|
const name = (0, parser_1.astIsSymbol)(node.children[1]) ? node.children[1].name : '';
|
|
80
77
|
if (!name)
|
|
81
78
|
continue;
|
|
82
|
-
|
|
83
|
-
if (paramList.type !== 'list' || !paramList.children) {
|
|
84
|
-
result.set(name, 0);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
let count = 0;
|
|
88
|
-
for (const child of paramList.children) {
|
|
89
|
-
if (child.type === 'symbol' && child.name && child.name !== '/') {
|
|
90
|
-
count++;
|
|
91
|
-
}
|
|
92
|
-
else if (child.type === 'symbol' && child.name === '/') {
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
result.set(name, count);
|
|
79
|
+
result.set(name, (0, shared_1.defunParams)(node).length);
|
|
97
80
|
}
|
|
98
81
|
return result;
|
|
99
82
|
}
|
|
100
83
|
function countCallArgsFromAst(ast) {
|
|
101
84
|
const result = new Map();
|
|
102
|
-
const allCalls = (0, parser_1.astFindAll)(ast, n =>
|
|
103
|
-
if (n.type !== 'list' || !n.children || n.children.length === 0)
|
|
104
|
-
return false;
|
|
105
|
-
return (0, parser_1.astIsSymbol)(n.children[0]);
|
|
106
|
-
});
|
|
85
|
+
const allCalls = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n) && n.children.length > 0 && (0, parser_1.astIsSymbol)(n.children[0]));
|
|
107
86
|
for (const call of allCalls) {
|
|
87
|
+
if (!(0, parser_1.astIsList)(call) || !(0, parser_1.astIsSymbol)(call.children[0]))
|
|
88
|
+
continue;
|
|
108
89
|
const name = call.children[0].name;
|
|
109
90
|
if (name === 'defun' || name === 'defun-q' || name.startsWith('c:') || name.includes(':'))
|
|
110
91
|
continue;
|
|
111
92
|
if (!result.has(name))
|
|
112
93
|
result.set(name, []);
|
|
113
|
-
const argCount = call.children
|
|
94
|
+
const argCount = call.children.length - 1;
|
|
114
95
|
result.get(name).push({ line: call.pos.line, count: argCount });
|
|
115
96
|
}
|
|
116
97
|
return result;
|
|
@@ -120,26 +101,25 @@ function findModuleDepsFromAst(ast) {
|
|
|
120
101
|
const exports = [];
|
|
121
102
|
const inPackageNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'in-package'));
|
|
122
103
|
for (const node of inPackageNodes) {
|
|
123
|
-
if (node
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
104
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length < 3)
|
|
105
|
+
continue;
|
|
106
|
+
const useList = node.children[2];
|
|
107
|
+
if ((0, parser_1.astIsList)(useList) && useList.children) {
|
|
108
|
+
for (const use of useList.children) {
|
|
109
|
+
if ((0, parser_1.astIsSymbol)(use) && use.name)
|
|
110
|
+
imports.push(use.name);
|
|
130
111
|
}
|
|
131
112
|
}
|
|
132
113
|
}
|
|
133
114
|
const setqNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'setq'));
|
|
134
115
|
for (const node of setqNodes) {
|
|
135
|
-
if (node
|
|
116
|
+
if ((0, parser_1.astIsList)(node) &&
|
|
136
117
|
node.children.length >= 3 &&
|
|
137
|
-
node.children[1]
|
|
118
|
+
(0, parser_1.astIsSymbol)(node.children[1]) &&
|
|
138
119
|
node.children[1].name === '@::*modules*' &&
|
|
139
|
-
node.children[2]
|
|
140
|
-
node.children[2].children) {
|
|
120
|
+
(0, parser_1.astIsList)(node.children[2])) {
|
|
141
121
|
for (const exp of node.children[2].children) {
|
|
142
|
-
if (
|
|
122
|
+
if (typeof exp.value === 'string') {
|
|
143
123
|
exports.push(exp.value);
|
|
144
124
|
}
|
|
145
125
|
}
|
|
@@ -249,38 +229,52 @@ function checkModuleCycle(moduleDeps, rootDir, allIssues) {
|
|
|
249
229
|
}
|
|
250
230
|
}
|
|
251
231
|
}
|
|
252
|
-
function lintProject(files, config, rootDir) {
|
|
232
|
+
async function lintProject(files, config, rootDir) {
|
|
253
233
|
(0, locale_1.setLocale)(config.locale || 'zh');
|
|
254
234
|
const allIssues = [];
|
|
255
|
-
const symbols = {
|
|
235
|
+
const symbols = {
|
|
236
|
+
defuns: new Map(),
|
|
237
|
+
references: new Map(),
|
|
238
|
+
fileDefuns: new Map(),
|
|
239
|
+
fileRefs: new Map(),
|
|
240
|
+
};
|
|
256
241
|
const maxFiles = config.project_analysis?.maxFiles ?? 500;
|
|
257
242
|
if (files.length > maxFiles) {
|
|
258
243
|
const msg = `${files.length} files exceeds project_analysis.maxFiles (${maxFiles}). Skipping cross-file analysis. To increase, set project_analysis.maxFiles in config.`;
|
|
259
244
|
console.warn(`[atlisp-lint] ${msg}`);
|
|
260
245
|
return [];
|
|
261
246
|
}
|
|
262
|
-
const fileContents = new Map();
|
|
263
247
|
const defunArgs = new Map();
|
|
264
248
|
const callArgCounts = new Map();
|
|
265
249
|
const moduleDeps = new Map();
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
250
|
+
const checks = config.checks;
|
|
251
|
+
const needsContent = checks['unused_package_dep'] !== 'off';
|
|
252
|
+
// Phase A: Read all files in parallel, then process metadata sequentially
|
|
253
|
+
const processedFiles = [];
|
|
254
|
+
const contentStore = needsContent ? new Map() : undefined;
|
|
255
|
+
const readResults = await Promise.all(files.map(async (filepath) => {
|
|
269
256
|
try {
|
|
270
|
-
content =
|
|
257
|
+
const content = await fsp.readFile(filepath, 'utf-8');
|
|
258
|
+
return { filepath, content, ok: true };
|
|
271
259
|
}
|
|
272
260
|
catch {
|
|
273
|
-
|
|
261
|
+
return { filepath, ok: false };
|
|
274
262
|
}
|
|
263
|
+
}));
|
|
264
|
+
for (const result of readResults) {
|
|
265
|
+
if (!result.ok)
|
|
266
|
+
continue;
|
|
267
|
+
const { filepath, content } = result;
|
|
275
268
|
const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
276
269
|
// Extract all metadata from a single AST parse
|
|
277
|
-
const defunList =
|
|
270
|
+
const { defuns: defunList, refs: refList } = collectProjectMetadata(ast);
|
|
271
|
+
symbols.fileDefuns.set(filepath, defunList);
|
|
278
272
|
for (const d of defunList) {
|
|
279
273
|
const list = symbols.defuns.get(d.name) || [];
|
|
280
274
|
list.push({ file: filepath, line: d.line });
|
|
281
275
|
symbols.defuns.set(d.name, list);
|
|
282
276
|
}
|
|
283
|
-
|
|
277
|
+
symbols.fileRefs.set(filepath, refList);
|
|
284
278
|
for (const r of refList) {
|
|
285
279
|
const list = symbols.references.get(r.name) || [];
|
|
286
280
|
list.push({ file: filepath, line: r.line });
|
|
@@ -289,31 +283,24 @@ function lintProject(files, config, rootDir) {
|
|
|
289
283
|
defunArgs.set(filepath, countFunctionArgsFromAst(ast));
|
|
290
284
|
callArgCounts.set(filepath, countCallArgsFromAst(ast));
|
|
291
285
|
moduleDeps.set(filepath, findModuleDepsFromAst(ast));
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
286
|
+
processedFiles.push(filepath);
|
|
287
|
+
if (contentStore)
|
|
288
|
+
contentStore.set(filepath, content);
|
|
295
289
|
}
|
|
296
290
|
// Phase B: Cross-file checks using only symbol tables
|
|
297
|
-
for (const
|
|
291
|
+
for (const filepath of processedFiles) {
|
|
298
292
|
const relPath = path.relative(rootDir, filepath);
|
|
299
293
|
const override = (0, config_1.findOverrides)(filepath);
|
|
300
|
-
const
|
|
301
|
-
if (
|
|
302
|
-
const defunList = [];
|
|
303
|
-
for (const [name, locations] of symbols.defuns) {
|
|
304
|
-
for (const loc of locations) {
|
|
305
|
-
if (loc.file === filepath) {
|
|
306
|
-
defunList.push({ name, line: loc.line });
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
294
|
+
const fileChecks = override?.checks || checks;
|
|
295
|
+
if (fileChecks['dangling_defun'] !== 'off') {
|
|
296
|
+
const defunList = symbols.fileDefuns.get(filepath) || [];
|
|
310
297
|
const danglingIssues = (0, dangling_defun_1.checkDanglingDefunFromDefs)(defunList, symbols.references, filepath);
|
|
311
298
|
for (const iss of danglingIssues) {
|
|
312
299
|
iss.file = relPath;
|
|
313
300
|
allIssues.push(iss);
|
|
314
301
|
}
|
|
315
302
|
}
|
|
316
|
-
if (
|
|
303
|
+
if (fileChecks['missing_export'] !== 'off') {
|
|
317
304
|
const pkgDir = findPackageDir(filepath);
|
|
318
305
|
if (pkgDir) {
|
|
319
306
|
const missingIssues = (0, missing_export_1.checkMissingExport)(filepath, pkgDir, symbols.defuns);
|
|
@@ -323,8 +310,8 @@ function lintProject(files, config, rootDir) {
|
|
|
323
310
|
}
|
|
324
311
|
}
|
|
325
312
|
}
|
|
326
|
-
if (
|
|
327
|
-
const content =
|
|
313
|
+
if (fileChecks['unused_package_dep'] !== 'off') {
|
|
314
|
+
const content = contentStore?.get(filepath);
|
|
328
315
|
if (content) {
|
|
329
316
|
const depIssues = (0, unused_package_dep_1.checkUnusedPackageDepFromContent)(filepath, content);
|
|
330
317
|
for (const iss of depIssues) {
|
|
@@ -333,65 +320,64 @@ function lintProject(files, config, rootDir) {
|
|
|
333
320
|
}
|
|
334
321
|
}
|
|
335
322
|
}
|
|
336
|
-
if (
|
|
323
|
+
if (fileChecks['duplicate_defun'] !== 'off') {
|
|
337
324
|
const dupIssues = (0, duplicate_defun_1.checkDuplicateDefun)(filepath, symbols.defuns);
|
|
338
325
|
for (const iss of dupIssues) {
|
|
339
326
|
iss.file = relPath;
|
|
340
327
|
allIssues.push(iss);
|
|
341
328
|
}
|
|
342
329
|
}
|
|
343
|
-
if (
|
|
330
|
+
if (fileChecks['arg_count_project'] !== 'off') {
|
|
344
331
|
const argIssues = checkArgCountProject(filepath, defunArgs, callArgCounts, symbols, relPath);
|
|
345
332
|
allIssues.push(...argIssues);
|
|
346
333
|
}
|
|
347
|
-
if (
|
|
348
|
-
const definedHere = new Set();
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
definedHere.add(name);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
for (const [name, refs] of symbols.references) {
|
|
356
|
-
if (builtin_arg_count_1.BUILTIN_FUNCTIONS.has(name))
|
|
334
|
+
if (fileChecks['function_reference'] !== 'off') {
|
|
335
|
+
const definedHere = new Set((symbols.fileDefuns.get(filepath) || []).map(d => d.name));
|
|
336
|
+
const fileRefs = symbols.fileRefs.get(filepath) || [];
|
|
337
|
+
for (const ref of fileRefs) {
|
|
338
|
+
if (builtin_arg_count_1.BUILTIN_FUNCTIONS.has(ref.name))
|
|
357
339
|
continue;
|
|
358
|
-
if (definedHere.has(name))
|
|
340
|
+
if (definedHere.has(ref.name))
|
|
359
341
|
continue;
|
|
360
|
-
if (symbols.defuns.has(name))
|
|
342
|
+
if (symbols.defuns.has(ref.name))
|
|
361
343
|
continue;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
message: (0, locale_2.t)('function_reference', name),
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
}
|
|
344
|
+
allIssues.push({
|
|
345
|
+
file: relPath,
|
|
346
|
+
line: ref.line,
|
|
347
|
+
severity: 'warn',
|
|
348
|
+
rule: 'function_reference',
|
|
349
|
+
message: (0, locale_2.t)('function_reference', ref.name),
|
|
350
|
+
});
|
|
373
351
|
}
|
|
374
352
|
}
|
|
375
353
|
}
|
|
376
354
|
// Module cycle check: run DFS once (not per file)
|
|
377
|
-
|
|
378
|
-
|
|
355
|
+
if (processedFiles.length > 0) {
|
|
356
|
+
const cycleCheckFile = processedFiles[0];
|
|
379
357
|
const override = (0, config_1.findOverrides)(cycleCheckFile);
|
|
380
|
-
const
|
|
381
|
-
if (
|
|
358
|
+
const fileChecks = override?.checks || checks;
|
|
359
|
+
if (fileChecks['module_cycle'] !== 'off') {
|
|
382
360
|
checkModuleCycle(moduleDeps, rootDir, allIssues);
|
|
383
361
|
}
|
|
384
362
|
}
|
|
385
363
|
return allIssues;
|
|
386
364
|
}
|
|
365
|
+
const packageDirCache = new Map();
|
|
387
366
|
function findPackageDir(filepath) {
|
|
367
|
+
const cached = packageDirCache.get(filepath);
|
|
368
|
+
if (cached !== undefined)
|
|
369
|
+
return cached;
|
|
388
370
|
let dir = path.dirname(filepath);
|
|
389
371
|
for (;;) {
|
|
390
|
-
if (fs.existsSync(path.join(dir, 'pkg.lsp')))
|
|
372
|
+
if (fs.existsSync(path.join(dir, 'pkg.lsp'))) {
|
|
373
|
+
packageDirCache.set(filepath, dir);
|
|
391
374
|
return dir;
|
|
375
|
+
}
|
|
392
376
|
const parent = path.dirname(dir);
|
|
393
|
-
if (parent === dir)
|
|
377
|
+
if (parent === dir) {
|
|
378
|
+
packageDirCache.set(filepath, null);
|
|
394
379
|
return null;
|
|
380
|
+
}
|
|
395
381
|
dir = parent;
|
|
396
382
|
}
|
|
397
383
|
}
|
package/dist/runner.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Issue, LintConfig } from './types';
|
|
2
2
|
export declare function runChecks(content: string, file: string, config: LintConfig): Issue[];
|
|
3
3
|
export declare function lintFiles(files: string[], config: LintConfig, rootDir: string, contentCache?: Map<string, string>): Issue[];
|
|
4
|
-
export declare function lintFilesParallel(files: string[], config: LintConfig, rootDir: string): Promise<Issue[]>;
|
|
4
|
+
export declare function lintFilesParallel(files: string[], config: LintConfig, rootDir: string, contentCache?: Map<string, string>): Promise<Issue[]>;
|
|
5
5
|
export { applyFixesFromProviders as applyFixes, FIXABLE_RULES } from './fixes/index';
|
|
6
6
|
export declare function parseIgnoreFile(rootDir: string): string[];
|
|
7
7
|
export declare function fixFile(filepath: string): string[];
|
package/dist/runner.js
CHANGED
|
@@ -138,15 +138,21 @@ function runChecks(content, file, config) {
|
|
|
138
138
|
addIfEnabled('error_handling', () => (0, error_handling_1.checkErrorHandling)(content, file));
|
|
139
139
|
addIfEnabled('global_naming', () => (0, global_naming_1.checkGlobalNaming)(content, file));
|
|
140
140
|
addIfEnabled('extra_parens', () => (0, extra_parens_1.checkExtraParens)(content, file));
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
// Early AST parse for checks that need AST but aren't visitor-based
|
|
142
|
+
let ast;
|
|
143
|
+
const needsEarlyAst = ['arg_count', 'builtin_arg_count', 'empty_catch', 'no_return', 'undeclared_setq'].some(r => checks[r] !== 'off');
|
|
144
|
+
if (needsEarlyAst) {
|
|
145
|
+
ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
146
|
+
}
|
|
147
|
+
addIfEnabled('arg_count', () => (0, arg_count_1.checkArgCount)(content, file, ast));
|
|
148
|
+
addIfEnabled('builtin_arg_count', () => (0, builtin_arg_count_1.checkBuiltinArgCount)(content, file, ast));
|
|
143
149
|
addIfEnabled('strcat_usage', () => (0, strcat_usage_1.checkStrcatUsage)(content, file));
|
|
144
150
|
addIfEnabled('cond_simplify', () => (0, cond_simplify_1.checkCondSimplify)(content, file));
|
|
145
151
|
addIfEnabled('quote_style', () => (0, quote_style_1.checkQuoteStyle)(content, file));
|
|
146
152
|
addIfEnabled('eq_usage', () => (0, eq_usage_1.checkEqUsage)(content, file));
|
|
147
153
|
addIfEnabled('lambda_syntax', () => (0, lambda_syntax_1.checkLambdaSyntax)(content, file));
|
|
148
154
|
addIfEnabled('comment_style', () => (0, comment_style_1.checkCommentStyle)(content, file));
|
|
149
|
-
addIfEnabled('empty_catch', () => (0, empty_catch_1.checkEmptyCatch)(content, file));
|
|
155
|
+
addIfEnabled('empty_catch', () => (0, empty_catch_1.checkEmptyCatch)(content, file, ast));
|
|
150
156
|
addIfEnabled('nth_usage', () => (0, nth_usage_1.checkNthUsage)(content, file));
|
|
151
157
|
addIfEnabled('append_single', () => (0, append_single_1.checkAppendSingle)(content, file));
|
|
152
158
|
addIfEnabled('setq_multiple', () => (0, setq_multiple_1.checkSetqMultiple)(content, file));
|
|
@@ -154,12 +160,12 @@ function runChecks(content, file, config) {
|
|
|
154
160
|
addIfEnabled('magic_number', () => (0, magic_number_1.checkMagicNumber)(content, file));
|
|
155
161
|
addIfEnabled('mixed_indent', () => (0, mixed_indent_1.checkMixedIndent)(content, file));
|
|
156
162
|
addIfEnabled('long_function_call', () => (0, long_function_call_1.checkLongFunctionCall)(content, file, 6));
|
|
157
|
-
addIfEnabled('no_return', () => (0, no_return_1.checkNoReturn)(content, file));
|
|
163
|
+
addIfEnabled('no_return', () => (0, no_return_1.checkNoReturn)(content, file, ast));
|
|
158
164
|
addIfEnabled('shadow_builtin', () => (0, shadow_builtin_1.checkShadowBuiltin)(content, file));
|
|
159
165
|
addIfEnabled('dynamic_doc', () => (0, dynamic_doc_1.checkDynamicDoc)(content, file));
|
|
160
166
|
addIfEnabled('loop_optimization', () => (0, loop_optimization_1.checkLoopOptimization)(content, file));
|
|
161
167
|
addIfEnabled('redundant_if', () => (0, redundant_if_1.checkRedundantIf)(content, file));
|
|
162
|
-
addIfEnabled('undeclared_setq', () => (0, undeclared_setq_1.checkUndeclaredSetq)(content, file));
|
|
168
|
+
addIfEnabled('undeclared_setq', () => (0, undeclared_setq_1.checkUndeclaredSetq)(content, file, ast));
|
|
163
169
|
addIfEnabled('format_indent', () => (0, format_indent_1.checkFormatIndent)(content, file, config.line_length.tab_width));
|
|
164
170
|
// New checks
|
|
165
171
|
addIfEnabled('unused_catch_result', () => (0, unused_catch_result_1.checkUnusedCatchResult)(content, file));
|
|
@@ -212,7 +218,9 @@ function runChecks(content, file, config) {
|
|
|
212
218
|
const needsAst = Array.from(astRules).some(r => checks[r] !== 'off');
|
|
213
219
|
if (needsAst) {
|
|
214
220
|
try {
|
|
215
|
-
|
|
221
|
+
if (!ast) {
|
|
222
|
+
ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
223
|
+
}
|
|
216
224
|
const visitorIssues = (0, visitor_runner_1.runChecksWithVisitor)(ast, file, config, disableMap);
|
|
217
225
|
addIssues(visitorIssues);
|
|
218
226
|
}
|
|
@@ -261,7 +269,7 @@ function lintFiles(files, config, rootDir, contentCache) {
|
|
|
261
269
|
}
|
|
262
270
|
return allIssues;
|
|
263
271
|
}
|
|
264
|
-
function lintFilesParallel(files, config, rootDir) {
|
|
272
|
+
function lintFilesParallel(files, config, rootDir, contentCache) {
|
|
265
273
|
(0, locale_1.setLocale)(config.locale || 'zh');
|
|
266
274
|
return new Promise(resolve => {
|
|
267
275
|
const allIssues = [];
|
|
@@ -274,8 +282,9 @@ function lintFilesParallel(files, config, rootDir) {
|
|
|
274
282
|
const idx = nextIndex++;
|
|
275
283
|
const filepath = files[idx];
|
|
276
284
|
const relPath = path.relative(rootDir, filepath);
|
|
285
|
+
const content = contentCache?.get(filepath);
|
|
277
286
|
const worker = new worker_threads_1.Worker(path.join(__dirname, 'worker.js'), {
|
|
278
|
-
workerData: { filepath, relPath, config },
|
|
287
|
+
workerData: { filepath, relPath, config, content },
|
|
279
288
|
});
|
|
280
289
|
worker.on('message', (msg) => {
|
|
281
290
|
if (completed.has(idx))
|
package/dist/utils.js
CHANGED
|
@@ -6,8 +6,7 @@ exports.codeLines = codeLines;
|
|
|
6
6
|
exports.findMatchingParen = findMatchingParen;
|
|
7
7
|
exports.extractForm = extractForm;
|
|
8
8
|
exports.extractStrings = extractStrings;
|
|
9
|
-
|
|
10
|
-
function stripLine(line) {
|
|
9
|
+
function stripLineInternal(line, keepQuotes) {
|
|
11
10
|
const out = [];
|
|
12
11
|
let inStr = false;
|
|
13
12
|
let i = 0;
|
|
@@ -17,46 +16,38 @@ function stripLine(line) {
|
|
|
17
16
|
break;
|
|
18
17
|
if (ch === '"') {
|
|
19
18
|
inStr = !inStr;
|
|
19
|
+
if (keepQuotes)
|
|
20
|
+
out.push(ch);
|
|
20
21
|
i++;
|
|
21
22
|
continue;
|
|
22
23
|
}
|
|
23
24
|
if (ch === '\\' && inStr) {
|
|
24
|
-
|
|
25
|
+
if (keepQuotes) {
|
|
26
|
+
out.push(ch);
|
|
27
|
+
if (i + 1 < line.length) {
|
|
28
|
+
out.push(line[i + 1]);
|
|
29
|
+
i++;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
if (i + 1 < line.length)
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
i++;
|
|
25
37
|
continue;
|
|
26
38
|
}
|
|
27
|
-
if (!inStr)
|
|
39
|
+
if (keepQuotes || !inStr)
|
|
28
40
|
out.push(ch);
|
|
29
41
|
i++;
|
|
30
42
|
}
|
|
31
43
|
return out.join('');
|
|
32
44
|
}
|
|
45
|
+
/** Strip string contents and comments from a line of AutoLISP code */
|
|
46
|
+
function stripLine(line) {
|
|
47
|
+
return stripLineInternal(line, false);
|
|
48
|
+
}
|
|
33
49
|
function stripLineRaw(line) {
|
|
34
|
-
|
|
35
|
-
let inStr = false;
|
|
36
|
-
let i = 0;
|
|
37
|
-
while (i < line.length) {
|
|
38
|
-
const ch = line[i];
|
|
39
|
-
if (ch === ';' && !inStr)
|
|
40
|
-
break;
|
|
41
|
-
if (ch === '"') {
|
|
42
|
-
inStr = !inStr;
|
|
43
|
-
out.push(ch);
|
|
44
|
-
i++;
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (ch === '\\' && inStr) {
|
|
48
|
-
out.push(ch);
|
|
49
|
-
if (i + 1 < line.length) {
|
|
50
|
-
out.push(line[i + 1]);
|
|
51
|
-
i++;
|
|
52
|
-
}
|
|
53
|
-
i++;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
out.push(ch);
|
|
57
|
-
i++;
|
|
58
|
-
}
|
|
59
|
-
return out.join('');
|
|
50
|
+
return stripLineInternal(line, true);
|
|
60
51
|
}
|
|
61
52
|
/** Iterate lines with string-aware content */
|
|
62
53
|
function* codeLines(content) {
|
package/dist/validate.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.validateConfig = validateConfig;
|
|
4
|
-
const VALID_RULES = [
|
|
4
|
+
const VALID_RULES = new Set([
|
|
5
5
|
'append_single',
|
|
6
6
|
'arg_count',
|
|
7
7
|
'assoc_without_cdr',
|
|
@@ -101,7 +101,7 @@ const VALID_RULES = [
|
|
|
101
101
|
'redundant_car_cdr',
|
|
102
102
|
'equal_nil',
|
|
103
103
|
'redundant_and_or',
|
|
104
|
-
];
|
|
104
|
+
]);
|
|
105
105
|
const VALID_SEVERITIES = ['off', 'info', 'warn', 'error'];
|
|
106
106
|
function validateConfig(config) {
|
|
107
107
|
const errors = [];
|
|
@@ -110,7 +110,7 @@ function validateConfig(config) {
|
|
|
110
110
|
}
|
|
111
111
|
if (config.checks) {
|
|
112
112
|
for (const [rule, sev] of Object.entries(config.checks)) {
|
|
113
|
-
if (!VALID_RULES.
|
|
113
|
+
if (!VALID_RULES.has(rule)) {
|
|
114
114
|
errors.push({ path: `checks.${rule}`, message: `Unknown rule '${rule}'` });
|
|
115
115
|
}
|
|
116
116
|
if (!VALID_SEVERITIES.includes(sev)) {
|
package/dist/visitor-runner.d.ts
CHANGED
|
@@ -1,32 +1,4 @@
|
|
|
1
1
|
import { Issue, LintConfig } from './types';
|
|
2
2
|
import { AstNode } from '@atlisp/parser';
|
|
3
|
-
export interface CollectState {
|
|
4
|
-
openCount: number;
|
|
5
|
-
closeCount: number;
|
|
6
|
-
setqDefs: Map<string, {
|
|
7
|
-
line: number;
|
|
8
|
-
file: string;
|
|
9
|
-
}>;
|
|
10
|
-
symbolRefs: Map<string, number>;
|
|
11
|
-
condKeys: Map<number, Set<string>>;
|
|
12
|
-
defunScopes: Array<{
|
|
13
|
-
name: string;
|
|
14
|
-
params: string[];
|
|
15
|
-
body: AstNode[];
|
|
16
|
-
line: number;
|
|
17
|
-
file: string;
|
|
18
|
-
}>;
|
|
19
|
-
letScopes: Array<{
|
|
20
|
-
bindings: string[];
|
|
21
|
-
body: AstNode[];
|
|
22
|
-
line: number;
|
|
23
|
-
}>;
|
|
24
|
-
dangerousCalls: Array<{
|
|
25
|
-
name: string;
|
|
26
|
-
node: AstNode;
|
|
27
|
-
}>;
|
|
28
|
-
ssgetCount: number;
|
|
29
|
-
sssetCount: number;
|
|
30
|
-
}
|
|
31
3
|
export declare function runChecksWithVisitor(ast: AstNode, file: string, config: LintConfig, disableMap: Map<string, Set<number>>): Issue[];
|
|
32
4
|
//# sourceMappingURL=visitor-runner.d.ts.map
|