@atlisp/lint 0.2.22 → 0.2.23

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.
@@ -2,39 +2,98 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checkStringConcatInLoop = checkStringConcatInLoop;
4
4
  const locale_1 = require("../locale");
5
- function checkStringConcatInLoop(content, file) {
6
- const issues = [];
7
- const lines = content.split('\n');
5
+ function maskLine(line) {
6
+ let out = '';
7
+ let inString = false;
8
+ for (let k = 0; k < line.length; k++) {
9
+ const ch = line[k];
10
+ if (inString) {
11
+ if (ch === '\\') {
12
+ out += ' ';
13
+ k++;
14
+ }
15
+ else {
16
+ if (ch === '"')
17
+ inString = false;
18
+ out += ' ';
19
+ }
20
+ continue;
21
+ }
22
+ if (ch === '"') {
23
+ inString = true;
24
+ out += ' ';
25
+ continue;
26
+ }
27
+ if (ch === ';')
28
+ break;
29
+ out += ch;
30
+ }
31
+ return out;
32
+ }
33
+ function findLoops(lines) {
34
+ const loops = [];
8
35
  const re = /\((while|repeat|foreach)\s/g;
9
36
  for (let i = 0; i < lines.length; i++) {
37
+ const masked = maskLine(lines[i]);
10
38
  let match;
11
- while ((match = re.exec(lines[i])) !== null) {
12
- const name = match[1];
13
- let depth = 0;
14
- let started = false;
15
- for (let j = i; j < lines.length; j++) {
16
- for (let k = 0; k < lines[j].length; k++) {
17
- const ch = lines[j][k];
18
- if (ch === '(')
19
- depth++;
20
- else if (ch === ')')
21
- depth--;
39
+ while ((match = re.exec(masked)) !== null) {
40
+ loops.push({ name: match[1], line: i, col: match.index });
41
+ }
42
+ }
43
+ return loops;
44
+ }
45
+ // Find the line index where the form opening at (startLine, startCol) closes.
46
+ // Returns -1 if the form is never closed (unbalanced input).
47
+ function findFormEnd(lines, startLine, startCol) {
48
+ let depth = 0;
49
+ let inString = false;
50
+ for (let j = startLine; j < lines.length; j++) {
51
+ const line = lines[j];
52
+ const begin = j === startLine ? startCol : 0;
53
+ for (let k = begin; k < line.length; k++) {
54
+ const ch = line[k];
55
+ if (inString) {
56
+ if (ch === '\\') {
57
+ k++;
58
+ }
59
+ else if (ch === '"') {
60
+ inString = false;
22
61
  }
23
- if (!started && depth > 0)
24
- started = true;
25
- if (depth === 0 && started)
26
- break;
62
+ continue;
27
63
  }
28
- const loopBody = lines.slice(i, i + 20).join('\n');
29
- if (loopBody.includes('strcat') || loopBody.includes('strcat ')) {
30
- issues.push({
31
- file,
32
- line: i + 1,
33
- severity: 'info',
34
- rule: 'string_concat_loop',
35
- message: (0, locale_1.t)('string_concat_loop', name),
36
- });
64
+ if (ch === '"') {
65
+ inString = true;
66
+ continue;
37
67
  }
68
+ if (ch === ';')
69
+ break;
70
+ if (ch === '(')
71
+ depth++;
72
+ else if (ch === ')') {
73
+ depth--;
74
+ if (depth === 0)
75
+ return j;
76
+ }
77
+ }
78
+ }
79
+ return -1;
80
+ }
81
+ function checkStringConcatInLoop(content, file) {
82
+ const issues = [];
83
+ const lines = content.split('\n');
84
+ for (const loop of findLoops(lines)) {
85
+ const end = findFormEnd(lines, loop.line, loop.col);
86
+ if (end < 0)
87
+ continue;
88
+ const loopBody = lines.slice(loop.line, end + 1).join('\n');
89
+ if (loopBody.includes('(strcat')) {
90
+ issues.push({
91
+ file,
92
+ line: loop.line + 1,
93
+ severity: 'info',
94
+ rule: 'string_concat_loop',
95
+ message: (0, locale_1.t)('string_concat_loop', loop.name),
96
+ });
38
97
  }
39
98
  }
40
99
  return issues;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlisp/lint",
3
- "version": "0.2.22",
3
+ "version": "0.2.23",
4
4
  "description": "AutoLISP static analysis tool — parens, security, conventions, SBCL syntax validation",
5
5
  "keywords": [
6
6
  "CAD",