@rcrsr/rill-cli 0.6.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/LICENSE +21 -0
- 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 +481 -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 +370 -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 +134 -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 +143 -0
- package/dist/check/rules/formatting.d.ts.map +1 -0
- package/dist/check/rules/formatting.js +656 -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 +77 -0
- package/dist/check/rules/loops.d.ts.map +1 -0
- package/dist/check/rules/loops.js +310 -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 +174 -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 +79 -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 +167 -0
- package/dist/check/rules/types.js.map +1 -0
- package/dist/check/types.d.ts +112 -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 +110 -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 +259 -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 +366 -0
- package/dist/cli-check.js.map +1 -0
- package/dist/cli-error-enrichment.d.ts +73 -0
- package/dist/cli-error-enrichment.d.ts.map +1 -0
- package/dist/cli-error-enrichment.js +205 -0
- package/dist/cli-error-enrichment.js.map +1 -0
- package/dist/cli-error-formatter.d.ts +45 -0
- package/dist/cli-error-formatter.d.ts.map +1 -0
- package/dist/cli-error-formatter.js +218 -0
- package/dist/cli-error-formatter.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 +116 -0
- package/dist/cli-eval.js.map +1 -0
- package/dist/cli-exec.d.ts +58 -0
- package/dist/cli-exec.d.ts.map +1 -0
- package/dist/cli-exec.js +326 -0
- package/dist/cli-exec.js.map +1 -0
- package/dist/cli-explain.d.ts +24 -0
- package/dist/cli-explain.d.ts.map +1 -0
- package/dist/cli-explain.js +68 -0
- package/dist/cli-explain.js.map +1 -0
- package/dist/cli-lsp-diagnostic.d.ts +35 -0
- package/dist/cli-lsp-diagnostic.d.ts.map +1 -0
- package/dist/cli-lsp-diagnostic.js +98 -0
- package/dist/cli-lsp-diagnostic.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 +62 -0
- package/dist/cli-shared.d.ts.map +1 -0
- package/dist/cli-shared.js +158 -0
- package/dist/cli-shared.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +62 -0
- package/dist/cli.js.map +1 -0
- package/dist/test-internal-import.d.ts +2 -0
- package/dist/test-internal-import.d.ts.map +1 -0
- package/dist/test-internal-import.js +7 -0
- package/dist/test-internal-import.js.map +1 -0
- package/package.json +24 -0
- package/src/check/config.ts +202 -0
- package/src/check/fixer.ts +174 -0
- package/src/check/index.ts +39 -0
- package/src/check/rules/anti-patterns.ts +585 -0
- package/src/check/rules/closures.ts +445 -0
- package/src/check/rules/collections.ts +437 -0
- package/src/check/rules/conditionals.ts +155 -0
- package/src/check/rules/flow.ts +262 -0
- package/src/check/rules/formatting.ts +811 -0
- package/src/check/rules/helpers.ts +89 -0
- package/src/check/rules/index.ts +140 -0
- package/src/check/rules/loops.ts +372 -0
- package/src/check/rules/naming.ts +242 -0
- package/src/check/rules/strings.ts +104 -0
- package/src/check/rules/types.ts +214 -0
- package/src/check/types.ts +163 -0
- package/src/check/validator.ts +136 -0
- package/src/check/visitor.ts +338 -0
- package/src/cli-check.ts +456 -0
- package/src/cli-error-enrichment.ts +274 -0
- package/src/cli-error-formatter.ts +313 -0
- package/src/cli-eval.ts +145 -0
- package/src/cli-exec.ts +408 -0
- package/src/cli-explain.ts +76 -0
- package/src/cli-lsp-diagnostic.ts +132 -0
- package/src/cli-module-loader.ts +101 -0
- package/src/cli-shared.ts +187 -0
- package/tests/check/cli-check.test.ts +189 -0
- package/tests/check/config.test.ts +350 -0
- package/tests/check/fixer.test.ts +373 -0
- package/tests/check/format-diagnostics.test.ts +327 -0
- package/tests/check/rules/anti-patterns.test.ts +467 -0
- package/tests/check/rules/closures.test.ts +192 -0
- package/tests/check/rules/collections.test.ts +380 -0
- package/tests/check/rules/conditionals.test.ts +185 -0
- package/tests/check/rules/flow.test.ts +250 -0
- package/tests/check/rules/formatting.test.ts +755 -0
- package/tests/check/rules/loops.test.ts +334 -0
- package/tests/check/rules/naming.test.ts +336 -0
- package/tests/check/rules/strings.test.ts +129 -0
- package/tests/check/rules/types.test.ts +257 -0
- package/tests/check/validator.test.ts +444 -0
- package/tests/check/visitor.test.ts +171 -0
- package/tests/cli/check.test.ts +801 -0
- package/tests/cli/error-enrichment.test.ts +510 -0
- package/tests/cli/error-formatter.test.ts +631 -0
- package/tests/cli/eval.test.ts +85 -0
- package/tests/cli/exec.test.ts +537 -0
- package/tests/cli-explain.test.ts +249 -0
- package/tests/cli-lsp-diagnostic.test.ts +202 -0
- package/tests/cli-shared.test.ts +439 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Helper Functions
|
|
3
|
+
* Common utilities used across validation rules.
|
|
4
|
+
*/
|
|
5
|
+
import type { ExpressionNode } from '@rcrsr/rill';
|
|
6
|
+
/**
|
|
7
|
+
* Extract source line at location for context display.
|
|
8
|
+
* Splits source by newlines, retrieves the specified line (1-indexed), and trims it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function extractContextLine(line: number, source: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Detect if expression is a bare $ (pipe variable) reference.
|
|
13
|
+
* Used by IMPLICIT_DOLLAR_* rules to detect replaceable patterns.
|
|
14
|
+
*
|
|
15
|
+
* Returns true only for single bare $, not $var or $.field or $[0].
|
|
16
|
+
* O(1) depth traversal (max 3 node levels): PipeChain -> ArithHead -> PostfixExpr -> Variable.
|
|
17
|
+
*
|
|
18
|
+
* Distinct from containsBareReference() in closures.ts:
|
|
19
|
+
* - isBareReference(): O(1) single-node check, answers "is this exact node a bare $?"
|
|
20
|
+
* - containsBareReference(): Recursive AST walker, answers "does this subtree contain any bare $?"
|
|
21
|
+
*
|
|
22
|
+
* @param expr - Expression node to check
|
|
23
|
+
* @returns true if expr is a bare $ reference, false otherwise
|
|
24
|
+
*/
|
|
25
|
+
export declare function isBareReference(expr: ExpressionNode | null | undefined): boolean;
|
|
26
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/check/rules/helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,aAAa,CAAC;AAErB;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAIvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GACtC,OAAO,CAkDT"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Helper Functions
|
|
3
|
+
* Common utilities used across validation rules.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Extract source line at location for context display.
|
|
7
|
+
* Splits source by newlines, retrieves the specified line (1-indexed), and trims it.
|
|
8
|
+
*/
|
|
9
|
+
export function extractContextLine(line, source) {
|
|
10
|
+
const lines = source.split('\n');
|
|
11
|
+
const sourceLine = lines[line - 1];
|
|
12
|
+
return sourceLine ? sourceLine.trim() : '';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Detect if expression is a bare $ (pipe variable) reference.
|
|
16
|
+
* Used by IMPLICIT_DOLLAR_* rules to detect replaceable patterns.
|
|
17
|
+
*
|
|
18
|
+
* Returns true only for single bare $, not $var or $.field or $[0].
|
|
19
|
+
* O(1) depth traversal (max 3 node levels): PipeChain -> ArithHead -> PostfixExpr -> Variable.
|
|
20
|
+
*
|
|
21
|
+
* Distinct from containsBareReference() in closures.ts:
|
|
22
|
+
* - isBareReference(): O(1) single-node check, answers "is this exact node a bare $?"
|
|
23
|
+
* - containsBareReference(): Recursive AST walker, answers "does this subtree contain any bare $?"
|
|
24
|
+
*
|
|
25
|
+
* @param expr - Expression node to check
|
|
26
|
+
* @returns true if expr is a bare $ reference, false otherwise
|
|
27
|
+
*/
|
|
28
|
+
export function isBareReference(expr) {
|
|
29
|
+
// Defensive: handle null/undefined input
|
|
30
|
+
if (!expr) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
// Expression is PipeChain
|
|
34
|
+
if (expr.type !== 'PipeChain') {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const pipeChain = expr;
|
|
38
|
+
// Must have no pipe targets (just the head)
|
|
39
|
+
if (pipeChain.pipes.length > 0 || pipeChain.terminator !== null) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const head = pipeChain.head;
|
|
43
|
+
// ArithHead can be BinaryExpr, UnaryExpr, or PostfixExpr
|
|
44
|
+
// For bare $, we need PostfixExpr
|
|
45
|
+
if (head.type !== 'PostfixExpr') {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const postfix = head;
|
|
49
|
+
// Must have no method calls (just the primary)
|
|
50
|
+
if (postfix.methods.length > 0) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
const primary = postfix.primary;
|
|
54
|
+
// Primary must be a Variable
|
|
55
|
+
if (primary.type !== 'Variable') {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const variable = primary;
|
|
59
|
+
// Must be pipe variable ($) with no access chain, default value, or existence check
|
|
60
|
+
return (variable.isPipeVar &&
|
|
61
|
+
variable.name === null &&
|
|
62
|
+
variable.accessChain.length === 0 &&
|
|
63
|
+
variable.defaultValue === null &&
|
|
64
|
+
variable.existenceCheck === null);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/check/rules/helpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,MAAc;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;IACnC,OAAO,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAuC;IAEvC,yCAAyC;IACzC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,KAAK,CAAC;IACf,CAAC;IAED,0BAA0B;IAC1B,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,IAAqB,CAAC;IAExC,4CAA4C;IAC5C,IAAI,SAAS,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,SAAS,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QAChE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC;IAE5B,yDAAyD;IACzD,kCAAkC;IAClC,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,IAAuB,CAAC;IAExC,+CAA+C;IAC/C,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAEhC,6BAA6B;IAC7B,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,OAAuB,CAAC;IAEzC,oFAAoF;IACpF,OAAO,CACL,QAAQ,CAAC,SAAS;QAClB,QAAQ,CAAC,IAAI,KAAK,IAAI;QACtB,QAAQ,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;QACjC,QAAQ,CAAC,YAAY,KAAK,IAAI;QAC9B,QAAQ,CAAC,cAAc,KAAK,IAAI,CACjC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Rules Registry
|
|
3
|
+
* Barrel export for all validation rules.
|
|
4
|
+
*/
|
|
5
|
+
import type { ValidationRule } from '../types.js';
|
|
6
|
+
export { NAMING_SNAKE_CASE } from './naming.js';
|
|
7
|
+
export { CAPTURE_INLINE_CHAIN, CAPTURE_BEFORE_BRANCH } from './flow.js';
|
|
8
|
+
export { BREAK_IN_PARALLEL, PREFER_MAP, FOLD_INTERMEDIATES, FILTER_NEGATION, METHOD_SHORTHAND, } from './collections.js';
|
|
9
|
+
export { LOOP_ACCUMULATOR, PREFER_DO_WHILE, USE_EACH } from './loops.js';
|
|
10
|
+
export { USE_DEFAULT_OPERATOR, CONDITION_TYPE } from './conditionals.js';
|
|
11
|
+
export { CLOSURE_BARE_DOLLAR, CLOSURE_BRACES, CLOSURE_LATE_BINDING, } from './closures.js';
|
|
12
|
+
export { UNNECESSARY_ASSERTION, VALIDATE_EXTERNAL } from './types.js';
|
|
13
|
+
export { USE_EMPTY_METHOD } from './strings.js';
|
|
14
|
+
export { AVOID_REASSIGNMENT, COMPLEX_CONDITION, LOOP_OUTER_CAPTURE, } from './anti-patterns.js';
|
|
15
|
+
export { SPACING_OPERATOR, SPACING_BRACES, SPACING_BRACKETS, SPACING_CLOSURE, INDENT_CONTINUATION, IMPLICIT_DOLLAR_METHOD, IMPLICIT_DOLLAR_FUNCTION, IMPLICIT_DOLLAR_CLOSURE, THROWAWAY_CAPTURE, } from './formatting.js';
|
|
16
|
+
/**
|
|
17
|
+
* All registered validation rules.
|
|
18
|
+
* Rules are applied during AST traversal via the validator.
|
|
19
|
+
*/
|
|
20
|
+
export declare const VALIDATION_RULES: ValidationRule[];
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/check/rules/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAwClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAMzB;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAmD5C,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validation Rules Registry
|
|
3
|
+
* Barrel export for all validation rules.
|
|
4
|
+
*/
|
|
5
|
+
import { NAMING_SNAKE_CASE } from './naming.js';
|
|
6
|
+
import { CAPTURE_INLINE_CHAIN, CAPTURE_BEFORE_BRANCH } from './flow.js';
|
|
7
|
+
import { BREAK_IN_PARALLEL, PREFER_MAP, FOLD_INTERMEDIATES, FILTER_NEGATION, METHOD_SHORTHAND, } from './collections.js';
|
|
8
|
+
import { LOOP_ACCUMULATOR, PREFER_DO_WHILE, USE_EACH } from './loops.js';
|
|
9
|
+
import { USE_DEFAULT_OPERATOR, CONDITION_TYPE } from './conditionals.js';
|
|
10
|
+
import { CLOSURE_BARE_DOLLAR, CLOSURE_BRACES, CLOSURE_LATE_BINDING, } from './closures.js';
|
|
11
|
+
import { UNNECESSARY_ASSERTION, VALIDATE_EXTERNAL } from './types.js';
|
|
12
|
+
import { USE_EMPTY_METHOD } from './strings.js';
|
|
13
|
+
import { AVOID_REASSIGNMENT, COMPLEX_CONDITION, LOOP_OUTER_CAPTURE, } from './anti-patterns.js';
|
|
14
|
+
import { SPACING_OPERATOR, SPACING_BRACES, SPACING_BRACKETS, SPACING_CLOSURE, INDENT_CONTINUATION, IMPLICIT_DOLLAR_METHOD, IMPLICIT_DOLLAR_FUNCTION, IMPLICIT_DOLLAR_CLOSURE, THROWAWAY_CAPTURE, } from './formatting.js';
|
|
15
|
+
// ============================================================
|
|
16
|
+
// RE-EXPORT INDIVIDUAL RULES
|
|
17
|
+
// ============================================================
|
|
18
|
+
export { NAMING_SNAKE_CASE } from './naming.js';
|
|
19
|
+
export { CAPTURE_INLINE_CHAIN, CAPTURE_BEFORE_BRANCH } from './flow.js';
|
|
20
|
+
export { BREAK_IN_PARALLEL, PREFER_MAP, FOLD_INTERMEDIATES, FILTER_NEGATION, METHOD_SHORTHAND, } from './collections.js';
|
|
21
|
+
export { LOOP_ACCUMULATOR, PREFER_DO_WHILE, USE_EACH } from './loops.js';
|
|
22
|
+
export { USE_DEFAULT_OPERATOR, CONDITION_TYPE } from './conditionals.js';
|
|
23
|
+
export { CLOSURE_BARE_DOLLAR, CLOSURE_BRACES, CLOSURE_LATE_BINDING, } from './closures.js';
|
|
24
|
+
export { UNNECESSARY_ASSERTION, VALIDATE_EXTERNAL } from './types.js';
|
|
25
|
+
export { USE_EMPTY_METHOD } from './strings.js';
|
|
26
|
+
export { AVOID_REASSIGNMENT, COMPLEX_CONDITION, LOOP_OUTER_CAPTURE, } from './anti-patterns.js';
|
|
27
|
+
export { SPACING_OPERATOR, SPACING_BRACES, SPACING_BRACKETS, SPACING_CLOSURE, INDENT_CONTINUATION, IMPLICIT_DOLLAR_METHOD, IMPLICIT_DOLLAR_FUNCTION, IMPLICIT_DOLLAR_CLOSURE, THROWAWAY_CAPTURE, } from './formatting.js';
|
|
28
|
+
// ============================================================
|
|
29
|
+
// RULE REGISTRY
|
|
30
|
+
// ============================================================
|
|
31
|
+
/**
|
|
32
|
+
* All registered validation rules.
|
|
33
|
+
* Rules are applied during AST traversal via the validator.
|
|
34
|
+
*/
|
|
35
|
+
export const VALIDATION_RULES = [
|
|
36
|
+
// Naming conventions
|
|
37
|
+
NAMING_SNAKE_CASE,
|
|
38
|
+
// Flow and capture
|
|
39
|
+
CAPTURE_INLINE_CHAIN,
|
|
40
|
+
CAPTURE_BEFORE_BRANCH,
|
|
41
|
+
// Collection operators
|
|
42
|
+
BREAK_IN_PARALLEL,
|
|
43
|
+
PREFER_MAP,
|
|
44
|
+
FOLD_INTERMEDIATES,
|
|
45
|
+
FILTER_NEGATION,
|
|
46
|
+
METHOD_SHORTHAND,
|
|
47
|
+
// Loop conventions
|
|
48
|
+
LOOP_ACCUMULATOR,
|
|
49
|
+
PREFER_DO_WHILE,
|
|
50
|
+
USE_EACH,
|
|
51
|
+
// Conditional conventions
|
|
52
|
+
USE_DEFAULT_OPERATOR,
|
|
53
|
+
CONDITION_TYPE,
|
|
54
|
+
// Closure conventions
|
|
55
|
+
CLOSURE_BARE_DOLLAR,
|
|
56
|
+
CLOSURE_BRACES,
|
|
57
|
+
CLOSURE_LATE_BINDING,
|
|
58
|
+
// Type safety
|
|
59
|
+
UNNECESSARY_ASSERTION,
|
|
60
|
+
VALIDATE_EXTERNAL,
|
|
61
|
+
// String handling
|
|
62
|
+
USE_EMPTY_METHOD,
|
|
63
|
+
// Anti-patterns
|
|
64
|
+
AVOID_REASSIGNMENT,
|
|
65
|
+
COMPLEX_CONDITION,
|
|
66
|
+
LOOP_OUTER_CAPTURE,
|
|
67
|
+
// Formatting
|
|
68
|
+
SPACING_OPERATOR,
|
|
69
|
+
SPACING_BRACES,
|
|
70
|
+
SPACING_BRACKETS,
|
|
71
|
+
SPACING_CLOSURE,
|
|
72
|
+
INDENT_CONTINUATION,
|
|
73
|
+
IMPLICIT_DOLLAR_METHOD,
|
|
74
|
+
IMPLICIT_DOLLAR_FUNCTION,
|
|
75
|
+
IMPLICIT_DOLLAR_CLOSURE,
|
|
76
|
+
THROWAWAY_CAPTURE,
|
|
77
|
+
];
|
|
78
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/check/rules/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loop Convention Rules
|
|
3
|
+
* Enforces conventions for while, do-while, and loop control flow.
|
|
4
|
+
*/
|
|
5
|
+
import type { ValidationRule } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Validates that variables captured in loop bodies aren't referenced in conditions.
|
|
8
|
+
*
|
|
9
|
+
* In while and do-while loops, $ serves as the accumulator across iterations.
|
|
10
|
+
* Variables captured inside the loop body exist only within that iteration, so
|
|
11
|
+
* referencing them in the loop condition is a logic error - the condition will
|
|
12
|
+
* always see undefined (or the outer scope variable if one exists).
|
|
13
|
+
*
|
|
14
|
+
* Error pattern (captured variable in condition):
|
|
15
|
+
* 0 -> ($x < 5) @ { # $x is undefined in condition
|
|
16
|
+
* $ => $x
|
|
17
|
+
* $x + 1
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* Correct pattern ($ as accumulator):
|
|
21
|
+
* 0 -> ($ < 5) @ { $ + 1 }
|
|
22
|
+
*
|
|
23
|
+
* Also correct (capture only used within iteration):
|
|
24
|
+
* 0 -> ($ < 5) @ {
|
|
25
|
+
* $ => $x
|
|
26
|
+
* log($x) # $x only used in body, not condition
|
|
27
|
+
* $x + 1
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* References:
|
|
31
|
+
* - docs/guide-conventions.md:151-171
|
|
32
|
+
*/
|
|
33
|
+
export declare const LOOP_ACCUMULATOR: ValidationRule;
|
|
34
|
+
/**
|
|
35
|
+
* Suggests using do-while for retry patterns.
|
|
36
|
+
*
|
|
37
|
+
* Do-while is clearer for retry patterns where the body must run at least once:
|
|
38
|
+
*
|
|
39
|
+
* Good (do-while for retry):
|
|
40
|
+
* @ {
|
|
41
|
+
* attemptOperation()
|
|
42
|
+
* } ? (.contains("RETRY"))
|
|
43
|
+
*
|
|
44
|
+
* Less clear (while with separate first attempt):
|
|
45
|
+
* attemptOperation() => $result
|
|
46
|
+
* $result -> .contains("RETRY") @ {
|
|
47
|
+
* attemptOperation()
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* This is informational - helps guide users to the clearer pattern.
|
|
51
|
+
*
|
|
52
|
+
* References:
|
|
53
|
+
* - docs/guide-conventions.md:173-186
|
|
54
|
+
*/
|
|
55
|
+
export declare const PREFER_DO_WHILE: ValidationRule;
|
|
56
|
+
/**
|
|
57
|
+
* Suggests using each for collection iteration instead of while loops.
|
|
58
|
+
*
|
|
59
|
+
* When iterating over a collection, each is clearer and more idiomatic:
|
|
60
|
+
*
|
|
61
|
+
* Good (each for collection):
|
|
62
|
+
* $items -> each { process($) }
|
|
63
|
+
*
|
|
64
|
+
* Less clear (while loop):
|
|
65
|
+
* 0 => $i
|
|
66
|
+
* ($i < $items.len) @ {
|
|
67
|
+
* $items[$i] -> process()
|
|
68
|
+
* $i + 1
|
|
69
|
+
* }
|
|
70
|
+
*
|
|
71
|
+
* This is informational - while loops work, but each is clearer for collections.
|
|
72
|
+
*
|
|
73
|
+
* References:
|
|
74
|
+
* - docs/guide-conventions.md:188-196
|
|
75
|
+
*/
|
|
76
|
+
export declare const USE_EACH: ValidationRule;
|
|
77
|
+
//# sourceMappingURL=loops.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loops.d.ts","sourceRoot":"","sources":["../../../src/check/rules/loops.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EAGf,MAAM,aAAa,CAAC;AAmLrB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,gBAAgB,EAAE,cAyC9B,CAAC;AAMF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,EAAE,cA2B7B,CAAC;AAMF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,QAAQ,EAAE,cAmCtB,CAAC"}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loop Convention Rules
|
|
3
|
+
* Enforces conventions for while, do-while, and loop control flow.
|
|
4
|
+
*/
|
|
5
|
+
import { extractContextLine } from './helpers.js';
|
|
6
|
+
// ============================================================
|
|
7
|
+
// HELPER FUNCTIONS
|
|
8
|
+
// ============================================================
|
|
9
|
+
/**
|
|
10
|
+
* Collect all variable captures (=> $name) in the given AST node.
|
|
11
|
+
*/
|
|
12
|
+
function collectCaptures(node, names) {
|
|
13
|
+
switch (node.type) {
|
|
14
|
+
case 'Capture':
|
|
15
|
+
names.push(`$${node.name}`);
|
|
16
|
+
return;
|
|
17
|
+
case 'Block':
|
|
18
|
+
node.statements.forEach((stmt) => collectCaptures(stmt, names));
|
|
19
|
+
return;
|
|
20
|
+
case 'Statement':
|
|
21
|
+
collectCaptures(node.expression, names);
|
|
22
|
+
return;
|
|
23
|
+
case 'AnnotatedStatement':
|
|
24
|
+
collectCaptures(node.statement, names);
|
|
25
|
+
return;
|
|
26
|
+
case 'PipeChain':
|
|
27
|
+
node.pipes.forEach((pipe) => collectCaptures(pipe, names));
|
|
28
|
+
if (node.terminator && node.terminator.type === 'Capture')
|
|
29
|
+
collectCaptures(node.terminator, names);
|
|
30
|
+
return;
|
|
31
|
+
case 'PostfixExpr':
|
|
32
|
+
collectCaptures(node.primary, names);
|
|
33
|
+
node.methods.forEach((method) => collectCaptures(method, names));
|
|
34
|
+
return;
|
|
35
|
+
case 'BinaryExpr':
|
|
36
|
+
collectCaptures(node.left, names);
|
|
37
|
+
collectCaptures(node.right, names);
|
|
38
|
+
return;
|
|
39
|
+
case 'UnaryExpr':
|
|
40
|
+
collectCaptures(node.operand, names);
|
|
41
|
+
return;
|
|
42
|
+
case 'GroupedExpr':
|
|
43
|
+
collectCaptures(node.expression, names);
|
|
44
|
+
return;
|
|
45
|
+
case 'Conditional':
|
|
46
|
+
if (node.input)
|
|
47
|
+
collectCaptures(node.input, names);
|
|
48
|
+
if (node.condition)
|
|
49
|
+
collectCaptures(node.condition, names);
|
|
50
|
+
collectCaptures(node.thenBranch, names);
|
|
51
|
+
if (node.elseBranch)
|
|
52
|
+
collectCaptures(node.elseBranch, names);
|
|
53
|
+
return;
|
|
54
|
+
case 'WhileLoop':
|
|
55
|
+
case 'DoWhileLoop':
|
|
56
|
+
collectCaptures(node.body, names);
|
|
57
|
+
return;
|
|
58
|
+
default:
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Collect all variable references ($name) in the given AST node.
|
|
64
|
+
*/
|
|
65
|
+
function collectVariableReferences(node, names) {
|
|
66
|
+
switch (node.type) {
|
|
67
|
+
case 'Variable':
|
|
68
|
+
// Add the variable name if it's not the pipe variable ($)
|
|
69
|
+
if (!node.isPipeVar && node.name) {
|
|
70
|
+
names.push(`$${node.name}`);
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
case 'Block':
|
|
74
|
+
node.statements.forEach((stmt) => collectVariableReferences(stmt, names));
|
|
75
|
+
return;
|
|
76
|
+
case 'Statement':
|
|
77
|
+
collectVariableReferences(node.expression, names);
|
|
78
|
+
return;
|
|
79
|
+
case 'AnnotatedStatement':
|
|
80
|
+
collectVariableReferences(node.statement, names);
|
|
81
|
+
return;
|
|
82
|
+
case 'PipeChain':
|
|
83
|
+
collectVariableReferences(node.head, names);
|
|
84
|
+
node.pipes.forEach((pipe) => collectVariableReferences(pipe, names));
|
|
85
|
+
if (node.terminator)
|
|
86
|
+
collectVariableReferences(node.terminator, names);
|
|
87
|
+
return;
|
|
88
|
+
case 'PostfixExpr':
|
|
89
|
+
collectVariableReferences(node.primary, names);
|
|
90
|
+
node.methods.forEach((method) => collectVariableReferences(method, names));
|
|
91
|
+
return;
|
|
92
|
+
case 'BinaryExpr':
|
|
93
|
+
collectVariableReferences(node.left, names);
|
|
94
|
+
collectVariableReferences(node.right, names);
|
|
95
|
+
return;
|
|
96
|
+
case 'UnaryExpr':
|
|
97
|
+
collectVariableReferences(node.operand, names);
|
|
98
|
+
return;
|
|
99
|
+
case 'GroupedExpr':
|
|
100
|
+
collectVariableReferences(node.expression, names);
|
|
101
|
+
return;
|
|
102
|
+
case 'Conditional':
|
|
103
|
+
if (node.input)
|
|
104
|
+
collectVariableReferences(node.input, names);
|
|
105
|
+
if (node.condition)
|
|
106
|
+
collectVariableReferences(node.condition, names);
|
|
107
|
+
collectVariableReferences(node.thenBranch, names);
|
|
108
|
+
if (node.elseBranch)
|
|
109
|
+
collectVariableReferences(node.elseBranch, names);
|
|
110
|
+
return;
|
|
111
|
+
case 'WhileLoop':
|
|
112
|
+
case 'DoWhileLoop':
|
|
113
|
+
collectVariableReferences(node.condition, names);
|
|
114
|
+
collectVariableReferences(node.body, names);
|
|
115
|
+
return;
|
|
116
|
+
default:
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Check if a loop body appears to be calling a retry function.
|
|
122
|
+
* Simple heuristic: looks for function calls like attemptOperation() or retry().
|
|
123
|
+
*/
|
|
124
|
+
function callsRetryFunction(node) {
|
|
125
|
+
if (node.type === 'Block') {
|
|
126
|
+
return node.statements.some((stmt) => callsRetryFunction(stmt));
|
|
127
|
+
}
|
|
128
|
+
if (node.type === 'Statement') {
|
|
129
|
+
return callsRetryFunction(node.expression);
|
|
130
|
+
}
|
|
131
|
+
if (node.type === 'PipeChain') {
|
|
132
|
+
const chain = node;
|
|
133
|
+
const head = chain.head;
|
|
134
|
+
// Check if head is a function call
|
|
135
|
+
if (head.type === 'PostfixExpr') {
|
|
136
|
+
const primary = head.primary;
|
|
137
|
+
if (primary.type === 'HostCall' || primary.type === 'ClosureCall') {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
// ============================================================
|
|
145
|
+
// LOOP_ACCUMULATOR RULE
|
|
146
|
+
// ============================================================
|
|
147
|
+
/**
|
|
148
|
+
* Validates that variables captured in loop bodies aren't referenced in conditions.
|
|
149
|
+
*
|
|
150
|
+
* In while and do-while loops, $ serves as the accumulator across iterations.
|
|
151
|
+
* Variables captured inside the loop body exist only within that iteration, so
|
|
152
|
+
* referencing them in the loop condition is a logic error - the condition will
|
|
153
|
+
* always see undefined (or the outer scope variable if one exists).
|
|
154
|
+
*
|
|
155
|
+
* Error pattern (captured variable in condition):
|
|
156
|
+
* 0 -> ($x < 5) @ { # $x is undefined in condition
|
|
157
|
+
* $ => $x
|
|
158
|
+
* $x + 1
|
|
159
|
+
* }
|
|
160
|
+
*
|
|
161
|
+
* Correct pattern ($ as accumulator):
|
|
162
|
+
* 0 -> ($ < 5) @ { $ + 1 }
|
|
163
|
+
*
|
|
164
|
+
* Also correct (capture only used within iteration):
|
|
165
|
+
* 0 -> ($ < 5) @ {
|
|
166
|
+
* $ => $x
|
|
167
|
+
* log($x) # $x only used in body, not condition
|
|
168
|
+
* $x + 1
|
|
169
|
+
* }
|
|
170
|
+
*
|
|
171
|
+
* References:
|
|
172
|
+
* - docs/guide-conventions.md:151-171
|
|
173
|
+
*/
|
|
174
|
+
export const LOOP_ACCUMULATOR = {
|
|
175
|
+
code: 'LOOP_ACCUMULATOR',
|
|
176
|
+
category: 'loops',
|
|
177
|
+
severity: 'info',
|
|
178
|
+
nodeTypes: ['WhileLoop', 'DoWhileLoop'],
|
|
179
|
+
validate(node, context) {
|
|
180
|
+
const loop = node;
|
|
181
|
+
// Collect all variable captures in loop body
|
|
182
|
+
const capturedNames = [];
|
|
183
|
+
collectCaptures(loop.body, capturedNames);
|
|
184
|
+
if (capturedNames.length === 0) {
|
|
185
|
+
return []; // No captures, no problem
|
|
186
|
+
}
|
|
187
|
+
// Collect all variable references in loop condition
|
|
188
|
+
const conditionRefs = [];
|
|
189
|
+
collectVariableReferences(loop.condition, conditionRefs);
|
|
190
|
+
// Find captures that are referenced in the condition
|
|
191
|
+
const capturedSet = new Set(capturedNames);
|
|
192
|
+
const problematicVars = conditionRefs.filter((ref) => capturedSet.has(ref));
|
|
193
|
+
if (problematicVars.length > 0) {
|
|
194
|
+
const vars = [...new Set(problematicVars)].join(', ');
|
|
195
|
+
return [
|
|
196
|
+
{
|
|
197
|
+
location: node.span.start,
|
|
198
|
+
severity: 'info',
|
|
199
|
+
code: 'LOOP_ACCUMULATOR',
|
|
200
|
+
message: `${vars} captured in loop body but referenced in condition; loop body variables reset each iteration`,
|
|
201
|
+
context: extractContextLine(node.span.start.line, context.source),
|
|
202
|
+
fix: null, // Complex fix - requires refactoring loop body
|
|
203
|
+
},
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
return [];
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
// ============================================================
|
|
210
|
+
// PREFER_DO_WHILE RULE
|
|
211
|
+
// ============================================================
|
|
212
|
+
/**
|
|
213
|
+
* Suggests using do-while for retry patterns.
|
|
214
|
+
*
|
|
215
|
+
* Do-while is clearer for retry patterns where the body must run at least once:
|
|
216
|
+
*
|
|
217
|
+
* Good (do-while for retry):
|
|
218
|
+
* @ {
|
|
219
|
+
* attemptOperation()
|
|
220
|
+
* } ? (.contains("RETRY"))
|
|
221
|
+
*
|
|
222
|
+
* Less clear (while with separate first attempt):
|
|
223
|
+
* attemptOperation() => $result
|
|
224
|
+
* $result -> .contains("RETRY") @ {
|
|
225
|
+
* attemptOperation()
|
|
226
|
+
* }
|
|
227
|
+
*
|
|
228
|
+
* This is informational - helps guide users to the clearer pattern.
|
|
229
|
+
*
|
|
230
|
+
* References:
|
|
231
|
+
* - docs/guide-conventions.md:173-186
|
|
232
|
+
*/
|
|
233
|
+
export const PREFER_DO_WHILE = {
|
|
234
|
+
code: 'PREFER_DO_WHILE',
|
|
235
|
+
category: 'loops',
|
|
236
|
+
severity: 'info',
|
|
237
|
+
nodeTypes: ['WhileLoop'],
|
|
238
|
+
validate(node, context) {
|
|
239
|
+
const loop = node;
|
|
240
|
+
// Heuristic: if loop body appears to be calling a retry/attempt function,
|
|
241
|
+
// suggest do-while
|
|
242
|
+
if (callsRetryFunction(loop.body)) {
|
|
243
|
+
return [
|
|
244
|
+
{
|
|
245
|
+
location: node.span.start,
|
|
246
|
+
severity: 'info',
|
|
247
|
+
code: 'PREFER_DO_WHILE',
|
|
248
|
+
message: 'Consider do-while for retry patterns where body runs at least once: @ { body } ? (condition)',
|
|
249
|
+
context: extractContextLine(node.span.start.line, context.source),
|
|
250
|
+
fix: null, // Complex fix - requires restructuring to do-while
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
}
|
|
254
|
+
return [];
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
// ============================================================
|
|
258
|
+
// USE_EACH RULE
|
|
259
|
+
// ============================================================
|
|
260
|
+
/**
|
|
261
|
+
* Suggests using each for collection iteration instead of while loops.
|
|
262
|
+
*
|
|
263
|
+
* When iterating over a collection, each is clearer and more idiomatic:
|
|
264
|
+
*
|
|
265
|
+
* Good (each for collection):
|
|
266
|
+
* $items -> each { process($) }
|
|
267
|
+
*
|
|
268
|
+
* Less clear (while loop):
|
|
269
|
+
* 0 => $i
|
|
270
|
+
* ($i < $items.len) @ {
|
|
271
|
+
* $items[$i] -> process()
|
|
272
|
+
* $i + 1
|
|
273
|
+
* }
|
|
274
|
+
*
|
|
275
|
+
* This is informational - while loops work, but each is clearer for collections.
|
|
276
|
+
*
|
|
277
|
+
* References:
|
|
278
|
+
* - docs/guide-conventions.md:188-196
|
|
279
|
+
*/
|
|
280
|
+
export const USE_EACH = {
|
|
281
|
+
code: 'USE_EACH',
|
|
282
|
+
category: 'loops',
|
|
283
|
+
severity: 'info',
|
|
284
|
+
nodeTypes: ['WhileLoop'],
|
|
285
|
+
validate(node, context) {
|
|
286
|
+
const loop = node;
|
|
287
|
+
// Simple heuristic: if the condition or body appears to be doing array iteration
|
|
288
|
+
const conditionStr = JSON.stringify(loop.condition);
|
|
289
|
+
const bodyStr = JSON.stringify(loop.body);
|
|
290
|
+
// Look for patterns like:
|
|
291
|
+
// - field access to 'len' (array length checks)
|
|
292
|
+
// - bracket access patterns with BracketAccess nodes in body
|
|
293
|
+
const hasLenCheck = conditionStr.includes('"field":"len"');
|
|
294
|
+
const hasBracketAccess = bodyStr.includes('"accessKind":"bracket"');
|
|
295
|
+
if (hasLenCheck || hasBracketAccess) {
|
|
296
|
+
return [
|
|
297
|
+
{
|
|
298
|
+
location: node.span.start,
|
|
299
|
+
severity: 'info',
|
|
300
|
+
code: 'USE_EACH',
|
|
301
|
+
message: "Use 'each' for collection iteration instead of while loops: collection -> each { body }",
|
|
302
|
+
context: extractContextLine(node.span.start.line, context.source),
|
|
303
|
+
fix: null, // Complex fix - requires restructuring to each
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
}
|
|
307
|
+
return [];
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
//# sourceMappingURL=loops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loops.js","sourceRoot":"","sources":["../../../src/check/rules/loops.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Naming Convention Rules
|
|
3
|
+
* Enforces snake_case naming for variables, parameters, and dict keys.
|
|
4
|
+
*/
|
|
5
|
+
import type { ValidationRule } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Validates that variable definitions, parameters, and dict keys use snake_case.
|
|
8
|
+
*
|
|
9
|
+
* Checks definition sites only (not variable usage):
|
|
10
|
+
* - Captures: => $user_name, => $item_list, => $is_valid
|
|
11
|
+
* - Closure params: |user_name, count| { }
|
|
12
|
+
* - Dict keys: [user_name: "Alice", is_active: true]
|
|
13
|
+
*
|
|
14
|
+
* Exceptions:
|
|
15
|
+
* - Single-letter names are valid (common for loop variables)
|
|
16
|
+
*
|
|
17
|
+
* References:
|
|
18
|
+
* - docs/guide-conventions.md:10-53
|
|
19
|
+
*/
|
|
20
|
+
export declare const NAMING_SNAKE_CASE: ValidationRule;
|
|
21
|
+
//# sourceMappingURL=naming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../../src/check/rules/naming.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EAKf,MAAM,aAAa,CAAC;AAsFrB;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAkI/B,CAAC"}
|