Skip to content

if/else-if condition rewriter doesn't recurse into call(...).prop — while-conditions handle the identical shape correctly #254

Description

@port2life

Environment

  • PineTS version: 0.9.28 (npm package pinets, and the browser UMD bundle — both
    byte-identical per SHA-256)
  • Reproduced with a small Node.js script that imports PineTS from the npm package,
    builds a new PineTS(candles, symbol, timeframe) engine against a plain OHLC candle
    array, and calls await engine.run(source). Node v20+ (tested on v25.9.0 — run
    node --version to confirm on your machine). Not dependent on any host app.

Minimal reproduction

//@version=6
indicator("repro")
type store
    float src
var store[] upbin = array.new<store>()
upbin.unshift(store.new(high))
if upbin.get(0).src > 0
    label.new(bar_index, high, "x")
plot(close)

Observed

upbin is not defined

Again a runtime ReferenceError — the script "transpiles" but the emitted JS is broken.

Expected

TradingView compiles and runs this. upbin.get(0).src inside an if condition should
resolve to the array element's field, same as it does anywhere else in the script.

The exact same expression shape in a while condition works correctly:

type store
    float src
var store[] upbin = array.new<store>()
upbin.unshift(store.new(high))
i = 0
while upbin.get(0).src > 0 and i < 1
    i := i + 1

This transpiles and runs fine — upbin resolves correctly.

Analysis

Appears to be in the statement transformer's expression-rewrite pass that runs
specifically over if/else if test expressions. Its member-expression visitor doesn't
recurse into the object position when that object is itself a call expression — so for
arr.get(i).prop, the outer .prop access is visited but the inner arr.get(i) call
(and specifically the arr identifier inside it) is never rewritten to a JS-safe
reference. The emitted code ends up referencing the raw Pine-side name, which was never
declared in the generated JS scope. The equivalent rewrite pass used for while
conditions does not have this gap — it correctly recurses through call-expression
objects, which is why the identical expression works there.

Suggested fix

Have the if/else-if condition rewriter's member-expression visitor recurse into the
object when it's a call expression (reusing whatever visitor logic the while-condition
path already applies correctly).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions