@seljs/checker 1.0.0 → 1.0.1
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/CHANGELOG.md +7 -0
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/checker/checker.cjs +482 -0
- package/dist/checker/checker.d.cts +176 -0
- package/dist/checker/checker.d.mts +176 -0
- package/dist/checker/checker.mjs +481 -0
- package/dist/checker/diagnostics.cjs +66 -0
- package/dist/checker/diagnostics.mjs +66 -0
- package/dist/checker/index.cjs +2 -0
- package/dist/checker/index.d.mts +2 -0
- package/dist/checker/index.mjs +3 -0
- package/dist/checker/type-compatibility.cjs +70 -0
- package/dist/checker/type-compatibility.d.cts +8 -0
- package/dist/checker/type-compatibility.d.mts +8 -0
- package/dist/checker/type-compatibility.mjs +69 -0
- package/dist/constants.cjs +14 -0
- package/dist/constants.d.cts +7 -0
- package/dist/constants.d.mts +7 -0
- package/dist/constants.mjs +13 -0
- package/dist/debug.cjs +7 -0
- package/dist/debug.mjs +5 -0
- package/dist/environment/codec-registry.cjs +109 -0
- package/dist/environment/codec-registry.d.cts +45 -0
- package/dist/environment/codec-registry.d.mts +45 -0
- package/dist/environment/codec-registry.mjs +108 -0
- package/dist/environment/hydrate.cjs +163 -0
- package/dist/environment/hydrate.d.cts +52 -0
- package/dist/environment/hydrate.d.mts +52 -0
- package/dist/environment/hydrate.mjs +160 -0
- package/dist/environment/index.cjs +4 -0
- package/dist/environment/index.d.mts +4 -0
- package/dist/environment/index.mjs +5 -0
- package/dist/environment/register-types.cjs +107 -0
- package/dist/environment/register-types.d.cts +15 -0
- package/dist/environment/register-types.d.mts +15 -0
- package/dist/environment/register-types.mjs +106 -0
- package/dist/environment/value-wrappers.cjs +44 -0
- package/dist/environment/value-wrappers.d.cts +20 -0
- package/dist/environment/value-wrappers.d.mts +20 -0
- package/dist/environment/value-wrappers.mjs +41 -0
- package/dist/index.cjs +27 -0
- package/dist/index.d.cts +11 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.mjs +13 -0
- package/dist/rules/defaults/deferred-call.cjs +107 -0
- package/dist/rules/defaults/deferred-call.mjs +106 -0
- package/dist/rules/defaults/index.cjs +6 -0
- package/dist/rules/defaults/index.mjs +7 -0
- package/dist/rules/defaults/no-constant-condition.cjs +31 -0
- package/dist/rules/defaults/no-constant-condition.mjs +31 -0
- package/dist/rules/defaults/no-mixed-operators.cjs +39 -0
- package/dist/rules/defaults/no-mixed-operators.mjs +39 -0
- package/dist/rules/defaults/no-redundant-bool.cjs +26 -0
- package/dist/rules/defaults/no-redundant-bool.mjs +26 -0
- package/dist/rules/defaults/no-self-comparison.cjs +44 -0
- package/dist/rules/defaults/no-self-comparison.mjs +43 -0
- package/dist/rules/defaults/require-type.cjs +18 -0
- package/dist/rules/defaults/require-type.mjs +18 -0
- package/dist/rules/facade.cjs +31 -0
- package/dist/rules/facade.d.cts +20 -0
- package/dist/rules/facade.d.mts +20 -0
- package/dist/rules/facade.mjs +31 -0
- package/dist/rules/index.cjs +2 -0
- package/dist/rules/index.d.mts +3 -0
- package/dist/rules/index.mjs +3 -0
- package/dist/rules/runner.cjs +40 -0
- package/dist/rules/runner.d.cts +27 -0
- package/dist/rules/runner.d.mts +27 -0
- package/dist/rules/runner.mjs +40 -0
- package/dist/rules/types.d.cts +77 -0
- package/dist/rules/types.d.mts +77 -0
- package/dist/utils/ast-utils.cjs +164 -0
- package/dist/utils/ast-utils.mjs +162 -0
- package/package.json +24 -17
- package/dist/checker/checker.d.ts +0 -173
- package/dist/checker/checker.js +0 -567
- package/dist/checker/diagnostics.d.ts +0 -10
- package/dist/checker/diagnostics.js +0 -80
- package/dist/checker/index.d.ts +0 -2
- package/dist/checker/index.js +0 -2
- package/dist/checker/type-compatibility.d.ts +0 -16
- package/dist/checker/type-compatibility.js +0 -59
- package/dist/constants.d.ts +0 -4
- package/dist/constants.js +0 -10
- package/dist/debug.d.ts +0 -2
- package/dist/debug.js +0 -2
- package/dist/environment/codec-registry.d.ts +0 -42
- package/dist/environment/codec-registry.js +0 -146
- package/dist/environment/hydrate.d.ts +0 -48
- package/dist/environment/hydrate.js +0 -198
- package/dist/environment/index.d.ts +0 -4
- package/dist/environment/index.js +0 -4
- package/dist/environment/register-types.d.ts +0 -14
- package/dist/environment/register-types.js +0 -154
- package/dist/environment/value-wrappers.d.ts +0 -17
- package/dist/environment/value-wrappers.js +0 -65
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -4
- package/dist/rules/defaults/deferred-call.d.ts +0 -13
- package/dist/rules/defaults/deferred-call.js +0 -162
- package/dist/rules/defaults/index.d.ts +0 -6
- package/dist/rules/defaults/index.js +0 -6
- package/dist/rules/defaults/no-constant-condition.d.ts +0 -7
- package/dist/rules/defaults/no-constant-condition.js +0 -36
- package/dist/rules/defaults/no-mixed-operators.d.ts +0 -9
- package/dist/rules/defaults/no-mixed-operators.js +0 -44
- package/dist/rules/defaults/no-redundant-bool.d.ts +0 -5
- package/dist/rules/defaults/no-redundant-bool.js +0 -27
- package/dist/rules/defaults/no-self-comparison.d.ts +0 -9
- package/dist/rules/defaults/no-self-comparison.js +0 -31
- package/dist/rules/defaults/require-type.d.ts +0 -7
- package/dist/rules/defaults/require-type.js +0 -19
- package/dist/rules/facade.d.ts +0 -22
- package/dist/rules/facade.js +0 -29
- package/dist/rules/index.d.ts +0 -3
- package/dist/rules/index.js +0 -3
- package/dist/rules/runner.d.ts +0 -16
- package/dist/rules/runner.js +0 -30
- package/dist/rules/types.d.ts +0 -73
- package/dist/rules/types.js +0 -1
- package/dist/utils/ast-utils.d.ts +0 -55
- package/dist/utils/ast-utils.js +0 -255
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { nodeSpan, walkAST } from "../../utils/ast-utils.mjs";
|
|
2
|
+
//#region src/rules/defaults/no-mixed-operators.ts
|
|
3
|
+
/**
|
|
4
|
+
* Check whether a child expression has explicit parentheses in the source.
|
|
5
|
+
* Scans backwards from the child's start position for a `(`.
|
|
6
|
+
*/
|
|
7
|
+
const hasExplicitParens = (src, child) => {
|
|
8
|
+
let i = nodeSpan(child).from - 1;
|
|
9
|
+
while (i >= 0 && (src[i] === " " || src[i] === " ")) i--;
|
|
10
|
+
return i >= 0 && src[i] === "(";
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Flags mixed `&&` and `||` without explicit parentheses.
|
|
14
|
+
*
|
|
15
|
+
* The cel-js parser discards parentheses from the AST, so this rule
|
|
16
|
+
* inspects the original source text via `node.input` to determine
|
|
17
|
+
* whether parentheses were explicitly written.
|
|
18
|
+
*/
|
|
19
|
+
const noMixedOperators = {
|
|
20
|
+
name: "no-mixed-operators",
|
|
21
|
+
description: "Require parentheses when mixing logical operators (&& and ||).",
|
|
22
|
+
defaultSeverity: "info",
|
|
23
|
+
tier: "structural",
|
|
24
|
+
run(context) {
|
|
25
|
+
const diagnostics = [];
|
|
26
|
+
walkAST(context.ast, (node) => {
|
|
27
|
+
if (node.op !== "&&" && node.op !== "||") return;
|
|
28
|
+
const opposite = node.op === "&&" ? "||" : "&&";
|
|
29
|
+
const [left, right] = node.args;
|
|
30
|
+
for (const child of [left, right]) if (child.op === opposite && !hasExplicitParens(node.input, child)) {
|
|
31
|
+
diagnostics.push(context.report(node, "Mixed logical operators without parentheses. Add explicit grouping to clarify precedence."));
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
return diagnostics;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
export { noMixedOperators };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const require_ast_utils = require("../../utils/ast-utils.cjs");
|
|
2
|
+
//#region src/rules/defaults/no-redundant-bool.ts
|
|
3
|
+
/**
|
|
4
|
+
* Flags redundant boolean comparisons: `x == true`, `x != false`, etc.
|
|
5
|
+
*/
|
|
6
|
+
const noRedundantBool = {
|
|
7
|
+
name: "no-redundant-bool",
|
|
8
|
+
description: "Disallow redundant comparisons to boolean literals (true/false).",
|
|
9
|
+
defaultSeverity: "warning",
|
|
10
|
+
tier: "structural",
|
|
11
|
+
run(context) {
|
|
12
|
+
const diagnostics = [];
|
|
13
|
+
require_ast_utils.walkAST(context.ast, (node) => {
|
|
14
|
+
if (node.op !== "==" && node.op !== "!=") return;
|
|
15
|
+
const [left, right] = node.args;
|
|
16
|
+
const leftIsBool = left.op === "value" && typeof left.args === "boolean";
|
|
17
|
+
const rightIsBool = right.op === "value" && typeof right.args === "boolean";
|
|
18
|
+
if (!leftIsBool && !rightIsBool) return;
|
|
19
|
+
const boolValue = leftIsBool ? left.args : right.args;
|
|
20
|
+
diagnostics.push(context.report(node, `Redundant comparison to \`${String(boolValue)}\`. Simplify the expression.`));
|
|
21
|
+
});
|
|
22
|
+
return diagnostics;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
26
|
+
exports.noRedundantBool = noRedundantBool;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { walkAST } from "../../utils/ast-utils.mjs";
|
|
2
|
+
//#region src/rules/defaults/no-redundant-bool.ts
|
|
3
|
+
/**
|
|
4
|
+
* Flags redundant boolean comparisons: `x == true`, `x != false`, etc.
|
|
5
|
+
*/
|
|
6
|
+
const noRedundantBool = {
|
|
7
|
+
name: "no-redundant-bool",
|
|
8
|
+
description: "Disallow redundant comparisons to boolean literals (true/false).",
|
|
9
|
+
defaultSeverity: "warning",
|
|
10
|
+
tier: "structural",
|
|
11
|
+
run(context) {
|
|
12
|
+
const diagnostics = [];
|
|
13
|
+
walkAST(context.ast, (node) => {
|
|
14
|
+
if (node.op !== "==" && node.op !== "!=") return;
|
|
15
|
+
const [left, right] = node.args;
|
|
16
|
+
const leftIsBool = left.op === "value" && typeof left.args === "boolean";
|
|
17
|
+
const rightIsBool = right.op === "value" && typeof right.args === "boolean";
|
|
18
|
+
if (!leftIsBool && !rightIsBool) return;
|
|
19
|
+
const boolValue = leftIsBool ? left.args : right.args;
|
|
20
|
+
diagnostics.push(context.report(node, `Redundant comparison to \`${String(boolValue)}\`. Simplify the expression.`));
|
|
21
|
+
});
|
|
22
|
+
return diagnostics;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
//#endregion
|
|
26
|
+
export { noRedundantBool };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require("../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
const require_ast_utils = require("../../utils/ast-utils.cjs");
|
|
3
|
+
let _marcbachmann_cel_js = require("@marcbachmann/cel-js");
|
|
4
|
+
//#region src/rules/defaults/no-self-comparison.ts
|
|
5
|
+
const COMPARISON_OPS = new Set([
|
|
6
|
+
"==",
|
|
7
|
+
"!=",
|
|
8
|
+
"<",
|
|
9
|
+
"<=",
|
|
10
|
+
">",
|
|
11
|
+
">="
|
|
12
|
+
]);
|
|
13
|
+
const ALWAYS_TRUE_OPS = new Set([
|
|
14
|
+
"==",
|
|
15
|
+
"<=",
|
|
16
|
+
">="
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Flags comparisons where both sides are the same expression (tautology/contradiction).
|
|
20
|
+
*
|
|
21
|
+
* Uses `serialize()` for identity comparison (safe for this purpose —
|
|
22
|
+
* normalization is acceptable when comparing subtree identity, unlike
|
|
23
|
+
* span calculation where it would produce incorrect positions).
|
|
24
|
+
*/
|
|
25
|
+
const noSelfComparison = {
|
|
26
|
+
name: "no-self-comparison",
|
|
27
|
+
description: "Disallow comparing an expression to itself.",
|
|
28
|
+
defaultSeverity: "warning",
|
|
29
|
+
tier: "structural",
|
|
30
|
+
run(context) {
|
|
31
|
+
const diagnostics = [];
|
|
32
|
+
require_ast_utils.walkAST(context.ast, (node) => {
|
|
33
|
+
if (!COMPARISON_OPS.has(node.op)) return;
|
|
34
|
+
const [left, right] = node.args;
|
|
35
|
+
if ((0, _marcbachmann_cel_js.serialize)(left) === (0, _marcbachmann_cel_js.serialize)(right)) {
|
|
36
|
+
const alwaysResult = ALWAYS_TRUE_OPS.has(node.op) ? "true" : "false";
|
|
37
|
+
diagnostics.push(context.report(node, `Both sides of \`${node.op}\` are identical. This is always \`${alwaysResult}\`.`));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return diagnostics;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
exports.noSelfComparison = noSelfComparison;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { walkAST } from "../../utils/ast-utils.mjs";
|
|
2
|
+
import { serialize } from "@marcbachmann/cel-js";
|
|
3
|
+
//#region src/rules/defaults/no-self-comparison.ts
|
|
4
|
+
const COMPARISON_OPS = new Set([
|
|
5
|
+
"==",
|
|
6
|
+
"!=",
|
|
7
|
+
"<",
|
|
8
|
+
"<=",
|
|
9
|
+
">",
|
|
10
|
+
">="
|
|
11
|
+
]);
|
|
12
|
+
const ALWAYS_TRUE_OPS = new Set([
|
|
13
|
+
"==",
|
|
14
|
+
"<=",
|
|
15
|
+
">="
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Flags comparisons where both sides are the same expression (tautology/contradiction).
|
|
19
|
+
*
|
|
20
|
+
* Uses `serialize()` for identity comparison (safe for this purpose —
|
|
21
|
+
* normalization is acceptable when comparing subtree identity, unlike
|
|
22
|
+
* span calculation where it would produce incorrect positions).
|
|
23
|
+
*/
|
|
24
|
+
const noSelfComparison = {
|
|
25
|
+
name: "no-self-comparison",
|
|
26
|
+
description: "Disallow comparing an expression to itself.",
|
|
27
|
+
defaultSeverity: "warning",
|
|
28
|
+
tier: "structural",
|
|
29
|
+
run(context) {
|
|
30
|
+
const diagnostics = [];
|
|
31
|
+
walkAST(context.ast, (node) => {
|
|
32
|
+
if (!COMPARISON_OPS.has(node.op)) return;
|
|
33
|
+
const [left, right] = node.args;
|
|
34
|
+
if (serialize(left) === serialize(right)) {
|
|
35
|
+
const alwaysResult = ALWAYS_TRUE_OPS.has(node.op) ? "true" : "false";
|
|
36
|
+
diagnostics.push(context.report(node, `Both sides of \`${node.op}\` are identical. This is always \`${alwaysResult}\`.`));
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return diagnostics;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { noSelfComparison };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/rules/defaults/require-type.ts
|
|
2
|
+
/**
|
|
3
|
+
* Rule factory that enforces an expression evaluates to the expected CEL type.
|
|
4
|
+
*
|
|
5
|
+
* @param expected - The CEL type name the expression must resolve to (e.g. "bool", "uint256")
|
|
6
|
+
*/
|
|
7
|
+
const requireType = (expected) => ({
|
|
8
|
+
name: `require-type-${expected}`,
|
|
9
|
+
description: `Expression must evaluate to ${expected}.`,
|
|
10
|
+
defaultSeverity: "error",
|
|
11
|
+
tier: "type-aware",
|
|
12
|
+
run(context) {
|
|
13
|
+
if (context.resolvedType === expected) return [];
|
|
14
|
+
return [context.reportAt(0, context.expression.length, `Expected expression to evaluate to "${expected}", but got "${String(context.resolvedType)}".`)];
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
exports.requireType = requireType;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/rules/defaults/require-type.ts
|
|
2
|
+
/**
|
|
3
|
+
* Rule factory that enforces an expression evaluates to the expected CEL type.
|
|
4
|
+
*
|
|
5
|
+
* @param expected - The CEL type name the expression must resolve to (e.g. "bool", "uint256")
|
|
6
|
+
*/
|
|
7
|
+
const requireType = (expected) => ({
|
|
8
|
+
name: `require-type-${expected}`,
|
|
9
|
+
description: `Expression must evaluate to ${expected}.`,
|
|
10
|
+
defaultSeverity: "error",
|
|
11
|
+
tier: "type-aware",
|
|
12
|
+
run(context) {
|
|
13
|
+
if (context.resolvedType === expected) return [];
|
|
14
|
+
return [context.reportAt(0, context.expression.length, `Expected expression to evaluate to "${expected}", but got "${String(context.resolvedType)}".`)];
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
export { requireType };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const require_deferred_call = require("./defaults/deferred-call.cjs");
|
|
2
|
+
const require_no_constant_condition = require("./defaults/no-constant-condition.cjs");
|
|
3
|
+
const require_no_mixed_operators = require("./defaults/no-mixed-operators.cjs");
|
|
4
|
+
const require_no_redundant_bool = require("./defaults/no-redundant-bool.cjs");
|
|
5
|
+
const require_no_self_comparison = require("./defaults/no-self-comparison.cjs");
|
|
6
|
+
const require_require_type = require("./defaults/require-type.cjs");
|
|
7
|
+
require("./defaults/index.cjs");
|
|
8
|
+
//#region src/rules/facade.ts
|
|
9
|
+
/**
|
|
10
|
+
* Unified export for rules.
|
|
11
|
+
*
|
|
12
|
+
* Provides access to individual built-in rules, a convenience array of all
|
|
13
|
+
* built-in rules, and rule factories for custom enforcement.
|
|
14
|
+
*/
|
|
15
|
+
const rules = {
|
|
16
|
+
builtIn: [
|
|
17
|
+
require_no_redundant_bool.noRedundantBool,
|
|
18
|
+
require_no_constant_condition.noConstantCondition,
|
|
19
|
+
require_no_mixed_operators.noMixedOperators,
|
|
20
|
+
require_no_self_comparison.noSelfComparison,
|
|
21
|
+
require_deferred_call.deferredCall
|
|
22
|
+
],
|
|
23
|
+
noRedundantBool: require_no_redundant_bool.noRedundantBool,
|
|
24
|
+
noConstantCondition: require_no_constant_condition.noConstantCondition,
|
|
25
|
+
noMixedOperators: require_no_mixed_operators.noMixedOperators,
|
|
26
|
+
noSelfComparison: require_no_self_comparison.noSelfComparison,
|
|
27
|
+
deferredCall: require_deferred_call.deferredCall,
|
|
28
|
+
requireType: require_require_type.requireType
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
exports.rules = rules;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SELRule } from "./types.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/rules/facade.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Unified export for rules.
|
|
6
|
+
*
|
|
7
|
+
* Provides access to individual built-in rules, a convenience array of all
|
|
8
|
+
* built-in rules, and rule factories for custom enforcement.
|
|
9
|
+
*/
|
|
10
|
+
declare const rules: {
|
|
11
|
+
/** All built-in lint rules. */readonly builtIn: readonly [SELRule, SELRule, SELRule, SELRule, SELRule]; /** Flags redundant comparisons to boolean literals (true/false). */
|
|
12
|
+
readonly noRedundantBool: SELRule; /** Flags constant conditions (e.g. `true && x`, `1 == 1`). */
|
|
13
|
+
readonly noConstantCondition: SELRule; /** Requires parentheses when mixing `&&` and `||`. */
|
|
14
|
+
readonly noMixedOperators: SELRule; /** Flags self-comparisons (e.g. `x == x`). */
|
|
15
|
+
readonly noSelfComparison: SELRule; /** Flags contract calls with dynamic arguments that will execute as live RPC calls. */
|
|
16
|
+
readonly deferredCall: SELRule; /** Rule factory that enforces an expression evaluates to the expected CEL type. */
|
|
17
|
+
readonly requireType: (expected: string) => SELRule;
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { rules };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SELRule } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/rules/facade.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Unified export for rules.
|
|
6
|
+
*
|
|
7
|
+
* Provides access to individual built-in rules, a convenience array of all
|
|
8
|
+
* built-in rules, and rule factories for custom enforcement.
|
|
9
|
+
*/
|
|
10
|
+
declare const rules: {
|
|
11
|
+
/** All built-in lint rules. */readonly builtIn: readonly [SELRule, SELRule, SELRule, SELRule, SELRule]; /** Flags redundant comparisons to boolean literals (true/false). */
|
|
12
|
+
readonly noRedundantBool: SELRule; /** Flags constant conditions (e.g. `true && x`, `1 == 1`). */
|
|
13
|
+
readonly noConstantCondition: SELRule; /** Requires parentheses when mixing `&&` and `||`. */
|
|
14
|
+
readonly noMixedOperators: SELRule; /** Flags self-comparisons (e.g. `x == x`). */
|
|
15
|
+
readonly noSelfComparison: SELRule; /** Flags contract calls with dynamic arguments that will execute as live RPC calls. */
|
|
16
|
+
readonly deferredCall: SELRule; /** Rule factory that enforces an expression evaluates to the expected CEL type. */
|
|
17
|
+
readonly requireType: (expected: string) => SELRule;
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { rules };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { deferredCall } from "./defaults/deferred-call.mjs";
|
|
2
|
+
import { noConstantCondition } from "./defaults/no-constant-condition.mjs";
|
|
3
|
+
import { noMixedOperators } from "./defaults/no-mixed-operators.mjs";
|
|
4
|
+
import { noRedundantBool } from "./defaults/no-redundant-bool.mjs";
|
|
5
|
+
import { noSelfComparison } from "./defaults/no-self-comparison.mjs";
|
|
6
|
+
import { requireType } from "./defaults/require-type.mjs";
|
|
7
|
+
import "./defaults/index.mjs";
|
|
8
|
+
//#region src/rules/facade.ts
|
|
9
|
+
/**
|
|
10
|
+
* Unified export for rules.
|
|
11
|
+
*
|
|
12
|
+
* Provides access to individual built-in rules, a convenience array of all
|
|
13
|
+
* built-in rules, and rule factories for custom enforcement.
|
|
14
|
+
*/
|
|
15
|
+
const rules = {
|
|
16
|
+
builtIn: [
|
|
17
|
+
noRedundantBool,
|
|
18
|
+
noConstantCondition,
|
|
19
|
+
noMixedOperators,
|
|
20
|
+
noSelfComparison,
|
|
21
|
+
deferredCall
|
|
22
|
+
],
|
|
23
|
+
noRedundantBool,
|
|
24
|
+
noConstantCondition,
|
|
25
|
+
noMixedOperators,
|
|
26
|
+
noSelfComparison,
|
|
27
|
+
deferredCall,
|
|
28
|
+
requireType
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { rules };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const require_ast_utils = require("../utils/ast-utils.cjs");
|
|
2
|
+
//#region src/rules/runner.ts
|
|
3
|
+
/**
|
|
4
|
+
* Run enabled rules of the specified tier against a parsed AST.
|
|
5
|
+
*/
|
|
6
|
+
const runRules = ({ expression, ast, schema, rules, tier, resolvedType }) => {
|
|
7
|
+
const diagnostics = [];
|
|
8
|
+
for (const rule of rules) {
|
|
9
|
+
if ((rule.tier ?? "structural") !== tier) continue;
|
|
10
|
+
const severity = rule.defaultSeverity;
|
|
11
|
+
const context = {
|
|
12
|
+
expression,
|
|
13
|
+
ast,
|
|
14
|
+
schema,
|
|
15
|
+
severity,
|
|
16
|
+
resolvedType,
|
|
17
|
+
report(node, message) {
|
|
18
|
+
const span = require_ast_utils.nodeSpan(node);
|
|
19
|
+
return {
|
|
20
|
+
message,
|
|
21
|
+
severity,
|
|
22
|
+
from: span.from,
|
|
23
|
+
to: span.to
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
reportAt(from, to, message) {
|
|
27
|
+
return {
|
|
28
|
+
message,
|
|
29
|
+
severity,
|
|
30
|
+
from,
|
|
31
|
+
to
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
diagnostics.push(...rule.run(context));
|
|
36
|
+
}
|
|
37
|
+
return diagnostics;
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
exports.runRules = runRules;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RuleTier, SELRule } from "./types.cjs";
|
|
2
|
+
import { SELDiagnostic } from "../checker/checker.cjs";
|
|
3
|
+
import { ASTNode } from "@marcbachmann/cel-js";
|
|
4
|
+
import { SELSchema } from "@seljs/schema";
|
|
5
|
+
|
|
6
|
+
//#region src/rules/runner.d.ts
|
|
7
|
+
interface RunRulesOptions {
|
|
8
|
+
expression: string;
|
|
9
|
+
ast: ASTNode;
|
|
10
|
+
schema: SELSchema;
|
|
11
|
+
rules: readonly SELRule[];
|
|
12
|
+
tier: RuleTier;
|
|
13
|
+
resolvedType?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run enabled rules of the specified tier against a parsed AST.
|
|
17
|
+
*/
|
|
18
|
+
declare const runRules: ({
|
|
19
|
+
expression,
|
|
20
|
+
ast,
|
|
21
|
+
schema,
|
|
22
|
+
rules,
|
|
23
|
+
tier,
|
|
24
|
+
resolvedType
|
|
25
|
+
}: RunRulesOptions) => SELDiagnostic[];
|
|
26
|
+
//#endregion
|
|
27
|
+
export { RunRulesOptions, runRules };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RuleTier, SELRule } from "./types.mjs";
|
|
2
|
+
import { SELDiagnostic } from "../checker/checker.mjs";
|
|
3
|
+
import { ASTNode } from "@marcbachmann/cel-js";
|
|
4
|
+
import { SELSchema } from "@seljs/schema";
|
|
5
|
+
|
|
6
|
+
//#region src/rules/runner.d.ts
|
|
7
|
+
interface RunRulesOptions {
|
|
8
|
+
expression: string;
|
|
9
|
+
ast: ASTNode;
|
|
10
|
+
schema: SELSchema;
|
|
11
|
+
rules: readonly SELRule[];
|
|
12
|
+
tier: RuleTier;
|
|
13
|
+
resolvedType?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run enabled rules of the specified tier against a parsed AST.
|
|
17
|
+
*/
|
|
18
|
+
declare const runRules: ({
|
|
19
|
+
expression,
|
|
20
|
+
ast,
|
|
21
|
+
schema,
|
|
22
|
+
rules,
|
|
23
|
+
tier,
|
|
24
|
+
resolvedType
|
|
25
|
+
}: RunRulesOptions) => SELDiagnostic[];
|
|
26
|
+
//#endregion
|
|
27
|
+
export { RunRulesOptions, runRules };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { nodeSpan } from "../utils/ast-utils.mjs";
|
|
2
|
+
//#region src/rules/runner.ts
|
|
3
|
+
/**
|
|
4
|
+
* Run enabled rules of the specified tier against a parsed AST.
|
|
5
|
+
*/
|
|
6
|
+
const runRules = ({ expression, ast, schema, rules, tier, resolvedType }) => {
|
|
7
|
+
const diagnostics = [];
|
|
8
|
+
for (const rule of rules) {
|
|
9
|
+
if ((rule.tier ?? "structural") !== tier) continue;
|
|
10
|
+
const severity = rule.defaultSeverity;
|
|
11
|
+
const context = {
|
|
12
|
+
expression,
|
|
13
|
+
ast,
|
|
14
|
+
schema,
|
|
15
|
+
severity,
|
|
16
|
+
resolvedType,
|
|
17
|
+
report(node, message) {
|
|
18
|
+
const span = nodeSpan(node);
|
|
19
|
+
return {
|
|
20
|
+
message,
|
|
21
|
+
severity,
|
|
22
|
+
from: span.from,
|
|
23
|
+
to: span.to
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
reportAt(from, to, message) {
|
|
27
|
+
return {
|
|
28
|
+
message,
|
|
29
|
+
severity,
|
|
30
|
+
from,
|
|
31
|
+
to
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
diagnostics.push(...rule.run(context));
|
|
36
|
+
}
|
|
37
|
+
return diagnostics;
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
export { runRules };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SELDiagnostic } from "../checker/checker.cjs";
|
|
2
|
+
import { ASTNode } from "@marcbachmann/cel-js";
|
|
3
|
+
import { SELSchema } from "@seljs/schema";
|
|
4
|
+
|
|
5
|
+
//#region src/rules/types.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Severity levels for rules.
|
|
8
|
+
*/
|
|
9
|
+
type RuleSeverity = "error" | "warning" | "info";
|
|
10
|
+
/**
|
|
11
|
+
* Tier determines when a rule runs:
|
|
12
|
+
* - "structural": runs on any successfully-parsed expression, even if type-check fails
|
|
13
|
+
* - "type-aware": runs only when both parse and type-check succeed
|
|
14
|
+
*/
|
|
15
|
+
type RuleTier = "structural" | "type-aware";
|
|
16
|
+
/**
|
|
17
|
+
* Context passed to each rule's run function.
|
|
18
|
+
*/
|
|
19
|
+
interface RuleContext {
|
|
20
|
+
/**
|
|
21
|
+
* The raw expression string.
|
|
22
|
+
*/
|
|
23
|
+
expression: string;
|
|
24
|
+
/**
|
|
25
|
+
* The parsed AST root node.
|
|
26
|
+
*/
|
|
27
|
+
ast: ASTNode;
|
|
28
|
+
/**
|
|
29
|
+
* The active schema (contracts, variables, types, functions).
|
|
30
|
+
*/
|
|
31
|
+
schema: SELSchema;
|
|
32
|
+
/**
|
|
33
|
+
* The resolved severity for this rule invocation.
|
|
34
|
+
*/
|
|
35
|
+
severity: RuleSeverity;
|
|
36
|
+
/**
|
|
37
|
+
* Resolved CEL type of the full expression. Only set for type-aware rules.
|
|
38
|
+
*/
|
|
39
|
+
resolvedType?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Create a diagnostic spanning an AST node's source range.
|
|
42
|
+
*/
|
|
43
|
+
report: (node: ASTNode, message: string) => SELDiagnostic;
|
|
44
|
+
/**
|
|
45
|
+
* Create a diagnostic at an explicit position range.
|
|
46
|
+
*/
|
|
47
|
+
reportAt: (from: number, to: number, message: string) => SELDiagnostic;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A lint rule that analyzes a parsed expression and reports diagnostics.
|
|
51
|
+
*/
|
|
52
|
+
interface SELRule {
|
|
53
|
+
/**
|
|
54
|
+
* Unique rule identifier (kebab-case, e.g. "no-redundant-bool").
|
|
55
|
+
*/
|
|
56
|
+
name: string;
|
|
57
|
+
/**
|
|
58
|
+
* Human-readable description.
|
|
59
|
+
*/
|
|
60
|
+
description: string;
|
|
61
|
+
/**
|
|
62
|
+
* Severity level for diagnostics reported by this rule.
|
|
63
|
+
*/
|
|
64
|
+
defaultSeverity: RuleSeverity;
|
|
65
|
+
/**
|
|
66
|
+
* Execution tier. Defaults to "structural" if omitted.
|
|
67
|
+
* - "structural": rule receives AST even when type-check failed
|
|
68
|
+
* - "type-aware": rule only runs after successful type-check
|
|
69
|
+
*/
|
|
70
|
+
tier?: RuleTier;
|
|
71
|
+
/**
|
|
72
|
+
* Analyze the expression and return diagnostics.
|
|
73
|
+
*/
|
|
74
|
+
run: (context: RuleContext) => SELDiagnostic[];
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { RuleContext, RuleSeverity, RuleTier, SELRule };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SELDiagnostic } from "../checker/checker.mjs";
|
|
2
|
+
import { ASTNode } from "@marcbachmann/cel-js";
|
|
3
|
+
import { SELSchema } from "@seljs/schema";
|
|
4
|
+
|
|
5
|
+
//#region src/rules/types.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Severity levels for rules.
|
|
8
|
+
*/
|
|
9
|
+
type RuleSeverity = "error" | "warning" | "info";
|
|
10
|
+
/**
|
|
11
|
+
* Tier determines when a rule runs:
|
|
12
|
+
* - "structural": runs on any successfully-parsed expression, even if type-check fails
|
|
13
|
+
* - "type-aware": runs only when both parse and type-check succeed
|
|
14
|
+
*/
|
|
15
|
+
type RuleTier = "structural" | "type-aware";
|
|
16
|
+
/**
|
|
17
|
+
* Context passed to each rule's run function.
|
|
18
|
+
*/
|
|
19
|
+
interface RuleContext {
|
|
20
|
+
/**
|
|
21
|
+
* The raw expression string.
|
|
22
|
+
*/
|
|
23
|
+
expression: string;
|
|
24
|
+
/**
|
|
25
|
+
* The parsed AST root node.
|
|
26
|
+
*/
|
|
27
|
+
ast: ASTNode;
|
|
28
|
+
/**
|
|
29
|
+
* The active schema (contracts, variables, types, functions).
|
|
30
|
+
*/
|
|
31
|
+
schema: SELSchema;
|
|
32
|
+
/**
|
|
33
|
+
* The resolved severity for this rule invocation.
|
|
34
|
+
*/
|
|
35
|
+
severity: RuleSeverity;
|
|
36
|
+
/**
|
|
37
|
+
* Resolved CEL type of the full expression. Only set for type-aware rules.
|
|
38
|
+
*/
|
|
39
|
+
resolvedType?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Create a diagnostic spanning an AST node's source range.
|
|
42
|
+
*/
|
|
43
|
+
report: (node: ASTNode, message: string) => SELDiagnostic;
|
|
44
|
+
/**
|
|
45
|
+
* Create a diagnostic at an explicit position range.
|
|
46
|
+
*/
|
|
47
|
+
reportAt: (from: number, to: number, message: string) => SELDiagnostic;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A lint rule that analyzes a parsed expression and reports diagnostics.
|
|
51
|
+
*/
|
|
52
|
+
interface SELRule {
|
|
53
|
+
/**
|
|
54
|
+
* Unique rule identifier (kebab-case, e.g. "no-redundant-bool").
|
|
55
|
+
*/
|
|
56
|
+
name: string;
|
|
57
|
+
/**
|
|
58
|
+
* Human-readable description.
|
|
59
|
+
*/
|
|
60
|
+
description: string;
|
|
61
|
+
/**
|
|
62
|
+
* Severity level for diagnostics reported by this rule.
|
|
63
|
+
*/
|
|
64
|
+
defaultSeverity: RuleSeverity;
|
|
65
|
+
/**
|
|
66
|
+
* Execution tier. Defaults to "structural" if omitted.
|
|
67
|
+
* - "structural": rule receives AST even when type-check failed
|
|
68
|
+
* - "type-aware": rule only runs after successful type-check
|
|
69
|
+
*/
|
|
70
|
+
tier?: RuleTier;
|
|
71
|
+
/**
|
|
72
|
+
* Analyze the expression and return diagnostics.
|
|
73
|
+
*/
|
|
74
|
+
run: (context: RuleContext) => SELDiagnostic[];
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { RuleContext, RuleSeverity, RuleTier, SELRule };
|