@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.
- package/README.md +16 -8
- package/dist/check/config.d.ts +20 -0
- package/dist/check/config.d.ts.map +1 -0
- package/dist/check/config.js +151 -0
- package/dist/check/config.js.map +1 -0
- package/dist/check/fixer.d.ts +39 -0
- package/dist/check/fixer.d.ts.map +1 -0
- package/dist/check/fixer.js +119 -0
- package/dist/check/fixer.js.map +1 -0
- package/dist/check/index.d.ts +10 -0
- package/dist/check/index.d.ts.map +1 -0
- package/dist/check/index.js +21 -0
- package/dist/check/index.js.map +1 -0
- package/dist/check/rules/anti-patterns.d.ts +65 -0
- package/dist/check/rules/anti-patterns.d.ts.map +1 -0
- package/dist/check/rules/anti-patterns.js +427 -0
- package/dist/check/rules/anti-patterns.js.map +1 -0
- package/dist/check/rules/closures.d.ts +66 -0
- package/dist/check/rules/closures.d.ts.map +1 -0
- package/dist/check/rules/closures.js +373 -0
- package/dist/check/rules/closures.js.map +1 -0
- package/dist/check/rules/collections.d.ts +90 -0
- package/dist/check/rules/collections.d.ts.map +1 -0
- package/dist/check/rules/collections.js +373 -0
- package/dist/check/rules/collections.js.map +1 -0
- package/dist/check/rules/conditionals.d.ts +41 -0
- package/dist/check/rules/conditionals.d.ts.map +1 -0
- package/dist/check/rules/conditionals.js +106 -0
- package/dist/check/rules/conditionals.js.map +1 -0
- package/dist/check/rules/flow.d.ts +46 -0
- package/dist/check/rules/flow.d.ts.map +1 -0
- package/dist/check/rules/flow.js +206 -0
- package/dist/check/rules/flow.js.map +1 -0
- package/dist/check/rules/formatting.d.ts +133 -0
- package/dist/check/rules/formatting.d.ts.map +1 -0
- package/dist/check/rules/formatting.js +639 -0
- package/dist/check/rules/formatting.js.map +1 -0
- package/dist/check/rules/helpers.d.ts +26 -0
- package/dist/check/rules/helpers.d.ts.map +1 -0
- package/dist/check/rules/helpers.js +66 -0
- package/dist/check/rules/helpers.js.map +1 -0
- package/dist/check/rules/index.d.ts +21 -0
- package/dist/check/rules/index.d.ts.map +1 -0
- package/dist/check/rules/index.js +78 -0
- package/dist/check/rules/index.js.map +1 -0
- package/dist/check/rules/loops.d.ts +70 -0
- package/dist/check/rules/loops.d.ts.map +1 -0
- package/dist/check/rules/loops.js +227 -0
- package/dist/check/rules/loops.js.map +1 -0
- package/dist/check/rules/naming.d.ts +21 -0
- package/dist/check/rules/naming.d.ts.map +1 -0
- package/dist/check/rules/naming.js +167 -0
- package/dist/check/rules/naming.js.map +1 -0
- package/dist/check/rules/strings.d.ts +28 -0
- package/dist/check/rules/strings.d.ts.map +1 -0
- package/dist/check/rules/strings.js +80 -0
- package/dist/check/rules/strings.js.map +1 -0
- package/dist/check/rules/types.d.ts +41 -0
- package/dist/check/rules/types.d.ts.map +1 -0
- package/dist/check/rules/types.js +162 -0
- package/dist/check/rules/types.js.map +1 -0
- package/dist/check/types.d.ts +106 -0
- package/dist/check/types.d.ts.map +1 -0
- package/dist/check/types.js +6 -0
- package/dist/check/types.js.map +1 -0
- package/dist/check/validator.d.ts +18 -0
- package/dist/check/validator.d.ts.map +1 -0
- package/dist/check/validator.js +88 -0
- package/dist/check/validator.js.map +1 -0
- package/dist/check/visitor.d.ts +33 -0
- package/dist/check/visitor.d.ts.map +1 -0
- package/dist/check/visitor.js +243 -0
- package/dist/check/visitor.js.map +1 -0
- package/dist/cli-check.d.ts +43 -0
- package/dist/cli-check.d.ts.map +1 -0
- package/dist/cli-check.js +356 -0
- package/dist/cli-check.js.map +1 -0
- package/dist/cli-eval.d.ts +15 -0
- package/dist/cli-eval.d.ts.map +1 -0
- package/dist/cli-eval.js +120 -0
- package/dist/cli-eval.js.map +1 -0
- package/dist/cli-exec.d.ts +49 -0
- package/dist/cli-exec.d.ts.map +1 -0
- package/dist/cli-exec.js +191 -0
- package/dist/cli-exec.js.map +1 -0
- package/dist/cli-module-loader.d.ts +19 -0
- package/dist/cli-module-loader.d.ts.map +1 -0
- package/dist/cli-module-loader.js +83 -0
- package/dist/cli-module-loader.js.map +1 -0
- package/dist/cli-shared.d.ts +36 -0
- package/dist/cli-shared.d.ts.map +1 -0
- package/dist/cli-shared.js +101 -0
- package/dist/cli-shared.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lexer/readers.d.ts +1 -1
- package/dist/lexer/readers.d.ts.map +1 -1
- package/dist/lexer/readers.js +62 -32
- package/dist/lexer/readers.js.map +1 -1
- package/dist/lexer/tokenizer.d.ts.map +1 -1
- package/dist/lexer/tokenizer.js +5 -6
- package/dist/lexer/tokenizer.js.map +1 -1
- package/dist/parser/index.js +1 -1
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/parser-expr.js +23 -5
- package/dist/parser/parser-expr.js.map +1 -1
- package/dist/parser/parser-functions.d.ts +2 -2
- package/dist/parser/parser-functions.d.ts.map +1 -1
- package/dist/parser/parser-functions.js +2 -1
- package/dist/parser/parser-functions.js.map +1 -1
- package/dist/parser/parser-literals.js +2 -2
- package/dist/parser/parser-literals.js.map +1 -1
- package/dist/parser/parser-script.js +9 -7
- package/dist/parser/parser-script.js.map +1 -1
- package/dist/parser/parser-variables.js +4 -3
- package/dist/parser/parser-variables.js.map +1 -1
- package/dist/runtime/core/callable.d.ts +5 -6
- package/dist/runtime/core/callable.d.ts.map +1 -1
- package/dist/runtime/core/callable.js.map +1 -1
- package/dist/runtime/core/context.d.ts.map +1 -1
- package/dist/runtime/core/context.js +19 -32
- package/dist/runtime/core/context.js.map +1 -1
- package/dist/runtime/core/equals.js +1 -1
- package/dist/runtime/core/equals.js.map +1 -1
- package/dist/runtime/core/eval/evaluator.d.ts +78 -0
- package/dist/runtime/core/eval/evaluator.d.ts.map +1 -1
- package/dist/runtime/core/eval/evaluator.js +78 -0
- package/dist/runtime/core/eval/evaluator.js.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/closures.js +9 -1
- package/dist/runtime/core/eval/mixins/closures.js.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.d.ts.map +1 -1
- package/dist/runtime/core/eval/mixins/variables.js +143 -2
- package/dist/runtime/core/eval/mixins/variables.js.map +1 -1
- package/dist/runtime/core/types.d.ts +15 -2
- package/dist/runtime/core/types.d.ts.map +1 -1
- package/dist/runtime/core/types.js.map +1 -1
- package/dist/runtime/ext/extensions.d.ts +51 -0
- package/dist/runtime/ext/extensions.d.ts.map +1 -0
- package/dist/runtime/ext/extensions.js +67 -0
- package/dist/runtime/ext/extensions.js.map +1 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/index.js.map +1 -1
- package/dist/types.d.ts +8 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +5 -4
- package/dist/types.js.map +1 -1
- package/docs/00_INDEX.md +1 -0
- package/docs/01_guide.md +3 -3
- package/docs/02_types.md +8 -10
- package/docs/03_variables.md +10 -0
- package/docs/04_operators.md +3 -3
- package/docs/05_control-flow.md +21 -0
- package/docs/07_collections.md +2 -0
- package/docs/10_parsing.md +9 -9
- package/docs/11_reference.md +1 -1
- package/docs/12_examples.md +36 -62
- package/docs/14_host-integration.md +116 -111
- package/docs/15_grammar.ebnf +1 -5
- package/docs/16_conventions.md +3 -4
- package/docs/17_cli-tools.md +184 -0
- package/docs/99_llm-reference.txt +46 -5
- package/package.json +13 -4
- package/dist/demo.d.ts +0 -6
- package/dist/demo.d.ts.map +0 -1
- package/dist/demo.js +0 -121
- package/dist/demo.js.map +0 -1
- package/dist/lexer.d.ts +0 -19
- package/dist/lexer.d.ts.map +0 -1
- package/dist/lexer.js +0 -344
- package/dist/lexer.js.map +0 -1
- package/dist/parser/arithmetic.d.ts +0 -16
- package/dist/parser/arithmetic.d.ts.map +0 -1
- package/dist/parser/arithmetic.js +0 -128
- package/dist/parser/arithmetic.js.map +0 -1
- package/dist/parser/boolean.d.ts +0 -15
- package/dist/parser/boolean.d.ts.map +0 -1
- package/dist/parser/boolean.js +0 -20
- package/dist/parser/boolean.js.map +0 -1
- package/dist/parser/control-flow.d.ts +0 -56
- package/dist/parser/control-flow.d.ts.map +0 -1
- package/dist/parser/control-flow.js +0 -167
- package/dist/parser/control-flow.js.map +0 -1
- package/dist/parser/expressions.d.ts +0 -23
- package/dist/parser/expressions.d.ts.map +0 -1
- package/dist/parser/expressions.js +0 -950
- package/dist/parser/expressions.js.map +0 -1
- package/dist/parser/extraction.d.ts +0 -48
- package/dist/parser/extraction.d.ts.map +0 -1
- package/dist/parser/extraction.js +0 -279
- package/dist/parser/extraction.js.map +0 -1
- package/dist/parser/functions.d.ts +0 -20
- package/dist/parser/functions.d.ts.map +0 -1
- package/dist/parser/functions.js +0 -96
- package/dist/parser/functions.js.map +0 -1
- package/dist/parser/literals.d.ts +0 -37
- package/dist/parser/literals.d.ts.map +0 -1
- package/dist/parser/literals.js +0 -373
- package/dist/parser/literals.js.map +0 -1
- package/dist/parser/script.d.ts +0 -14
- package/dist/parser/script.d.ts.map +0 -1
- package/dist/parser/script.js +0 -196
- package/dist/parser/script.js.map +0 -1
- package/dist/parser/variables.d.ts +0 -10
- package/dist/parser/variables.d.ts.map +0 -1
- package/dist/parser/variables.js +0 -215
- package/dist/parser/variables.js.map +0 -1
- package/dist/runtime/ast-equals.d.ts +0 -13
- package/dist/runtime/ast-equals.d.ts.map +0 -1
- package/dist/runtime/ast-equals.js +0 -447
- package/dist/runtime/ast-equals.js.map +0 -1
- package/dist/runtime/builtins.d.ts +0 -13
- package/dist/runtime/builtins.d.ts.map +0 -1
- package/dist/runtime/builtins.js +0 -180
- package/dist/runtime/builtins.js.map +0 -1
- package/dist/runtime/callable.d.ts +0 -88
- package/dist/runtime/callable.d.ts.map +0 -1
- package/dist/runtime/callable.js +0 -98
- package/dist/runtime/callable.js.map +0 -1
- package/dist/runtime/context.d.ts +0 -13
- package/dist/runtime/context.d.ts.map +0 -1
- package/dist/runtime/context.js +0 -73
- package/dist/runtime/context.js.map +0 -1
- package/dist/runtime/core/evaluate.d.ts +0 -42
- package/dist/runtime/core/evaluate.d.ts.map +0 -1
- package/dist/runtime/core/evaluate.debug.js +0 -1251
- package/dist/runtime/core/evaluate.js +0 -1913
- package/dist/runtime/core/evaluate.js.map +0 -1
- package/dist/runtime/evaluate.d.ts +0 -32
- package/dist/runtime/evaluate.d.ts.map +0 -1
- package/dist/runtime/evaluate.js +0 -1111
- package/dist/runtime/evaluate.js.map +0 -1
- package/dist/runtime/execute.d.ts +0 -26
- package/dist/runtime/execute.d.ts.map +0 -1
- package/dist/runtime/execute.js +0 -121
- package/dist/runtime/execute.js.map +0 -1
- package/dist/runtime/signals.d.ts +0 -19
- package/dist/runtime/signals.d.ts.map +0 -1
- package/dist/runtime/signals.js +0 -26
- package/dist/runtime/signals.js.map +0 -1
- package/dist/runtime/types.d.ts +0 -169
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -50
- package/dist/runtime/types.js.map +0 -1
- package/dist/runtime/values.d.ts +0 -50
- package/dist/runtime/values.d.ts.map +0 -1
- package/dist/runtime/values.js +0 -209
- package/dist/runtime/values.js.map +0 -1
- package/dist/runtime.d.ts +0 -254
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -2014
- package/dist/runtime.js.map +0 -1
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Closure Convention Rules
|
|
3
|
+
* Enforces closure best practices from docs/16_conventions.md:237-286.
|
|
4
|
+
*/
|
|
5
|
+
import { extractContextLine } from './helpers.js';
|
|
6
|
+
// ============================================================
|
|
7
|
+
// CLOSURE_BARE_DOLLAR RULE
|
|
8
|
+
// ============================================================
|
|
9
|
+
/**
|
|
10
|
+
* Warns on bare $ in stored closures without parameters.
|
|
11
|
+
* Bare $ in stored closures has ambiguous binding - it refers to the
|
|
12
|
+
* pipe value at closure invocation time, not definition time.
|
|
13
|
+
*
|
|
14
|
+
* Detection:
|
|
15
|
+
* - Zero-parameter closures (|| { }) used outside dict context
|
|
16
|
+
* - Body contains bare $ references (VariableNode with name '$')
|
|
17
|
+
*
|
|
18
|
+
* Valid patterns:
|
|
19
|
+
* - Dict closures: [count: ||{ $.items -> .len }] ($ binds to dict)
|
|
20
|
+
* - Parameterized closures: |x|{ $x } (explicit params)
|
|
21
|
+
* - Inline blocks: -> { $ * 2 } (immediate evaluation)
|
|
22
|
+
*
|
|
23
|
+
* References:
|
|
24
|
+
* - docs/16_conventions.md:251-261
|
|
25
|
+
* - docs/06_closures.md: Late binding section
|
|
26
|
+
*/
|
|
27
|
+
export const CLOSURE_BARE_DOLLAR = {
|
|
28
|
+
code: 'CLOSURE_BARE_DOLLAR',
|
|
29
|
+
category: 'closures',
|
|
30
|
+
severity: 'warning',
|
|
31
|
+
nodeTypes: ['Closure'],
|
|
32
|
+
validate(node, context) {
|
|
33
|
+
const closureNode = node;
|
|
34
|
+
// Only check zero-parameter closures (|| { })
|
|
35
|
+
if (closureNode.params.length > 0) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
// Check if closure body contains bare $ references
|
|
39
|
+
const hasBareReference = containsBareReference(closureNode.body);
|
|
40
|
+
if (hasBareReference) {
|
|
41
|
+
return [
|
|
42
|
+
{
|
|
43
|
+
location: closureNode.span.start,
|
|
44
|
+
severity: 'warning',
|
|
45
|
+
code: 'CLOSURE_BARE_DOLLAR',
|
|
46
|
+
message: 'Bare $ in stored closure has ambiguous binding. Use explicit capture: $ :> $item',
|
|
47
|
+
context: extractContextLine(closureNode.span.start.line, context.source),
|
|
48
|
+
fix: null, // Cannot auto-fix safely - requires context understanding
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
return [];
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Check if a node tree contains bare $ variable references.
|
|
57
|
+
* Recursively walks the AST looking for VariableNode with isPipeVar=true.
|
|
58
|
+
*/
|
|
59
|
+
function containsBareReference(node) {
|
|
60
|
+
if (node.type === 'Variable') {
|
|
61
|
+
const varNode = node;
|
|
62
|
+
// $ is represented as isPipeVar: true with name: null
|
|
63
|
+
if (varNode.isPipeVar) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Recursively check child nodes based on node type
|
|
68
|
+
switch (node.type) {
|
|
69
|
+
case 'Block': {
|
|
70
|
+
const blockNode = node;
|
|
71
|
+
for (const stmt of blockNode.statements) {
|
|
72
|
+
if (containsBareReference(stmt))
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case 'Statement': {
|
|
78
|
+
const stmtNode = node;
|
|
79
|
+
if (stmtNode.expression && containsBareReference(stmtNode.expression))
|
|
80
|
+
return true;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case 'PipeChain': {
|
|
84
|
+
const pipeNode = node;
|
|
85
|
+
if (pipeNode.head && containsBareReference(pipeNode.head))
|
|
86
|
+
return true;
|
|
87
|
+
if (pipeNode.pipes) {
|
|
88
|
+
for (const pipe of pipeNode.pipes) {
|
|
89
|
+
if (containsBareReference(pipe))
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
case 'PostfixExpr': {
|
|
96
|
+
const postfixNode = node;
|
|
97
|
+
if (postfixNode.primary && containsBareReference(postfixNode.primary))
|
|
98
|
+
return true;
|
|
99
|
+
if (postfixNode.methods) {
|
|
100
|
+
for (const method of postfixNode.methods) {
|
|
101
|
+
if (containsBareReference(method))
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
case 'BinaryExpr': {
|
|
108
|
+
const binaryNode = node;
|
|
109
|
+
if (binaryNode.left && containsBareReference(binaryNode.left))
|
|
110
|
+
return true;
|
|
111
|
+
if (binaryNode.right && containsBareReference(binaryNode.right))
|
|
112
|
+
return true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case 'UnaryExpr': {
|
|
116
|
+
const unaryNode = node;
|
|
117
|
+
if (unaryNode.operand && containsBareReference(unaryNode.operand))
|
|
118
|
+
return true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case 'GroupedExpr': {
|
|
122
|
+
const groupedNode = node;
|
|
123
|
+
if (groupedNode.expression &&
|
|
124
|
+
containsBareReference(groupedNode.expression))
|
|
125
|
+
return true;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case 'StringLiteral': {
|
|
129
|
+
const stringNode = node;
|
|
130
|
+
if (stringNode.parts && Array.isArray(stringNode.parts)) {
|
|
131
|
+
for (const part of stringNode.parts) {
|
|
132
|
+
if (typeof part === 'object' && containsBareReference(part))
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case 'Interpolation': {
|
|
139
|
+
const interpNode = node;
|
|
140
|
+
if (interpNode.expression && containsBareReference(interpNode.expression))
|
|
141
|
+
return true;
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
case 'Conditional': {
|
|
145
|
+
const condNode = node;
|
|
146
|
+
if (condNode.condition && containsBareReference(condNode.condition))
|
|
147
|
+
return true;
|
|
148
|
+
if (condNode.thenBranch && containsBareReference(condNode.thenBranch))
|
|
149
|
+
return true;
|
|
150
|
+
if (condNode.elseBranch && containsBareReference(condNode.elseBranch))
|
|
151
|
+
return true;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case 'MethodCall':
|
|
155
|
+
case 'HostCall':
|
|
156
|
+
case 'ClosureCall':
|
|
157
|
+
case 'Invoke': {
|
|
158
|
+
const callNode = node;
|
|
159
|
+
if (callNode.args && Array.isArray(callNode.args)) {
|
|
160
|
+
for (const arg of callNode.args) {
|
|
161
|
+
if (containsBareReference(arg))
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
// ============================================================
|
|
171
|
+
// CLOSURE_BRACES RULE
|
|
172
|
+
// ============================================================
|
|
173
|
+
/**
|
|
174
|
+
* Enforces braces for complex closure bodies.
|
|
175
|
+
* Simple expressions can use parentheses, but complex bodies need braces.
|
|
176
|
+
*
|
|
177
|
+
* Complex body criteria:
|
|
178
|
+
* - Contains Block (multiple statements)
|
|
179
|
+
* - Contains Conditional
|
|
180
|
+
* - Contains loop constructs
|
|
181
|
+
*
|
|
182
|
+
* Simple bodies (parentheses OK):
|
|
183
|
+
* - Single expression: |x|($x * 2)
|
|
184
|
+
* - Single method chain: |s|($s.trim.lower)
|
|
185
|
+
*
|
|
186
|
+
* Complex bodies (braces required):
|
|
187
|
+
* - Conditionals: |n| { ($n < 1) ? 1 ! ($n * $fact($n - 1)) }
|
|
188
|
+
* - Multiple statements: |x| { $x :> $y; $y * 2 }
|
|
189
|
+
*
|
|
190
|
+
* References:
|
|
191
|
+
* - docs/16_conventions.md:239-249
|
|
192
|
+
*/
|
|
193
|
+
export const CLOSURE_BRACES = {
|
|
194
|
+
code: 'CLOSURE_BRACES',
|
|
195
|
+
category: 'closures',
|
|
196
|
+
severity: 'info',
|
|
197
|
+
nodeTypes: ['Closure'],
|
|
198
|
+
validate(node, context) {
|
|
199
|
+
const closureNode = node;
|
|
200
|
+
const body = closureNode.body;
|
|
201
|
+
// Check if body is GroupedExpr containing complex content
|
|
202
|
+
if (body.type === 'GroupedExpr') {
|
|
203
|
+
const grouped = body;
|
|
204
|
+
const innerExpr = grouped.expression;
|
|
205
|
+
// Navigate through PipeChain to find the actual content
|
|
206
|
+
let content = innerExpr;
|
|
207
|
+
if (innerExpr && innerExpr.type === 'PipeChain') {
|
|
208
|
+
const head = innerExpr.head;
|
|
209
|
+
// Check if head is PostfixExpr
|
|
210
|
+
if (head && head.type === 'PostfixExpr') {
|
|
211
|
+
content = head.primary;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
content = head;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Check if the content is a conditional or loop
|
|
218
|
+
const isComplex = content &&
|
|
219
|
+
(content.type === 'Conditional' ||
|
|
220
|
+
content.type === 'WhileLoop' ||
|
|
221
|
+
content.type === 'DoWhileLoop');
|
|
222
|
+
if (isComplex) {
|
|
223
|
+
return [
|
|
224
|
+
{
|
|
225
|
+
location: closureNode.span.start,
|
|
226
|
+
severity: 'info',
|
|
227
|
+
code: 'CLOSURE_BRACES',
|
|
228
|
+
message: 'Use braces for complex closure bodies (conditionals, loops)',
|
|
229
|
+
context: extractContextLine(closureNode.span.start.line, context.source),
|
|
230
|
+
fix: null, // Auto-fix would require AST reconstruction
|
|
231
|
+
},
|
|
232
|
+
];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return [];
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
// ============================================================
|
|
239
|
+
// CLOSURE_LATE_BINDING RULE
|
|
240
|
+
// ============================================================
|
|
241
|
+
/**
|
|
242
|
+
* Detects closures created in loops that may suffer from late binding issues.
|
|
243
|
+
* When creating closures inside loops, variables are captured by reference,
|
|
244
|
+
* not by value. This causes all closures to share the final loop value.
|
|
245
|
+
*
|
|
246
|
+
* Detection:
|
|
247
|
+
* - Each loop body creates a Closure node
|
|
248
|
+
* - Closure references loop variable ($) without explicit capture
|
|
249
|
+
*
|
|
250
|
+
* Solution: Explicit capture per iteration:
|
|
251
|
+
* [1, 2, 3] -> each {
|
|
252
|
+
* $ :> $item
|
|
253
|
+
* || { $item }
|
|
254
|
+
* }
|
|
255
|
+
*
|
|
256
|
+
* References:
|
|
257
|
+
* - docs/16_conventions.md:251-261
|
|
258
|
+
* - docs/06_closures.md: Late binding section
|
|
259
|
+
*/
|
|
260
|
+
export const CLOSURE_LATE_BINDING = {
|
|
261
|
+
code: 'CLOSURE_LATE_BINDING',
|
|
262
|
+
category: 'closures',
|
|
263
|
+
severity: 'warning',
|
|
264
|
+
nodeTypes: ['EachExpr'],
|
|
265
|
+
validate(node, context) {
|
|
266
|
+
const eachNode = node;
|
|
267
|
+
const body = eachNode.body;
|
|
268
|
+
// Check if body contains a closure creation
|
|
269
|
+
const hasClosureCreation = containsClosureCreation(body);
|
|
270
|
+
if (hasClosureCreation) {
|
|
271
|
+
// Check if there's an explicit capture before the closure
|
|
272
|
+
const hasExplicitCapture = containsExplicitCapture(body);
|
|
273
|
+
if (!hasExplicitCapture) {
|
|
274
|
+
return [
|
|
275
|
+
{
|
|
276
|
+
location: eachNode.span.start,
|
|
277
|
+
severity: 'warning',
|
|
278
|
+
code: 'CLOSURE_LATE_BINDING',
|
|
279
|
+
message: 'Capture loop variable explicitly for deferred closures: $ :> $item',
|
|
280
|
+
context: extractContextLine(eachNode.span.start.line, context.source),
|
|
281
|
+
fix: null, // Auto-fix would require AST reconstruction
|
|
282
|
+
},
|
|
283
|
+
];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return [];
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* Check if a node contains a closure creation (Closure node).
|
|
291
|
+
*/
|
|
292
|
+
function containsClosureCreation(node) {
|
|
293
|
+
if (node.type === 'Closure') {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
// Recursively check child nodes
|
|
297
|
+
switch (node.type) {
|
|
298
|
+
case 'Block': {
|
|
299
|
+
const blockNode = node;
|
|
300
|
+
for (const stmt of blockNode.statements) {
|
|
301
|
+
if (containsClosureCreation(stmt))
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
case 'Statement': {
|
|
307
|
+
const stmtNode = node;
|
|
308
|
+
if (stmtNode.expression && containsClosureCreation(stmtNode.expression))
|
|
309
|
+
return true;
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
case 'PipeChain': {
|
|
313
|
+
const pipeNode = node;
|
|
314
|
+
if (pipeNode.head && containsClosureCreation(pipeNode.head))
|
|
315
|
+
return true;
|
|
316
|
+
if (pipeNode.pipes) {
|
|
317
|
+
for (const pipe of pipeNode.pipes) {
|
|
318
|
+
if (containsClosureCreation(pipe))
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case 'PostfixExpr': {
|
|
325
|
+
const postfixNode = node;
|
|
326
|
+
if (postfixNode.primary && containsClosureCreation(postfixNode.primary))
|
|
327
|
+
return true;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Check if a Block node contains an explicit capture statement ($ :> $name).
|
|
335
|
+
*/
|
|
336
|
+
function containsExplicitCapture(node) {
|
|
337
|
+
if (node.type !== 'Block') {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
const blockNode = node;
|
|
341
|
+
const statements = blockNode.statements;
|
|
342
|
+
if (!Array.isArray(statements)) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
// Look for capture of $ into a named variable
|
|
346
|
+
for (const stmt of statements) {
|
|
347
|
+
if (stmt.type === 'Statement' &&
|
|
348
|
+
stmt.expression &&
|
|
349
|
+
stmt.expression.type === 'PipeChain') {
|
|
350
|
+
const chain = stmt.expression;
|
|
351
|
+
// Check if any pipe is a Capture
|
|
352
|
+
if (chain.pipes && Array.isArray(chain.pipes)) {
|
|
353
|
+
for (const pipe of chain.pipes) {
|
|
354
|
+
if (pipe.type === 'Capture') {
|
|
355
|
+
// Check if the head is bare $
|
|
356
|
+
const head = chain.head;
|
|
357
|
+
if (head && head.type === 'PostfixExpr') {
|
|
358
|
+
const postfix = head;
|
|
359
|
+
if (postfix.primary && postfix.primary.type === 'Variable') {
|
|
360
|
+
const varNode = postfix.primary;
|
|
361
|
+
if (varNode.isPipeVar) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
//# sourceMappingURL=closures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"closures.js","sourceRoot":"","sources":["../../../src/check/rules/closures.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,+DAA+D;AAC/D,2BAA2B;AAC3B,+DAA+D;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAmB;IACjD,IAAI,EAAE,qBAAqB;IAC3B,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE,CAAC,SAAS,CAAC;IAEtB,QAAQ,CAAC,IAAa,EAAE,OAA0B;QAChD,MAAM,WAAW,GAAG,IAAmB,CAAC;QAExC,8CAA8C;QAC9C,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,mDAAmD;QACnD,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAEjE,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO;gBACL;oBACE,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,KAAK;oBAChC,QAAQ,EAAE,SAAS;oBACnB,IAAI,EAAE,qBAAqB;oBAC3B,OAAO,EACL,kFAAkF;oBACpF,OAAO,EAAE,kBAAkB,CACzB,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAC3B,OAAO,CAAC,MAAM,CACf;oBACD,GAAG,EAAE,IAAI,EAAE,0DAA0D;iBACtE;aACF,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC;AAEF;;;GAGG;AACH,SAAS,qBAAqB,CAAC,IAAa;IAC1C,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAoB,CAAC;QACrC,sDAAsD;QACtD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,mDAAmD;IACnD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,SAAS,GAAG,IAAW,CAAC;YAC9B,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;gBACxC,IAAI,qBAAqB,CAAC,IAAI,CAAC;oBAAE,OAAO,IAAI,CAAC;YAC/C,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,QAAQ,GAAG,IAAW,CAAC;YAC7B,IAAI,QAAQ,CAAC,UAAU,IAAI,qBAAqB,CAAC,QAAQ,CAAC,UAAU,CAAC;gBACnE,OAAO,IAAI,CAAC;YACd,MAAM;QACR,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,QAAQ,GAAG,IAAW,CAAC;YAC7B,IAAI,QAAQ,CAAC,IAAI,IAAI,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YACvE,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACnB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;oBAClC,IAAI,qBAAqB,CAAC,IAAI,CAAC;wBAAE,OAAO,IAAI,CAAC;gBAC/C,CAAC;YACH,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,WAAW,GAAG,IAAW,CAAC;YAChC,IAAI,WAAW,CAAC,OAAO,IAAI,qBAAqB,CAAC,WAAW,CAAC,OAAO,CAAC;gBACnE,OAAO,IAAI,CAAC;YACd,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;gBACxB,KAAK,MAAM,MAAM,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;oBACzC,IAAI,qBAAqB,CAAC,MAAM,CAAC;wBAAE,OAAO,IAAI,CAAC;gBACjD,CAAC;YACH,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,UAAU,GAAG,IAAW,CAAC;YAC/B,IAAI,UAAU,CAAC,IAAI,IAAI,qBAAqB,CAAC,UAAU,CAAC,IAAI,CAAC;gBAC3D,OAAO,IAAI,CAAC;YACd,IAAI,UAAU,CAAC,KAAK,IAAI,qBAAqB,CAAC,UAAU,CAAC,KAAK,CAAC;gBAC7D,OAAO,IAAI,CAAC;YACd,MAAM;QACR,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,SAAS,GAAG,IAAW,CAAC;YAC9B,IAAI,SAAS,CAAC,OAAO,IAAI,qBAAqB,CAAC,SAAS,CAAC,OAAO,CAAC;gBAC/D,OAAO,IAAI,CAAC;YACd,MAAM;QACR,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,WAAW,GAAG,IAAW,CAAC;YAChC,IACE,WAAW,CAAC,UAAU;gBACtB,qBAAqB,CAAC,WAAW,CAAC,UAAU,CAAC;gBAE7C,OAAO,IAAI,CAAC;YACd,MAAM;QACR,CAAC;QAED,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,UAAU,GAAG,IAAW,CAAC;YAC/B,IAAI,UAAU,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxD,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;oBACpC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,qBAAqB,CAAC,IAAI,CAAC;wBACzD,OAAO,IAAI,CAAC;gBAChB,CAAC;YACH,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,UAAU,GAAG,IAAW,CAAC;YAC/B,IAAI,UAAU,CAAC,UAAU,IAAI,qBAAqB,CAAC,UAAU,CAAC,UAAU,CAAC;gBACvE,OAAO,IAAI,CAAC;YACd,MAAM;QACR,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,QAAQ,GAAG,IAAW,CAAC;YAC7B,IAAI,QAAQ,CAAC,SAAS,IAAI,qBAAqB,CAAC,QAAQ,CAAC,SAAS,CAAC;gBACjE,OAAO,IAAI,CAAC;YACd,IAAI,QAAQ,CAAC,UAAU,IAAI,qBAAqB,CAAC,QAAQ,CAAC,UAAU,CAAC;gBACnE,OAAO,IAAI,CAAC;YACd,IAAI,QAAQ,CAAC,UAAU,IAAI,qBAAqB,CAAC,QAAQ,CAAC,UAAU,CAAC;gBACnE,OAAO,IAAI,CAAC;YACd,MAAM;QACR,CAAC;QAED,KAAK,YAAY,CAAC;QAClB,KAAK,UAAU,CAAC;QAChB,KAAK,aAAa,CAAC;QACnB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,QAAQ,GAAG,IAAW,CAAC;YAC7B,IAAI,QAAQ,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClD,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;oBAChC,IAAI,qBAAqB,CAAC,GAAG,CAAC;wBAAE,OAAO,IAAI,CAAC;gBAC9C,CAAC;YACH,CAAC;YACD,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+DAA+D;AAC/D,sBAAsB;AACtB,+DAA+D;AAE/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,IAAI,EAAE,gBAAgB;IACtB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,CAAC,SAAS,CAAC;IAEtB,QAAQ,CAAC,IAAa,EAAE,OAA0B;QAChD,MAAM,WAAW,GAAG,IAAmB,CAAC;QACxC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC;QAE9B,0DAA0D;QAC1D,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,IAAW,CAAC;YAC5B,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC;YAErC,wDAAwD;YACxD,IAAI,OAAO,GAAG,SAAS,CAAC;YACxB,IAAI,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAChD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;gBAC5B,+BAA+B;gBAC/B,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;oBACxC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;gBACzB,CAAC;qBAAM,CAAC;oBACN,OAAO,GAAG,IAAI,CAAC;gBACjB,CAAC;YACH,CAAC;YAED,gDAAgD;YAChD,MAAM,SAAS,GACb,OAAO;gBACP,CAAC,OAAO,CAAC,IAAI,KAAK,aAAa;oBAC7B,OAAO,CAAC,IAAI,KAAK,WAAW;oBAC5B,OAAO,CAAC,IAAI,KAAK,aAAa,CAAC,CAAC;YAEpC,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO;oBACL;wBACE,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,KAAK;wBAChC,QAAQ,EAAE,MAAM;wBAChB,IAAI,EAAE,gBAAgB;wBACtB,OAAO,EACL,6DAA6D;wBAC/D,OAAO,EAAE,kBAAkB,CACzB,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAC3B,OAAO,CAAC,MAAM,CACf;wBACD,GAAG,EAAE,IAAI,EAAE,4CAA4C;qBACxD;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC;AAEF,+DAA+D;AAC/D,4BAA4B;AAC5B,+DAA+D;AAE/D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAmB;IAClD,IAAI,EAAE,sBAAsB;IAC5B,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE,CAAC,UAAU,CAAC;IAEvB,QAAQ,CAAC,IAAa,EAAE,OAA0B;QAChD,MAAM,QAAQ,GAAG,IAAoB,CAAC;QACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;QAE3B,4CAA4C;QAC5C,MAAM,kBAAkB,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC;QAEzD,IAAI,kBAAkB,EAAE,CAAC;YACvB,0DAA0D;YAC1D,MAAM,kBAAkB,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAC;YAEzD,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACxB,OAAO;oBACL;wBACE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK;wBAC7B,QAAQ,EAAE,SAAS;wBACnB,IAAI,EAAE,sBAAsB;wBAC5B,OAAO,EACL,oEAAoE;wBACtE,OAAO,EAAE,kBAAkB,CACzB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EACxB,OAAO,CAAC,MAAM,CACf;wBACD,GAAG,EAAE,IAAI,EAAE,4CAA4C;qBACxD;iBACF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,SAAS,uBAAuB,CAAC,IAAa;IAC5C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gCAAgC;IAChC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,SAAS,GAAG,IAAW,CAAC;YAC9B,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,UAAU,EAAE,CAAC;gBACxC,IAAI,uBAAuB,CAAC,IAAI,CAAC;oBAAE,OAAO,IAAI,CAAC;YACjD,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,QAAQ,GAAG,IAAW,CAAC;YAC7B,IAAI,QAAQ,CAAC,UAAU,IAAI,uBAAuB,CAAC,QAAQ,CAAC,UAAU,CAAC;gBACrE,OAAO,IAAI,CAAC;YACd,MAAM;QACR,CAAC;QAED,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,QAAQ,GAAG,IAAW,CAAC;YAC7B,IAAI,QAAQ,CAAC,IAAI,IAAI,uBAAuB,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YACzE,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACnB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;oBAClC,IAAI,uBAAuB,CAAC,IAAI,CAAC;wBAAE,OAAO,IAAI,CAAC;gBACjD,CAAC;YACH,CAAC;YACD,MAAM;QACR,CAAC;QAED,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,WAAW,GAAG,IAAW,CAAC;YAChC,IAAI,WAAW,CAAC,OAAO,IAAI,uBAAuB,CAAC,WAAW,CAAC,OAAO,CAAC;gBACrE,OAAO,IAAI,CAAC;YACd,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,IAAa;IAC5C,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,IAAW,CAAC;IAC9B,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC;IAExC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,8CAA8C;IAC9C,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IACE,IAAI,CAAC,IAAI,KAAK,WAAW;YACzB,IAAI,CAAC,UAAU;YACf,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,WAAW,EACpC,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC;YAE9B,iCAAiC;YACjC,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;oBAC/B,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;wBAC5B,8BAA8B;wBAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;wBACxB,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;4BACxC,MAAM,OAAO,GAAG,IAAW,CAAC;4BAC5B,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gCAC3D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAuB,CAAC;gCAChD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;oCACtB,OAAO,IAAI,CAAC;gCACd,CAAC;4BACH,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collection Operator Rules
|
|
3
|
+
* Enforces conventions for each, map, fold, and filter operators.
|
|
4
|
+
*/
|
|
5
|
+
import type { ValidationRule } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Validates that break is not used in parallel operators (map, filter).
|
|
8
|
+
*
|
|
9
|
+
* Break is semantically invalid in parallel execution contexts:
|
|
10
|
+
* - map: executes in parallel, no iteration order
|
|
11
|
+
* - filter: parallel predicate evaluation
|
|
12
|
+
*
|
|
13
|
+
* Break is valid in sequential operators:
|
|
14
|
+
* - each: sequential iteration with early termination
|
|
15
|
+
* - fold: sequential reduction (though uncommon)
|
|
16
|
+
*
|
|
17
|
+
* Error severity because this is semantically wrong, not just stylistic.
|
|
18
|
+
*
|
|
19
|
+
* References:
|
|
20
|
+
* - docs/16_conventions.md:90-149
|
|
21
|
+
* - docs/07_collections.md
|
|
22
|
+
*/
|
|
23
|
+
export declare const BREAK_IN_PARALLEL: ValidationRule;
|
|
24
|
+
/**
|
|
25
|
+
* Suggests using map over each when no side effects are present.
|
|
26
|
+
*
|
|
27
|
+
* Map is semantically clearer for pure transformations:
|
|
28
|
+
* - Signals no side effects (parallel execution)
|
|
29
|
+
* - Better performance potential
|
|
30
|
+
* - More functional style
|
|
31
|
+
*
|
|
32
|
+
* Detects each expressions where:
|
|
33
|
+
* - Body doesn't reference accumulator ($@)
|
|
34
|
+
* - No accumulator initialization
|
|
35
|
+
* - Body doesn't contain side-effecting operations (host calls, logging)
|
|
36
|
+
*
|
|
37
|
+
* This is informational - both work, but map is clearer for pure transforms.
|
|
38
|
+
*
|
|
39
|
+
* References:
|
|
40
|
+
* - docs/16_conventions.md:90-149
|
|
41
|
+
*/
|
|
42
|
+
export declare const PREFER_MAP: ValidationRule;
|
|
43
|
+
/**
|
|
44
|
+
* Suggests using fold for final-only results, each(init) for running totals.
|
|
45
|
+
*
|
|
46
|
+
* Semantic distinction:
|
|
47
|
+
* - fold: returns final accumulated value only
|
|
48
|
+
* - each(init): returns list of all intermediate results
|
|
49
|
+
*
|
|
50
|
+
* Detects patterns that might benefit from one or the other:
|
|
51
|
+
* - fold used when intermediate results might be needed
|
|
52
|
+
* - each(init) used when only final result matters
|
|
53
|
+
*
|
|
54
|
+
* This is informational - helps users choose the right operator.
|
|
55
|
+
*
|
|
56
|
+
* References:
|
|
57
|
+
* - docs/16_conventions.md:90-149
|
|
58
|
+
* - docs/07_collections.md
|
|
59
|
+
*/
|
|
60
|
+
export declare const FOLD_INTERMEDIATES: ValidationRule;
|
|
61
|
+
/**
|
|
62
|
+
* Validates that negation in filter uses grouped form.
|
|
63
|
+
*
|
|
64
|
+
* Grouped negation is clearer and prevents bugs:
|
|
65
|
+
* - Correct: filter (!.empty) -- grouped negation
|
|
66
|
+
* - Wrong: filter .empty -- filters for empty elements (likely bug)
|
|
67
|
+
*
|
|
68
|
+
* The ungrouped form .empty would return truthy elements,
|
|
69
|
+
* which is likely not intended when filtering.
|
|
70
|
+
*
|
|
71
|
+
* References:
|
|
72
|
+
* - docs/16_conventions.md:90-149
|
|
73
|
+
*/
|
|
74
|
+
export declare const FILTER_NEGATION: ValidationRule;
|
|
75
|
+
/**
|
|
76
|
+
* Suggests using method shorthand over block form in collection operators.
|
|
77
|
+
*
|
|
78
|
+
* Method shorthand is more concise and clearer:
|
|
79
|
+
* - Preferred: map .upper
|
|
80
|
+
* - Verbose: map { $.upper() }
|
|
81
|
+
*
|
|
82
|
+
* Detects block forms that wrap a single method call and suggests shorthand.
|
|
83
|
+
*
|
|
84
|
+
* This is informational - both forms work identically.
|
|
85
|
+
*
|
|
86
|
+
* References:
|
|
87
|
+
* - docs/16_conventions.md:90-149
|
|
88
|
+
*/
|
|
89
|
+
export declare const METHOD_SHORTHAND: ValidationRule;
|
|
90
|
+
//# sourceMappingURL=collections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections.d.ts","sourceRoot":"","sources":["../../../src/check/rules/collections.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,aAAa,CAAC;AAsLrB;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAyB/B,CAAC;AAMF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,EAAE,cAuCxB,CAAC;AAMF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,kBAAkB,EAAE,cAYhC,CAAC;AAMF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,EAAE,cAiC7B,CAAC;AAMF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAiC9B,CAAC"}
|