@rcrsr/rill 0.1.0 → 0.2.0

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 (260) hide show
  1. package/README.md +16 -8
  2. package/dist/check/config.d.ts +20 -0
  3. package/dist/check/config.d.ts.map +1 -0
  4. package/dist/check/config.js +151 -0
  5. package/dist/check/config.js.map +1 -0
  6. package/dist/check/fixer.d.ts +39 -0
  7. package/dist/check/fixer.d.ts.map +1 -0
  8. package/dist/check/fixer.js +119 -0
  9. package/dist/check/fixer.js.map +1 -0
  10. package/dist/check/index.d.ts +10 -0
  11. package/dist/check/index.d.ts.map +1 -0
  12. package/dist/check/index.js +21 -0
  13. package/dist/check/index.js.map +1 -0
  14. package/dist/check/rules/anti-patterns.d.ts +65 -0
  15. package/dist/check/rules/anti-patterns.d.ts.map +1 -0
  16. package/dist/check/rules/anti-patterns.js +427 -0
  17. package/dist/check/rules/anti-patterns.js.map +1 -0
  18. package/dist/check/rules/closures.d.ts +66 -0
  19. package/dist/check/rules/closures.d.ts.map +1 -0
  20. package/dist/check/rules/closures.js +373 -0
  21. package/dist/check/rules/closures.js.map +1 -0
  22. package/dist/check/rules/collections.d.ts +90 -0
  23. package/dist/check/rules/collections.d.ts.map +1 -0
  24. package/dist/check/rules/collections.js +373 -0
  25. package/dist/check/rules/collections.js.map +1 -0
  26. package/dist/check/rules/conditionals.d.ts +41 -0
  27. package/dist/check/rules/conditionals.d.ts.map +1 -0
  28. package/dist/check/rules/conditionals.js +106 -0
  29. package/dist/check/rules/conditionals.js.map +1 -0
  30. package/dist/check/rules/flow.d.ts +46 -0
  31. package/dist/check/rules/flow.d.ts.map +1 -0
  32. package/dist/check/rules/flow.js +206 -0
  33. package/dist/check/rules/flow.js.map +1 -0
  34. package/dist/check/rules/formatting.d.ts +133 -0
  35. package/dist/check/rules/formatting.d.ts.map +1 -0
  36. package/dist/check/rules/formatting.js +639 -0
  37. package/dist/check/rules/formatting.js.map +1 -0
  38. package/dist/check/rules/helpers.d.ts +26 -0
  39. package/dist/check/rules/helpers.d.ts.map +1 -0
  40. package/dist/check/rules/helpers.js +66 -0
  41. package/dist/check/rules/helpers.js.map +1 -0
  42. package/dist/check/rules/index.d.ts +21 -0
  43. package/dist/check/rules/index.d.ts.map +1 -0
  44. package/dist/check/rules/index.js +78 -0
  45. package/dist/check/rules/index.js.map +1 -0
  46. package/dist/check/rules/loops.d.ts +70 -0
  47. package/dist/check/rules/loops.d.ts.map +1 -0
  48. package/dist/check/rules/loops.js +227 -0
  49. package/dist/check/rules/loops.js.map +1 -0
  50. package/dist/check/rules/naming.d.ts +21 -0
  51. package/dist/check/rules/naming.d.ts.map +1 -0
  52. package/dist/check/rules/naming.js +167 -0
  53. package/dist/check/rules/naming.js.map +1 -0
  54. package/dist/check/rules/strings.d.ts +28 -0
  55. package/dist/check/rules/strings.d.ts.map +1 -0
  56. package/dist/check/rules/strings.js +80 -0
  57. package/dist/check/rules/strings.js.map +1 -0
  58. package/dist/check/rules/types.d.ts +41 -0
  59. package/dist/check/rules/types.d.ts.map +1 -0
  60. package/dist/check/rules/types.js +162 -0
  61. package/dist/check/rules/types.js.map +1 -0
  62. package/dist/check/types.d.ts +106 -0
  63. package/dist/check/types.d.ts.map +1 -0
  64. package/dist/check/types.js +6 -0
  65. package/dist/check/types.js.map +1 -0
  66. package/dist/check/validator.d.ts +18 -0
  67. package/dist/check/validator.d.ts.map +1 -0
  68. package/dist/check/validator.js +88 -0
  69. package/dist/check/validator.js.map +1 -0
  70. package/dist/check/visitor.d.ts +33 -0
  71. package/dist/check/visitor.d.ts.map +1 -0
  72. package/dist/check/visitor.js +243 -0
  73. package/dist/check/visitor.js.map +1 -0
  74. package/dist/cli-check.d.ts +43 -0
  75. package/dist/cli-check.d.ts.map +1 -0
  76. package/dist/cli-check.js +356 -0
  77. package/dist/cli-check.js.map +1 -0
  78. package/dist/cli-eval.d.ts +15 -0
  79. package/dist/cli-eval.d.ts.map +1 -0
  80. package/dist/cli-eval.js +120 -0
  81. package/dist/cli-eval.js.map +1 -0
  82. package/dist/cli-exec.d.ts +49 -0
  83. package/dist/cli-exec.d.ts.map +1 -0
  84. package/dist/cli-exec.js +191 -0
  85. package/dist/cli-exec.js.map +1 -0
  86. package/dist/cli-module-loader.d.ts +19 -0
  87. package/dist/cli-module-loader.d.ts.map +1 -0
  88. package/dist/cli-module-loader.js +83 -0
  89. package/dist/cli-module-loader.js.map +1 -0
  90. package/dist/cli-shared.d.ts +36 -0
  91. package/dist/cli-shared.d.ts.map +1 -0
  92. package/dist/cli-shared.js +101 -0
  93. package/dist/cli-shared.js.map +1 -0
  94. package/dist/cli.d.ts +2 -0
  95. package/dist/cli.d.ts.map +1 -1
  96. package/dist/cli.js +4 -11
  97. package/dist/cli.js.map +1 -1
  98. package/dist/index.d.ts +1 -1
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +1 -1
  101. package/dist/index.js.map +1 -1
  102. package/dist/lexer/readers.d.ts +1 -1
  103. package/dist/lexer/readers.d.ts.map +1 -1
  104. package/dist/lexer/readers.js +62 -32
  105. package/dist/lexer/readers.js.map +1 -1
  106. package/dist/lexer/tokenizer.d.ts.map +1 -1
  107. package/dist/lexer/tokenizer.js +5 -6
  108. package/dist/lexer/tokenizer.js.map +1 -1
  109. package/dist/parser/index.js +1 -1
  110. package/dist/parser/index.js.map +1 -1
  111. package/dist/parser/parser-expr.js +23 -5
  112. package/dist/parser/parser-expr.js.map +1 -1
  113. package/dist/parser/parser-functions.d.ts +2 -2
  114. package/dist/parser/parser-functions.d.ts.map +1 -1
  115. package/dist/parser/parser-functions.js +2 -1
  116. package/dist/parser/parser-functions.js.map +1 -1
  117. package/dist/parser/parser-literals.js +2 -2
  118. package/dist/parser/parser-literals.js.map +1 -1
  119. package/dist/parser/parser-script.js +9 -7
  120. package/dist/parser/parser-script.js.map +1 -1
  121. package/dist/parser/parser-variables.js +4 -3
  122. package/dist/parser/parser-variables.js.map +1 -1
  123. package/dist/runtime/core/callable.d.ts +5 -6
  124. package/dist/runtime/core/callable.d.ts.map +1 -1
  125. package/dist/runtime/core/callable.js.map +1 -1
  126. package/dist/runtime/core/context.d.ts.map +1 -1
  127. package/dist/runtime/core/context.js +19 -32
  128. package/dist/runtime/core/context.js.map +1 -1
  129. package/dist/runtime/core/equals.js +1 -1
  130. package/dist/runtime/core/equals.js.map +1 -1
  131. package/dist/runtime/core/eval/evaluator.d.ts +78 -0
  132. package/dist/runtime/core/eval/evaluator.d.ts.map +1 -1
  133. package/dist/runtime/core/eval/evaluator.js +78 -0
  134. package/dist/runtime/core/eval/evaluator.js.map +1 -1
  135. package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
  136. package/dist/runtime/core/eval/mixins/closures.js +9 -1
  137. package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
  138. package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
  139. package/dist/runtime/core/eval/mixins/variables.js +143 -2
  140. package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
  141. package/dist/runtime/core/types.d.ts +15 -2
  142. package/dist/runtime/core/types.d.ts.map +1 -1
  143. package/dist/runtime/core/types.js.map +1 -1
  144. package/dist/runtime/ext/extensions.d.ts +51 -0
  145. package/dist/runtime/ext/extensions.d.ts.map +1 -0
  146. package/dist/runtime/ext/extensions.js +67 -0
  147. package/dist/runtime/ext/extensions.js.map +1 -0
  148. package/dist/runtime/index.d.ts +3 -0
  149. package/dist/runtime/index.d.ts.map +1 -1
  150. package/dist/runtime/index.js +1 -0
  151. package/dist/runtime/index.js.map +1 -1
  152. package/dist/types.d.ts +8 -4
  153. package/dist/types.d.ts.map +1 -1
  154. package/dist/types.js +5 -4
  155. package/dist/types.js.map +1 -1
  156. package/docs/00_INDEX.md +1 -0
  157. package/docs/01_guide.md +3 -3
  158. package/docs/02_types.md +8 -10
  159. package/docs/03_variables.md +10 -0
  160. package/docs/04_operators.md +3 -3
  161. package/docs/05_control-flow.md +21 -0
  162. package/docs/07_collections.md +2 -0
  163. package/docs/10_parsing.md +9 -9
  164. package/docs/11_reference.md +1 -1
  165. package/docs/12_examples.md +36 -62
  166. package/docs/14_host-integration.md +116 -111
  167. package/docs/15_grammar.ebnf +1 -5
  168. package/docs/16_conventions.md +3 -4
  169. package/docs/17_cli-tools.md +184 -0
  170. package/docs/99_llm-reference.txt +46 -5
  171. package/package.json +13 -4
  172. package/dist/demo.d.ts +0 -6
  173. package/dist/demo.d.ts.map +0 -1
  174. package/dist/demo.js +0 -121
  175. package/dist/demo.js.map +0 -1
  176. package/dist/lexer.d.ts +0 -19
  177. package/dist/lexer.d.ts.map +0 -1
  178. package/dist/lexer.js +0 -344
  179. package/dist/lexer.js.map +0 -1
  180. package/dist/parser/arithmetic.d.ts +0 -16
  181. package/dist/parser/arithmetic.d.ts.map +0 -1
  182. package/dist/parser/arithmetic.js +0 -128
  183. package/dist/parser/arithmetic.js.map +0 -1
  184. package/dist/parser/boolean.d.ts +0 -15
  185. package/dist/parser/boolean.d.ts.map +0 -1
  186. package/dist/parser/boolean.js +0 -20
  187. package/dist/parser/boolean.js.map +0 -1
  188. package/dist/parser/control-flow.d.ts +0 -56
  189. package/dist/parser/control-flow.d.ts.map +0 -1
  190. package/dist/parser/control-flow.js +0 -167
  191. package/dist/parser/control-flow.js.map +0 -1
  192. package/dist/parser/expressions.d.ts +0 -23
  193. package/dist/parser/expressions.d.ts.map +0 -1
  194. package/dist/parser/expressions.js +0 -950
  195. package/dist/parser/expressions.js.map +0 -1
  196. package/dist/parser/extraction.d.ts +0 -48
  197. package/dist/parser/extraction.d.ts.map +0 -1
  198. package/dist/parser/extraction.js +0 -279
  199. package/dist/parser/extraction.js.map +0 -1
  200. package/dist/parser/functions.d.ts +0 -20
  201. package/dist/parser/functions.d.ts.map +0 -1
  202. package/dist/parser/functions.js +0 -96
  203. package/dist/parser/functions.js.map +0 -1
  204. package/dist/parser/literals.d.ts +0 -37
  205. package/dist/parser/literals.d.ts.map +0 -1
  206. package/dist/parser/literals.js +0 -373
  207. package/dist/parser/literals.js.map +0 -1
  208. package/dist/parser/script.d.ts +0 -14
  209. package/dist/parser/script.d.ts.map +0 -1
  210. package/dist/parser/script.js +0 -196
  211. package/dist/parser/script.js.map +0 -1
  212. package/dist/parser/variables.d.ts +0 -10
  213. package/dist/parser/variables.d.ts.map +0 -1
  214. package/dist/parser/variables.js +0 -215
  215. package/dist/parser/variables.js.map +0 -1
  216. package/dist/runtime/ast-equals.d.ts +0 -13
  217. package/dist/runtime/ast-equals.d.ts.map +0 -1
  218. package/dist/runtime/ast-equals.js +0 -447
  219. package/dist/runtime/ast-equals.js.map +0 -1
  220. package/dist/runtime/builtins.d.ts +0 -13
  221. package/dist/runtime/builtins.d.ts.map +0 -1
  222. package/dist/runtime/builtins.js +0 -180
  223. package/dist/runtime/builtins.js.map +0 -1
  224. package/dist/runtime/callable.d.ts +0 -88
  225. package/dist/runtime/callable.d.ts.map +0 -1
  226. package/dist/runtime/callable.js +0 -98
  227. package/dist/runtime/callable.js.map +0 -1
  228. package/dist/runtime/context.d.ts +0 -13
  229. package/dist/runtime/context.d.ts.map +0 -1
  230. package/dist/runtime/context.js +0 -73
  231. package/dist/runtime/context.js.map +0 -1
  232. package/dist/runtime/core/evaluate.d.ts +0 -42
  233. package/dist/runtime/core/evaluate.d.ts.map +0 -1
  234. package/dist/runtime/core/evaluate.debug.js +0 -1251
  235. package/dist/runtime/core/evaluate.js +0 -1913
  236. package/dist/runtime/core/evaluate.js.map +0 -1
  237. package/dist/runtime/evaluate.d.ts +0 -32
  238. package/dist/runtime/evaluate.d.ts.map +0 -1
  239. package/dist/runtime/evaluate.js +0 -1111
  240. package/dist/runtime/evaluate.js.map +0 -1
  241. package/dist/runtime/execute.d.ts +0 -26
  242. package/dist/runtime/execute.d.ts.map +0 -1
  243. package/dist/runtime/execute.js +0 -121
  244. package/dist/runtime/execute.js.map +0 -1
  245. package/dist/runtime/signals.d.ts +0 -19
  246. package/dist/runtime/signals.d.ts.map +0 -1
  247. package/dist/runtime/signals.js +0 -26
  248. package/dist/runtime/signals.js.map +0 -1
  249. package/dist/runtime/types.d.ts +0 -169
  250. package/dist/runtime/types.d.ts.map +0 -1
  251. package/dist/runtime/types.js +0 -50
  252. package/dist/runtime/types.js.map +0 -1
  253. package/dist/runtime/values.d.ts +0 -50
  254. package/dist/runtime/values.d.ts.map +0 -1
  255. package/dist/runtime/values.js +0 -209
  256. package/dist/runtime/values.js.map +0 -1
  257. package/dist/runtime.d.ts +0 -254
  258. package/dist/runtime.d.ts.map +0 -1
  259. package/dist/runtime.js +0 -2014
  260. package/dist/runtime.js.map +0 -1
