(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel)
| 2361 | } |
| 2362 | |
| 2363 | func (b *Binder) bindOptionalChain(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { |
| 2364 | // For an optional chain, we emulate the behavior of a logical expression: |
| 2365 | // |
| 2366 | // a?.b -> a && a.b |
| 2367 | // a?.b.c -> a && a.b.c |
| 2368 | // a?.b?.c -> a && a.b && a.b.c |
| 2369 | // a?.[x = 1] -> a && a[x = 1] |
| 2370 | // |
| 2371 | // To do this we descend through the chain until we reach the root of a chain (the expression with a `?.`) |
| 2372 | // and build it's CFA graph as if it were the first condition (`a && ...`). Then we bind the rest |
| 2373 | // of the node as part of the "true" branch, and continue to do so as we ascend back up to the outermost |
| 2374 | // chain node. We then treat the entire node as the right side of the expression. |
| 2375 | var preChainLabel *ast.FlowLabel |
| 2376 | if ast.IsOptionalChainRoot(node) { |
| 2377 | preChainLabel = b.createBranchLabel() |
| 2378 | } |
| 2379 | b.bindOptionalExpression(node.Expression(), core.IfElse(preChainLabel != nil, preChainLabel, trueTarget), falseTarget) |
| 2380 | if preChainLabel != nil { |
| 2381 | b.currentFlow = b.finishFlowLabel(preChainLabel) |
| 2382 | } |
| 2383 | b.doWithConditionalBranches((*Binder).bindOptionalChainRest, node, trueTarget, falseTarget) |
| 2384 | if ast.IsOutermostOptionalChain(node) { |
| 2385 | b.addAntecedent(trueTarget, b.createFlowCondition(ast.FlowFlagsTrueCondition, b.currentFlow, node)) |
| 2386 | b.addAntecedent(falseTarget, b.createFlowCondition(ast.FlowFlagsFalseCondition, b.currentFlow, node)) |
| 2387 | } |
| 2388 | } |
| 2389 | |
| 2390 | func (b *Binder) bindOptionalExpression(node *ast.Node, trueTarget *ast.FlowLabel, falseTarget *ast.FlowLabel) { |
| 2391 | b.doWithConditionalBranches((*Binder).bind, node, trueTarget, falseTarget) |
no test coverage detected