|
| 1 | +--- |
| 2 | +"@objectstack/formula": patch |
| 3 | +--- |
| 4 | + |
| 5 | +fix(formula): a stdlib function written as a method gets the bare call shape, not the dialect (#14203) |
| 6 | + |
| 7 | +`validateExpression` refused `record.name.upper()` correctly and then handed the |
| 8 | +author the generic dialect trailer — "`predicate`s are bare CEL (e.g. |
| 9 | +`record.rating >= 4`)" — advice that cannot succeed on a source that already IS |
| 10 | +bare CEL and parses fine. The third instance of the same defect family as the |
| 11 | +`bounds` class (#7073) and the unknown-name class (#13821), and the one neither |
| 12 | +of them could cover: #13821's arm fires only when the name is ABSENT from |
| 13 | +`CEL_STDLIB_FUNCTIONS`, and `upper` is present, so this class had no |
| 14 | +prescription at all. The name is right; the call SHAPE is wrong. |
| 15 | + |
| 16 | +It is a high-frequency AI-author mistake, not an exotic one: method-call syntax |
| 17 | +is what almost every other language uses for string operations, so a generator |
| 18 | +that knows `upper` exists reaches for `record.name.upper()` before |
| 19 | +`upper(record.name)`. The remedy is one sentence and it is mechanical — the |
| 20 | +correct spelling is derivable from the fault itself: |
| 21 | + |
| 22 | +``` |
| 23 | +invalid CEL predicate: found no matching overload for 'dyn.upper()' |
| 24 | +
|
| 25 | +> 1 | record.name.upper() |
| 26 | + ^ — `upper` is callable bare, not as a method — a CALL-SHAPE fault, not |
| 27 | +a dialect mistake, so re-spelling the expression will not fix it. Write |
| 28 | +`upper(record.name)` instead. The callable names this platform advertises for |
| 29 | +authoring (the `functions` list `introspectScope` returns, |
| 30 | +`CEL_STDLIB_FUNCTIONS`) take their subject as an argument; only cel-js's own |
| 31 | +receiver methods (`record.name.split(',')`) are written after a dot. |
| 32 | +``` |
| 33 | + |
| 34 | +The spelling is assembled from the SOURCE, because cel-js's message names the |
| 35 | +receiver's TYPE (`dyn.upper()`) and never the author's expression. When the |
| 36 | +receiver is not a plain dotted chain (`record.tags[0].upper()`, |
| 37 | +`(a + b).upper()`, `'lit'.upper()`) the message names the call shape — |
| 38 | +`upper(…)` with the receiver as its first argument — rather than inventing a |
| 39 | +spelling it cannot derive. |
| 40 | + |
| 41 | +The arm is keyed on membership of the bare-callable catalog plus the |
| 42 | +environment's own record of the receiver form, never on the call shape alone. |
| 43 | +Two classes therefore keep exactly the behaviour they had: |
| 44 | + |
| 45 | +- the 33 receiver-only names cel-js registers (`split`, `map`, `getFullYear`) |
| 46 | + are correct ONLY after a dot — `record.name.split(',')` type-checks and never |
| 47 | + reaches this arm; |
| 48 | +- the seven advertised names registered BOTH ways (`contains`, `endsWith`, |
| 49 | + `matches`, `size`, `startsWith`, `string`, `trim`) keep the existing trailer |
| 50 | + when a receiver call of them faults, because the fault there is the arguments |
| 51 | + (`record.name.contains()`), and a bare rewrite would fault just as hard. |
| 52 | + |
| 53 | +No change to `CEL_STDLIB_FUNCTIONS`, to the registered environment, or to what |
| 54 | +`validateExpression` accepts: the receiver call was refused before this change |
| 55 | +and is refused after it. Only the sentence the author is told to act on changes. |
0 commit comments