@@ -0,0 +1,427 @@
1
+ /**
2
+ * Anti-Pattern Rules
3
+ * Enforces best practices from docs/16_conventions.md:411-462.
4
+ */
5
+ import { extractContextLine } from './helpers.js';
6
+ // ============================================================
7
+ // AVOID_REASSIGNMENT RULE
8
+ // ============================================================
9
+ /**
10
+ * Warns on variable reassignment patterns.
11
+ * Variables lock to their first type, and reassignment suggests confusing
12
+ * flow control. Prefer functional style or new variables.
13
+ *
14
+ * Detection:
15
+ * - Capture node (:> $var) where $var already exists in validation context
16
+ * - Tracks variables seen during validation pass
17
+ *
18
+ * Valid alternatives:
19
+ * - Use new variable: $result1, $result2
20
+ * - Functional chains: value -> op1 -> op2
21
+ *
22
+ * References:
23
+ * - docs/16_conventions.md:413-424
24
+ */
25
+ export const AVOID_REASSIGNMENT = {
26
+ code: 'AVOID_REASSIGNMENT',
27
+ category: 'anti-patterns',
28
+ severity: 'warning',
29
+ nodeTypes: ['Capture'],
30
+ validate(node, context) {
31
+ const captureNode = node;
32
+ const varName = captureNode.name;
33
+ // Check if this variable was already captured before
34
+ if (context.variables.has(varName)) {
35
+ const firstLocation = context.variables.get(varName);
36
+ return [
37
+ {
38
+ location: captureNode.span.start,
39
+ severity: 'warning',
40
+ code: 'AVOID_REASSIGNMENT',
41
+ message: `Variable reassignment detected: '$${varName}' first defined at line ${firstLocation.line}. Prefer new variable or functional style.`,
42
+ context: extractContextLine(captureNode.span.start.line, context.source),
43
+ fix: null, // Cannot auto-fix without understanding intent
44
+ },
45
+ ];
46
+ }
47
+ return [];
48
+ },
49
+ };
50
+ // ============================================================
51
+ // COMPLEX_CONDITION RULE
52
+ // ============================================================
53
+ /**
54
+ * Warns on complex nested boolean conditions.
55
+ * Complex conditions with multiple nested operators are hard to read.
56
+ * Extract to named variables for clarity.
57
+ *
58
+ * Detection:
59
+ * - Conditional nodes with conditions containing 3+ boolean operators (&&, ||)
60
+ * - Nesting depth > 2 for boolean expressions
61
+ *
62
+ * Valid alternatives:
63
+ * - Extract sub-conditions to named variables
64
+ * - Split complex checks into multiple smaller checks
65
+ *
66
+ * References:
67
+ * - docs/16_conventions.md:451-461
68
+ */
69
+ export const COMPLEX_CONDITION = {
70
+ code: 'COMPLEX_CONDITION',
71
+ category: 'anti-patterns',
72
+ severity: 'info',
73
+ nodeTypes: ['Conditional'],
74
+ validate(node, context) {
75
+ const conditionalNode = node;
76
+ const condition = conditionalNode.condition;
77
+ if (!condition) {
78
+ return [];
79
+ }
80
+ // Unwrap GroupedExpr to get to the actual condition
81
+ let unwrappedCondition = condition;
82
+ if (unwrappedCondition.type === 'GroupedExpr') {
83
+ unwrappedCondition = unwrappedCondition.expression;
84
+ }
85
+ // Count boolean operators, boolean nesting depth, and parenthetical nesting
86
+ const operatorCount = countBooleanOperators(unwrappedCondition);
87
+ const booleanDepth = getBooleanNestingDepth(unwrappedCondition);
88
+ const parenDepth = getParenNestingDepth(unwrappedCondition);
89
+ // Flag if 3+ operators, boolean nesting > 2, or excessive parentheses (> 2)
90
+ if (operatorCount >= 3 || booleanDepth > 2 || parenDepth > 2) {
91
+ return [
92
+ {
93
+ location: conditionalNode.span.start,
94
+ severity: 'info',
95
+ code: 'COMPLEX_CONDITION',
96
+ message: 'Complex condition with multiple operators. Extract to named checks for clarity.',
97
+ context: extractContextLine(conditionalNode.span.start.line, context.source),
98
+ fix: null, // Auto-fix would require semantic understanding
99
+ },
100
+ ];
101
+ }
102
+ return [];
103
+ },
104
+ };
105
+ /**
106
+ * Count boolean operators (&&, ||) in an expression tree.
107
+ */
108
+ function countBooleanOperators(node) {
109
+ let count = 0;
110
+ if (node.type === 'BinaryExpr') {
111
+ const binaryNode = node;
112
+ if (binaryNode.op === '&&' || binaryNode.op === '||') {
113
+ count = 1;
114
+ }
115
+ count += countBooleanOperators(binaryNode.left);
116
+ count += countBooleanOperators(binaryNode.right);
117
+ }
118
+ // Traverse other node types that might contain expressions
119
+ switch (node.type) {
120
+ case 'UnaryExpr': {
121
+ const unaryNode = node;
122
+ count += countBooleanOperators(unaryNode.operand);
123
+ break;
124
+ }
125
+ case 'GroupedExpr': {
126
+ const groupedNode = node;
127
+ count += countBooleanOperators(groupedNode.expression);
128
+ break;
129
+ }
130
+ case 'PipeChain': {
131
+ const pipeNode = node;
132
+ if (pipeNode.head)
133
+ count += countBooleanOperators(pipeNode.head);
134
+ if (pipeNode.pipes) {
135
+ for (const pipe of pipeNode.pipes) {
136
+ count += countBooleanOperators(pipe);
137
+ }
138
+ }
139
+ break;
140
+ }
141
+ case 'PostfixExpr': {
142
+ const postfixNode = node;
143
+ if (postfixNode.primary)
144
+ count += countBooleanOperators(postfixNode.primary);
145
+ break;
146
+ }
147
+ }
148
+ return count;
149
+ }
150
+ /**
151
+ * Calculate maximum nesting depth of boolean operators.
152
+ */
153
+ function getBooleanNestingDepth(node, currentDepth = 0) {
154
+ let maxDepth = currentDepth;
155
+ if (node.type === 'BinaryExpr') {
156
+ const binaryNode = node;
157
+ const depth = binaryNode.op === '&&' || binaryNode.op === '||'
158
+ ? currentDepth + 1
159
+ : currentDepth;
160
+ const leftDepth = getBooleanNestingDepth(binaryNode.left, depth);
161
+ const rightDepth = getBooleanNestingDepth(binaryNode.right, depth);
162
+ maxDepth = Math.max(maxDepth, leftDepth, rightDepth);
163
+ }
164
+ // Traverse other node types
165
+ switch (node.type) {
166
+ case 'UnaryExpr': {
167
+ const unaryNode = node;
168
+ maxDepth = Math.max(maxDepth, getBooleanNestingDepth(unaryNode.operand, currentDepth));
169
+ break;
170
+ }
171
+ case 'GroupedExpr': {
172
+ const groupedNode = node;
173
+ maxDepth = Math.max(maxDepth, getBooleanNestingDepth(groupedNode.expression, currentDepth));
174
+ break;
175
+ }
176
+ case 'PipeChain': {
177
+ const pipeNode = node;
178
+ if (pipeNode.head) {
179
+ maxDepth = Math.max(maxDepth, getBooleanNestingDepth(pipeNode.head, currentDepth));
180
+ }
181
+ if (pipeNode.pipes) {
182
+ for (const pipe of pipeNode.pipes) {
183
+ maxDepth = Math.max(maxDepth, getBooleanNestingDepth(pipe, currentDepth));
184
+ }
185
+ }
186
+ break;
187
+ }
188
+ case 'PostfixExpr': {
189
+ const postfixNode = node;
190
+ if (postfixNode.primary) {
191
+ maxDepth = Math.max(maxDepth, getBooleanNestingDepth(postfixNode.primary, currentDepth));
192
+ }
193
+ break;
194
+ }
195
+ }
196
+ return maxDepth;
197
+ }
198
+ // ============================================================
199
+ // LOOP_OUTER_CAPTURE RULE
200
+ // ============================================================
201
+ /**
202
+ * Detects attempts to modify outer-scope variables from inside loops.
203
+ * This is a common LLM-generated anti-pattern that never works in Rill.
204
+ *
205
+ * Rill's scoping rules mean that captures inside loop bodies create LOCAL
206
+ * variables that don't affect outer scope. This is a fundamental language
207
+ * constraint, not a style preference.
208
+ *
209
+ * WRONG - this pattern NEVER works:
210
+ * 0 :> $count
211
+ * [1, 2, 3] -> each { $count + 1 :> $count } # creates LOCAL $count
212
+ * $count # still 0!
213
+ *
214
+ * RIGHT - use accumulators:
215
+ * [1, 2, 3] -> fold(0) { $@ + 1 } # returns 3
216
+ * [1, 2, 3] -> each(0) { $@ + 1 } # returns [1, 2, 3]
217
+ *
218
+ * This rule catches captures inside loop/collection bodies where the
219
+ * variable name matches an outer-scope variable.
220
+ *
221
+ * References:
222
+ * - docs/99_llm-reference.txt (LOOP STATE PATTERNS)
223
+ * - docs/03_variables.md (Scope Rules)
224
+ */
225
+ export const LOOP_OUTER_CAPTURE = {
226
+ code: 'LOOP_OUTER_CAPTURE',
227
+ category: 'anti-patterns',
228
+ severity: 'warning',
229
+ nodeTypes: [
230
+ 'EachExpr',
231
+ 'MapExpr',
232
+ 'FilterExpr',
233
+ 'FoldExpr',
234
+ 'WhileLoop',
235
+ 'DoWhileLoop',
236
+ ],
237
+ validate(node, context) {
238
+ const diagnostics = [];
239
+ // Get the loop body based on node type
240
+ let body = null;
241
+ switch (node.type) {
242
+ case 'EachExpr':
243
+ body = node.body;
244
+ break;
245
+ case 'MapExpr':
246
+ body = node.body;
247
+ break;
248
+ case 'FilterExpr':
249
+ body = node.body;
250
+ break;
251
+ case 'FoldExpr':
252
+ body = node.body;
253
+ break;
254
+ case 'WhileLoop':
255
+ body = node.body;
256
+ break;
257
+ case 'DoWhileLoop':
258
+ body = node.body;
259
+ break;
260
+ }
261
+ if (!body)
262
+ return diagnostics;
263
+ // Find all captures in the body
264
+ const captures = findCapturesInBody(body);
265
+ // Check if any capture targets an outer-scope variable
266
+ for (const capture of captures) {
267
+ if (context.variables.has(capture.name)) {
268
+ const outerLocation = context.variables.get(capture.name);
269
+ diagnostics.push({
270
+ location: capture.span.start,
271
+ severity: 'warning',
272
+ code: 'LOOP_OUTER_CAPTURE',
273
+ message: `Cannot modify outer variable '$${capture.name}' from inside loop. ` +
274
+ `Captures inside loops create LOCAL variables. ` +
275
+ `Use fold(init) with $@ accumulator, or pack state into $ as a dict. ` +
276
+ `(Outer '$${capture.name}' defined at line ${outerLocation.line})`,
277
+ context: extractContextLine(capture.span.start.line, context.source),
278
+ fix: null,
279
+ });
280
+ }
281
+ }
282
+ return diagnostics;
283
+ },
284
+ };
285
+ /**
286
+ * Recursively find all Capture nodes in a loop body.
287
+ */
288
+ function findCapturesInBody(node) {
289
+ const captures = [];
290
+ function traverse(n) {
291
+ if (n.type === 'Capture') {
292
+ captures.push(n);
293
+ return;
294
+ }
295
+ // Traverse children based on node type
296
+ switch (n.type) {
297
+ case 'Block':
298
+ for (const stmt of n.statements)
299
+ traverse(stmt);
300
+ break;
301
+ case 'Statement':
302
+ traverse(n.expression);
303
+ break;
304
+ case 'AnnotatedStatement':
305
+ traverse(n.statement);
306
+ break;
307
+ case 'PipeChain':
308
+ traverse(n.head);
309
+ for (const pipe of n.pipes)
310
+ traverse(pipe);
311
+ if (n.terminator)
312
+ traverse(n.terminator);
313
+ break;
314
+ case 'PostfixExpr':
315
+ traverse(n.primary);
316
+ for (const method of n.methods)
317
+ traverse(method);
318
+ break;
319
+ case 'BinaryExpr':
320
+ traverse(n.left);
321
+ traverse(n.right);
322
+ break;
323
+ case 'UnaryExpr':
324
+ traverse(n.operand);
325
+ break;
326
+ case 'GroupedExpr':
327
+ traverse(n.expression);
328
+ break;
329
+ case 'Conditional':
330
+ if (n.input)
331
+ traverse(n.input);
332
+ if (n.condition)
333
+ traverse(n.condition);
334
+ traverse(n.thenBranch);
335
+ if (n.elseBranch)
336
+ traverse(n.elseBranch);
337
+ break;
338
+ case 'Closure':
339
+ // Don't traverse into closures - they have their own scope
340
+ break;
341
+ // Nested loops - traverse their bodies too
342
+ case 'WhileLoop':
343
+ traverse(n.body);
344
+ break;
345
+ case 'DoWhileLoop':
346
+ traverse(n.body);
347
+ break;
348
+ case 'EachExpr':
349
+ case 'MapExpr':
350
+ case 'FilterExpr':
351
+ case 'FoldExpr':
352
+ traverse(n.body);
353
+ break;
354
+ }
355
+ }
356
+ traverse(node);
357
+ return captures;
358
+ }
359
+ /**
360
+ * Calculate maximum consecutive GroupedExpr (parenthetical) nesting depth.
361
+ * Counts chains of nested parentheses like ((($x))).
362
+ * Treats PipeChain (single head) and PostfixExpr (primary only) as transparent wrappers.
363
+ */
364
+ function getParenNestingDepth(node) {
365
+ let maxDepth = 0;
366
+ function traverse(n, consecutiveDepth) {
367
+ if (n.type === 'GroupedExpr') {
368
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
369
+ const groupedNode = n;
370
+ const newDepth = consecutiveDepth + 1;
371
+ maxDepth = Math.max(maxDepth, newDepth);
372
+ traverse(groupedNode.expression, newDepth);
373
+ }
374
+ else if (n.type === 'PipeChain') {
375
+ // Treat simple PipeChain (head only) as transparent for nesting
376
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
377
+ const pipeNode = n;
378
+ if (pipeNode.head && (!pipeNode.pipes || pipeNode.pipes.length === 0)) {
379
+ // Transparent: pass through consecutive depth
380
+ traverse(pipeNode.head, consecutiveDepth);
381
+ }
382
+ else {
383
+ // Complex pipe chain: reset depth but continue traversing
384
+ if (pipeNode.head)
385
+ traverse(pipeNode.head, 0);
386
+ if (pipeNode.pipes) {
387
+ for (const pipe of pipeNode.pipes) {
388
+ traverse(pipe, 0);
389
+ }
390
+ }
391
+ }
392
+ }
393
+ else if (n.type === 'PostfixExpr') {
394
+ // Treat simple PostfixExpr (primary only) as transparent for nesting
395
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
396
+ const postfixNode = n;
397
+ if (postfixNode.primary &&
398
+ (!postfixNode.accessChain || postfixNode.accessChain.length === 0)) {
399
+ // Transparent: pass through consecutive depth
400
+ traverse(postfixNode.primary, consecutiveDepth);
401
+ }
402
+ else {
403
+ // Complex postfix: reset depth
404
+ if (postfixNode.primary)
405
+ traverse(postfixNode.primary, 0);
406
+ }
407
+ }
408
+ else {
409
+ // Reset consecutive depth when we hit a structural node
410
+ // but continue traversing children
411
+ if (n.type === 'BinaryExpr') {
412
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
413
+ const binaryNode = n;
414
+ traverse(binaryNode.left, 0);
415
+ traverse(binaryNode.right, 0);
416
+ }
417
+ else if (n.type === 'UnaryExpr') {
418
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
419
+ const unaryNode = n;
420
+ traverse(unaryNode.operand, 0);
421
+ }
422
+ }
423
+ }
424
+ traverse(node, 0);
425
+ return maxDepth;
426
+ }
427
+ //# sourceMappingURL=anti-patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anti-patterns.js","sourceRoot":"","sources":["../../../src/check/rules/anti-patterns.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAmBH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,+DAA+D;AAC/D,0BAA0B;AAC1B,+DAA+D;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAmB;IAChD,IAAI,EAAE,oBAAoB;IAC1B,QAAQ,EAAE,eAAe;IACzB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE,CAAC,SAAS,CAAC;IAEtB,QAAQ,CAAC,IAAa,EAAE,OAA0B;QAChD,MAAM,WAAW,GAAG,IAAmB,CAAC;QACxC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC;QAEjC,qDAAqD;QACrD,IAAI,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACnC,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAE,CAAC;YAEtD,OAAO;gBACL;oBACE,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,KAAK;oBAChC,QAAQ,EAAE,SAAS;oBACnB,IAAI,EAAE,oBAAoB;oBAC1B,OAAO,EAAE,qCAAqC,OAAO,2BAA2B,aAAa,CAAC,IAAI,4CAA4C;oBAC9I,OAAO,EAAE,kBAAkB,CACzB,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAC3B,OAAO,CAAC,MAAM,CACf;oBACD,GAAG,EAAE,IAAI,EAAE,+CAA+C;iBAC3D;aACF,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC;AAEF,+DAA+D;AAC/D,yBAAyB;AACzB,+DAA+D;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAmB;IAC/C,IAAI,EAAE,mBAAmB;IACzB,QAAQ,EAAE,eAAe;IACzB,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,CAAC,aAAa,CAAC;IAE1B,QAAQ,CAAC,IAAa,EAAE,OAA0B;QAChD,MAAM,eAAe,GAAG,IAAuB,CAAC;QAChD,MAAM,SAAS,GAAG,eAAe,CAAC,SAAS,CAAC;QAE5C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,oDAAoD;QACpD,IAAI,kBAAkB,GAAY,SAAS,CAAC;QAC5C,IAAI,kBAAkB,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YAC9C,kBAAkB,GAAI,kBAAsC,CAAC,UAAU,CAAC;QAC1E,CAAC;QAED,4EAA4E;QAC5E,MAAM,aAAa,GAAG,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;QAChE,MAAM,YAAY,GAAG,sBAAsB,CAAC,kBAAkB,CAAC,CAAC;QAChE,MAAM,UAAU,GAAG,oBAAoB,CAAC,kBAAkB,CAAC,CAAC;QAE5D,4EAA4E;QAC5E,IAAI,aAAa,IAAI,CAAC,IAAI,YAAY,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YAC7D,OAAO;gBACL;oBACE,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,KAAK;oBACpC,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,mBAAmB;oBACzB,OAAO,EACL,iFAAiF;oBACnF,OAAO,EAAE,kBAAkB,CACzB,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAC/B,OAAO,CAAC,MAAM,CACf;oBACD,GAAG,EAAE,IAAI,EAAE,gDAAgD;iBAC5D;aACF,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,SAAS,qBAAqB,CAAC,IAAa;IAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAC/B,MAAM,UAAU,GAAG,IAAW,CAAC;QAC/B,IAAI,UAAU,CAAC,EAAE,KAAK,IAAI,IAAI,UAAU,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;YACrD,KAAK,GAAG,CAAC,CAAC;QACZ,CAAC;QAED,KAAK,IAAI,qBAAqB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAChD,KAAK,IAAI,qBAAqB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACnD,CAAC;IAED,2DAA2D;IAC3D,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,SAAS,GAAG,IAAW,CAAC;YAC9B,KAAK,IAAI,qBAAqB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAClD,MAAM;QACR,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,WAAW,GAAG,IAAW,CAAC;YAChC,KAAK,IAAI,qBAAqB,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;YACvD,MAAM;QACR,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,QAAQ,GAAG,IAAW,CAAC;YAC7B,IAAI,QAAQ,CAAC,IAAI;gBAAE,KAAK,IAAI,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACjE,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACnB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;oBAClC,KAAK,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC;gBACvC,CAAC;YACH,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,WAAW,GAAG,IAAW,CAAC;YAChC,IAAI,WAAW,CAAC,OAAO;gBACrB,KAAK,IAAI,qBAAqB,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YACtD,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,IAAa,EAAE,YAAY,GAAG,CAAC;IAC7D,IAAI,QAAQ,GAAG,YAAY,CAAC;IAE5B,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAC/B,MAAM,UAAU,GAAG,IAAW,CAAC;QAC/B,MAAM,KAAK,GACT,UAAU,CAAC,EAAE,KAAK,IAAI,IAAI,UAAU,CAAC,EAAE,KAAK,IAAI;YAC9C,CAAC,CAAC,YAAY,GAAG,CAAC;YAClB,CAAC,CAAC,YAAY,CAAC;QAEnB,MAAM,SAAS,GAAG,sBAAsB,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACjE,MAAM,UAAU,GAAG,sBAAsB,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAEnE,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IACvD,CAAC;IAED,4BAA4B;IAC5B,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,SAAS,GAAG,IAAW,CAAC;YAC9B,QAAQ,GAAG,IAAI,CAAC,GAAG,CACjB,QAAQ,EACR,sBAAsB,CAAC,SAAS,CAAC,OAAO,EAAE,YAAY,CAAC,CACxD,CAAC;YACF,MAAM;QACR,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,WAAW,GAAG,IAAW,CAAC;YAChC,QAAQ,GAAG,IAAI,CAAC,GAAG,CACjB,QAAQ,EACR,sBAAsB,CAAC,WAAW,CAAC,UAAU,EAAE,YAAY,CAAC,CAC7D,CAAC;YACF,MAAM;QACR,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,QAAQ,GAAG,IAAW,CAAC;YAC7B,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAClB,QAAQ,GAAG,IAAI,CAAC,GAAG,CACjB,QAAQ,EACR,sBAAsB,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CACpD,CAAC;YACJ,CAAC;YACD,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACnB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;oBAClC,QAAQ,GAAG,IAAI,CAAC,GAAG,CACjB,QAAQ,EACR,sBAAsB,CAAC,IAAI,EAAE,YAAY,CAAC,CAC3C,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,WAAW,GAAG,IAAW,CAAC;YAChC,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;gBACxB,QAAQ,GAAG,IAAI,CAAC,GAAG,CACjB,QAAQ,EACR,sBAAsB,CAAC,WAAW,CAAC,OAAO,EAAE,YAAY,CAAC,CAC1D,CAAC;YACJ,CAAC;YACD,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,+DAA+D;AAC/D,0BAA0B;AAC1B,+DAA+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAmB;IAChD,IAAI,EAAE,oBAAoB;IAC1B,QAAQ,EAAE,eAAe;IACzB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE;QACT,UAAU;QACV,SAAS;QACT,YAAY;QACZ,UAAU;QACV,WAAW;QACX,aAAa;KACd;IAED,QAAQ,CAAC,IAAa,EAAE,OAA0B;QAChD,MAAM,WAAW,GAAiB,EAAE,CAAC;QAErC,uCAAuC;QACvC,IAAI,IAAI,GAAmB,IAAI,CAAC;QAChC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAClB,KAAK,UAAU;gBACb,IAAI,GAAI,IAAqB,CAAC,IAAI,CAAC;gBACnC,MAAM;YACR,KAAK,SAAS;gBACZ,IAAI,GAAI,IAAoB,CAAC,IAAI,CAAC;gBAClC,MAAM;YACR,KAAK,YAAY;gBACf,IAAI,GAAI,IAAuB,CAAC,IAAI,CAAC;gBACrC,MAAM;YACR,KAAK,UAAU;gBACb,IAAI,GAAI,IAAqB,CAAC,IAAI,CAAC;gBACnC,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,GAAI,IAAsB,CAAC,IAAI,CAAC;gBACpC,MAAM;YACR,KAAK,aAAa;gBAChB,IAAI,GAAI,IAAwB,CAAC,IAAI,CAAC;gBACtC,MAAM;QACV,CAAC;QAED,IAAI,CAAC,IAAI;YAAE,OAAO,WAAW,CAAC;QAE9B,gCAAgC;QAChC,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAE1C,uDAAuD;QACvD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxC,MAAM,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAE,CAAC;gBAC3D,WAAW,CAAC,IAAI,CAAC;oBACf,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK;oBAC5B,QAAQ,EAAE,SAAS;oBACnB,IAAI,EAAE,oBAAoB;oBAC1B,OAAO,EACL,kCAAkC,OAAO,CAAC,IAAI,sBAAsB;wBACpE,gDAAgD;wBAChD,sEAAsE;wBACtE,YAAY,OAAO,CAAC,IAAI,qBAAqB,aAAa,CAAC,IAAI,GAAG;oBACpE,OAAO,EAAE,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC;oBACpE,GAAG,EAAE,IAAI;iBACV,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,SAAS,kBAAkB,CAAC,IAAa;IACvC,MAAM,QAAQ,GAAkB,EAAE,CAAC;IAEnC,SAAS,QAAQ,CAAC,CAAU;QAC1B,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACzB,QAAQ,CAAC,IAAI,CAAC,CAAgB,CAAC,CAAC;YAChC,OAAO;QACT,CAAC;QAED,uCAAuC;QACvC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC;YACf,KAAK,OAAO;gBACV,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,UAAU;oBAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAChD,MAAM;YACR,KAAK,WAAW;gBACd,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;gBACvB,MAAM;YACR,KAAK,oBAAoB;gBACvB,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBACtB,MAAM;YACR,KAAK,WAAW;gBACd,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACjB,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;oBAAE,QAAQ,CAAC,IAAe,CAAC,CAAC;gBACtD,IAAI,CAAC,CAAC,UAAU;oBAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;gBACzC,MAAM;YACR,KAAK,aAAa;gBAChB,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBACpB,KAAK,MAAM,MAAM,IAAI,CAAC,CAAC,OAAO;oBAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACjD,MAAM;YACR,KAAK,YAAY;gBACf,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACjB,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gBAClB,MAAM;YACR,KAAK,WAAW;gBACd,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;gBACpB,MAAM;YACR,KAAK,aAAa;gBAChB,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;gBACvB,MAAM;YACR,KAAK,aAAa;gBAChB,IAAI,CAAC,CAAC,KAAK;oBAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC/B,IAAI,CAAC,CAAC,SAAS;oBAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBACvC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;gBACvB,IAAI,CAAC,CAAC,UAAU;oBAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;gBACzC,MAAM;YACR,KAAK,SAAS;gBACZ,2DAA2D;gBAC3D,MAAM;YACR,2CAA2C;YAC3C,KAAK,WAAW;gBACd,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACjB,MAAM;YACR,KAAK,aAAa;gBAChB,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACjB,MAAM;YACR,KAAK,UAAU,CAAC;YAChB,KAAK,SAAS,CAAC;YACf,KAAK,YAAY,CAAC;YAClB,KAAK,UAAU;gBACb,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACjB,MAAM;QACV,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,CAAC;IACf,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAS,oBAAoB,CAAC,IAAa;IACzC,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,SAAS,QAAQ,CAAC,CAAU,EAAE,gBAAwB;QACpD,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YAC7B,8DAA8D;YAC9D,MAAM,WAAW,GAAG,CAAQ,CAAC;YAC7B,MAAM,QAAQ,GAAG,gBAAgB,GAAG,CAAC,CAAC;YACtC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YACxC,QAAQ,CAAC,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAClC,gEAAgE;YAChE,8DAA8D;YAC9D,MAAM,QAAQ,GAAG,CAAQ,CAAC;YAC1B,IAAI,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;gBACtE,8CAA8C;gBAC9C,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACN,0DAA0D;gBAC1D,IAAI,QAAQ,CAAC,IAAI;oBAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC9C,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;oBACnB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;wBAClC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;oBACpB,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YACpC,qEAAqE;YACrE,8DAA8D;YAC9D,MAAM,WAAW,GAAG,CAAQ,CAAC;YAC7B,IACE,WAAW,CAAC,OAAO;gBACnB,CAAC,CAAC,WAAW,CAAC,WAAW,IAAI,WAAW,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,EAClE,CAAC;gBACD,8CAA8C;gBAC9C,QAAQ,CAAC,WAAW,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;YAClD,CAAC;iBAAM,CAAC;gBACN,+BAA+B;gBAC/B,IAAI,WAAW,CAAC,OAAO;oBAAE,QAAQ,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;aAAM,CAAC;YACN,wDAAwD;YACxD,mCAAmC;YACnC,IAAI,CAAC,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC5B,8DAA8D;gBAC9D,MAAM,UAAU,GAAG,CAAQ,CAAC;gBAC5B,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC7B,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAChC,CAAC;iBAAM,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAClC,8DAA8D;gBAC9D,MAAM,SAAS,GAAG,CAAQ,CAAC;gBAC3B,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAClB,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Closure Convention Rules
3
+ * Enforces closure best practices from docs/16_conventions.md:237-286.
4
+ */
5
+ import type { ValidationRule } from '../types.js';
6
+ /**
7
+ * Warns on bare $ in stored closures without parameters.
8
+ * Bare $ in stored closures has ambiguous binding - it refers to the
9
+ * pipe value at closure invocation time, not definition time.
10
+ *
11
+ * Detection:
12
+ * - Zero-parameter closures (|| { }) used outside dict context
13
+ * - Body contains bare $ references (VariableNode with name '$')
14
+ *
15
+ * Valid patterns:
16
+ * - Dict closures: [count: ||{ $.items -> .len }] ($ binds to dict)
17
+ * - Parameterized closures: |x|{ $x } (explicit params)
18
+ * - Inline blocks: -> { $ * 2 } (immediate evaluation)
19
+ *
20
+ * References:
21
+ * - docs/16_conventions.md:251-261
22
+ * - docs/06_closures.md: Late binding section
23
+ */
24
+ export declare const CLOSURE_BARE_DOLLAR: ValidationRule;
25
+ /**
26
+ * Enforces braces for complex closure bodies.
27
+ * Simple expressions can use parentheses, but complex bodies need braces.
28
+ *
29
+ * Complex body criteria:
30
+ * - Contains Block (multiple statements)
31
+ * - Contains Conditional
32
+ * - Contains loop constructs
33
+ *
34
+ * Simple bodies (parentheses OK):
35
+ * - Single expression: |x|($x * 2)
36
+ * - Single method chain: |s|($s.trim.lower)
37
+ *
38
+ * Complex bodies (braces required):
39
+ * - Conditionals: |n| { ($n < 1) ? 1 ! ($n * $fact($n - 1)) }
40
+ * - Multiple statements: |x| { $x :> $y; $y * 2 }
41
+ *
42
+ * References:
43
+ * - docs/16_conventions.md:239-249
44
+ */
45
+ export declare const CLOSURE_BRACES: ValidationRule;
46
+ /**
47
+ * Detects closures created in loops that may suffer from late binding issues.
48
+ * When creating closures inside loops, variables are captured by reference,
49
+ * not by value. This causes all closures to share the final loop value.
50
+ *
51
+ * Detection:
52
+ * - Each loop body creates a Closure node
53
+ * - Closure references loop variable ($) without explicit capture
54
+ *
55
+ * Solution: Explicit capture per iteration:
56
+ * [1, 2, 3] -> each {
57
+ * $ :> $item
58
+ * || { $item }
59
+ * }
60
+ *
61
+ * References:
62
+ * - docs/16_conventions.md:251-261
63
+ * - docs/06_closures.md: Late binding section
64
+ */
65
+ export declare const CLOSURE_LATE_BINDING: ValidationRule;
66
+ //# sourceMappingURL=closures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"closures.d.ts","sourceRoot":"","sources":["../../../src/check/rules/closures.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,aAAa,CAAC;AAarB;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,mBAAmB,EAAE,cAoCjC,CAAC;AAmIF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,cAAc,EAAE,cAsD5B,CAAC;AAMF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,oBAAoB,EAAE,cAqClC,CAAC"}