@atlisp/lint 0.2.21 → 0.2.22
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.
|
@@ -4,6 +4,7 @@ exports.checkMultipleSetq = checkMultipleSetq;
|
|
|
4
4
|
exports.checkMultipleSetqAst = checkMultipleSetqAst;
|
|
5
5
|
const locale_1 = require("../locale");
|
|
6
6
|
const parser_1 = require("@atlisp/parser");
|
|
7
|
+
const shared_1 = require("../visitors/shared");
|
|
7
8
|
function checkMultipleSetq(content, file) {
|
|
8
9
|
const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
9
10
|
return checkMultipleSetqAst(ast, file);
|
|
@@ -18,7 +19,7 @@ function checkMultipleSetqAst(ast, file) {
|
|
|
18
19
|
parents.add(n.parent);
|
|
19
20
|
}
|
|
20
21
|
for (const parent of parents) {
|
|
21
|
-
if (!(0, parser_1.astIsList)(parent))
|
|
22
|
+
if (!(0, parser_1.astIsList)(parent) || (0, shared_1.isNonSequentialParent)(parent))
|
|
22
23
|
continue;
|
|
23
24
|
let prevWasSetq = false;
|
|
24
25
|
let prevSetqNode = null;
|
|
@@ -2,14 +2,40 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkSetqMultiple = checkSetqMultiple;
|
|
4
4
|
const locale_1 = require("../locale");
|
|
5
|
-
const
|
|
5
|
+
const parser_1 = require("@atlisp/parser");
|
|
6
|
+
const shared_1 = require("../visitors/shared");
|
|
6
7
|
function checkSetqMultiple(content, file) {
|
|
7
8
|
const issues = [];
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
10
|
+
const parents = new Set();
|
|
11
|
+
const setqNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'setq'));
|
|
12
|
+
for (const n of setqNodes) {
|
|
13
|
+
if (n.parent)
|
|
14
|
+
parents.add(n.parent);
|
|
15
|
+
}
|
|
16
|
+
for (const parent of parents) {
|
|
17
|
+
if (!(0, parser_1.astIsList)(parent) || (0, shared_1.isNonSequentialParent)(parent))
|
|
18
|
+
continue;
|
|
19
|
+
let prevSetqNode = null;
|
|
20
|
+
for (const child of parent.children) {
|
|
21
|
+
if ((0, parser_1.astIsList)(child, 'setq')) {
|
|
22
|
+
if (prevSetqNode) {
|
|
23
|
+
issues.push({
|
|
24
|
+
file,
|
|
25
|
+
line: prevSetqNode.pos.line,
|
|
26
|
+
severity: 'warn',
|
|
27
|
+
rule: 'setq_multiple',
|
|
28
|
+
message: (0, locale_1.t)('setq_multiple'),
|
|
29
|
+
});
|
|
30
|
+
prevSetqNode = null;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
prevSetqNode = child;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
prevSetqNode = null;
|
|
38
|
+
}
|
|
13
39
|
}
|
|
14
40
|
}
|
|
15
41
|
return issues;
|
package/dist/fixes/index.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.FIXABLE_RULES = void 0;
|
|
|
4
4
|
exports.applyFixesFromProviders = applyFixesFromProviders;
|
|
5
5
|
const nth_usage_1 = require("../checks/nth-usage");
|
|
6
6
|
const parser_1 = require("@atlisp/parser");
|
|
7
|
+
const shared_1 = require("../visitors/shared");
|
|
7
8
|
function buildPosToOffset(_content) {
|
|
8
9
|
return pos => pos.offset;
|
|
9
10
|
}
|
|
@@ -162,7 +163,24 @@ function misplaced_else_fallback(issues, content) {
|
|
|
162
163
|
}
|
|
163
164
|
return content;
|
|
164
165
|
}
|
|
166
|
+
function isSetqInNonSequentialParent(ast, line) {
|
|
167
|
+
if (!ast)
|
|
168
|
+
return false;
|
|
169
|
+
const setqNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'setq') && n.pos.line === line);
|
|
170
|
+
for (const n of setqNodes) {
|
|
171
|
+
if (n.parent && (0, shared_1.isNonSequentialParent)(n.parent))
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
165
176
|
function setq_multiple_fallback(issues, content) {
|
|
177
|
+
let ast;
|
|
178
|
+
try {
|
|
179
|
+
ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
/* ignore parse errors */
|
|
183
|
+
}
|
|
166
184
|
const lines = content.split('\n');
|
|
167
185
|
const lineOffsets = buildLineOffsets(lines);
|
|
168
186
|
const ops = [];
|
|
@@ -170,6 +188,9 @@ function setq_multiple_fallback(issues, content) {
|
|
|
170
188
|
const lineIdx = issue.line - 1;
|
|
171
189
|
if (lineIdx < 0 || lineIdx >= lines.length)
|
|
172
190
|
continue;
|
|
191
|
+
// Skip setq forms in non-sequential positions (if branches, and/or, ...)
|
|
192
|
+
if (isSetqInNonSequentialParent(ast, issue.line))
|
|
193
|
+
continue;
|
|
173
194
|
const line = lines[lineIdx];
|
|
174
195
|
const setqMatch = line.match(/\(setq(?=\s|$)/);
|
|
175
196
|
if (!setqMatch)
|
|
@@ -594,6 +615,8 @@ const CONTENT_FIX_PROVIDERS = {
|
|
|
594
615
|
const parent = a.parent;
|
|
595
616
|
if (!parent || !(0, parser_1.astIsList)(parent))
|
|
596
617
|
continue;
|
|
618
|
+
if ((0, shared_1.isNonSequentialParent)(parent))
|
|
619
|
+
continue;
|
|
597
620
|
const aIdx = parent.children.indexOf(a);
|
|
598
621
|
if (aIdx === -1 || parent.children[aIdx + 1] !== b)
|
|
599
622
|
continue;
|
|
@@ -273,7 +273,11 @@ function registerVisitorRules(visitor, state, checks, addIssue, config, file) {
|
|
|
273
273
|
dangerNames.add('eval');
|
|
274
274
|
if (dangerNames.size > 0) {
|
|
275
275
|
visitor.on('*', node => {
|
|
276
|
-
if ((0, parser_1.astIsList)(node) &&
|
|
276
|
+
if ((0, parser_1.astIsList)(node) &&
|
|
277
|
+
node.children.length > 0 &&
|
|
278
|
+
(0, parser_1.astIsSymbol)(node.children[0]) &&
|
|
279
|
+
node.children[0].name &&
|
|
280
|
+
dangerNames.has(node.children[0].name)) {
|
|
277
281
|
if (!(0, shared_1.isInQuote)(node)) {
|
|
278
282
|
state.dangerousCalls.push({ name: node.children[0].name, node });
|
|
279
283
|
}
|
|
@@ -284,6 +288,10 @@ function registerVisitorRules(visitor, state, checks, addIssue, config, file) {
|
|
|
284
288
|
if (checks['multiple_setq'] !== 'off') {
|
|
285
289
|
visitor.on('*', node => {
|
|
286
290
|
if ((0, parser_1.astIsList)(node)) {
|
|
291
|
+
// Skip condition/branch positions (if/and/or/while/...) where setq
|
|
292
|
+
// children are mutually exclusive or short-circuited — merging would break them.
|
|
293
|
+
if ((0, shared_1.isNonSequentialParent)(node))
|
|
294
|
+
return;
|
|
287
295
|
let prevSetq = false;
|
|
288
296
|
for (const child of node.children) {
|
|
289
297
|
if ((0, parser_1.astIsList)(child, 'setq')) {
|
|
@@ -605,12 +613,18 @@ function registerVisitorRules(visitor, state, checks, addIssue, config, file) {
|
|
|
605
613
|
if ((0, shared_1.isInQuote)(node))
|
|
606
614
|
return;
|
|
607
615
|
const parent = node.parent;
|
|
608
|
-
if (parent &&
|
|
616
|
+
if (parent &&
|
|
617
|
+
(0, parser_1.astIsList)(parent) &&
|
|
618
|
+
parent.children.length > 0 &&
|
|
619
|
+
(0, parser_1.astIsSymbol)(parent.children[0], 'vl-catch-all-apply')) {
|
|
609
620
|
return;
|
|
610
621
|
}
|
|
611
622
|
let hasCallback = false;
|
|
612
623
|
for (const child of node.children.slice(1)) {
|
|
613
|
-
if ((0, parser_1.astIsList)(child) &&
|
|
624
|
+
if ((0, parser_1.astIsList)(child) &&
|
|
625
|
+
child.children.length > 0 &&
|
|
626
|
+
(0, parser_1.astIsSymbol)(child.children[0]) &&
|
|
627
|
+
(child.children[0].name === 'lambda' || child.children[0].name === 'function')) {
|
|
614
628
|
hasCallback = true;
|
|
615
629
|
break;
|
|
616
630
|
}
|
|
@@ -36,5 +36,6 @@ export declare function hasSelfCall(body: AstNode[], fnName: string): boolean;
|
|
|
36
36
|
export declare function defunParams(defunNode: AstNode): string[];
|
|
37
37
|
export declare function defunLocals(defunNode: AstNode): string[];
|
|
38
38
|
export declare function collectSymbolRefs(node: AstNode, result: Set<string>): void;
|
|
39
|
+
export declare function isNonSequentialParent(node: AstNode): boolean;
|
|
39
40
|
export declare const higherOrderFuncs: Set<string>;
|
|
40
41
|
//# sourceMappingURL=shared.d.ts.map
|
package/dist/visitors/shared.js
CHANGED
|
@@ -10,6 +10,7 @@ exports.hasSelfCall = hasSelfCall;
|
|
|
10
10
|
exports.defunParams = defunParams;
|
|
11
11
|
exports.defunLocals = defunLocals;
|
|
12
12
|
exports.collectSymbolRefs = collectSymbolRefs;
|
|
13
|
+
exports.isNonSequentialParent = isNonSequentialParent;
|
|
13
14
|
const parser_1 = require("@atlisp/parser");
|
|
14
15
|
function isInQuote(node, checkFunction = true) {
|
|
15
16
|
for (const p of (0, parser_1.astAncestors)(node)) {
|
|
@@ -114,6 +115,19 @@ function collectSymbolRefs(node, result) {
|
|
|
114
115
|
collectSymbolRefs(c, result);
|
|
115
116
|
}
|
|
116
117
|
}
|
|
118
|
+
// Forms whose child lists are not guaranteed to execute in sequence
|
|
119
|
+
// (conditions / branches / short-circuit value positions). Merging consecutive
|
|
120
|
+
// (setq ...) children of these heads would change program semantics.
|
|
121
|
+
const NON_SEQUENTIAL_HEADS = new Set(['if', 'and', 'or', 'while', 'repeat', 'foreach']);
|
|
122
|
+
// Returns true when the list's children occupy non-sequential positions
|
|
123
|
+
// (e.g. the then/else branches of an if), so consecutive setq children
|
|
124
|
+
// must NOT be reported as mergeable.
|
|
125
|
+
function isNonSequentialParent(node) {
|
|
126
|
+
if (!(0, parser_1.astIsList)(node) || node.children.length === 0)
|
|
127
|
+
return true;
|
|
128
|
+
const head = node.children[0];
|
|
129
|
+
return (0, parser_1.astIsSymbol)(head) && NON_SEQUENTIAL_HEADS.has(head.name);
|
|
130
|
+
}
|
|
117
131
|
exports.higherOrderFuncs = new Set([
|
|
118
132
|
'mapcar',
|
|
119
133
|
'apply',
|