| 100 | } |
| 101 | |
| 102 | export class TcbExpressionTranslator { |
| 103 | constructor( |
| 104 | protected tcb: Context, |
| 105 | protected scope: Scope, |
| 106 | ) {} |
| 107 | |
| 108 | translate(ast: AST): TcbExpr { |
| 109 | // `astToTcbExpr` actually does the conversion. A special resolver `tcbResolve` is passed |
| 110 | // which interprets specific expression nodes that interact with the `ImplicitReceiver`. These |
| 111 | // nodes actually refer to identifiers within the current scope. |
| 112 | return astToTcbExpr(ast, (ast: AST) => this.resolve(ast), this.tcb.env.config); |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * Resolve an `AST` expression within the given scope. |
| 117 | * |
| 118 | * Some `AST` expressions refer to top-level concepts (references, variables, the component |
| 119 | * context). This method assists in resolving those. |
| 120 | */ |
| 121 | protected resolve(ast: AST): TcbExpr | null { |
| 122 | if (ast instanceof PropertyRead && ast.receiver instanceof ImplicitReceiver) { |
| 123 | // Try to resolve a bound target for this expression. If no such target is available, then |
| 124 | // the expression is referencing the top-level component context. In that case, `null` is |
| 125 | // returned here to let it fall through resolution so it will be caught when the |
| 126 | // `ImplicitReceiver` is resolved in the branch below. |
| 127 | const target = this.tcb.boundTarget.getExpressionTarget(ast); |
| 128 | const targetExpression = target === null ? null : this.getTargetNodeExpression(target, ast); |
| 129 | if (target instanceof LetDeclaration && !this.isValidLetDeclarationAccess(target, ast)) { |
| 130 | this.tcb.oobRecorder.letUsedBeforeDefinition(this.tcb.id, ast, target); |
| 131 | // Cast the expression to `any` so we don't produce additional diagnostics. |
| 132 | // We don't use `markIgnoreForDiagnostics` here, because it won't prevent duplicate |
| 133 | // diagnostics for nested accesses in cases like `@let value = value.foo.bar.baz`. |
| 134 | if (targetExpression !== null) { |
| 135 | return new TcbExpr(`${targetExpression.print()} as any`); |
| 136 | } |
| 137 | } |
| 138 | return targetExpression; |
| 139 | } else if ( |
| 140 | ast instanceof Binary && |
| 141 | Binary.isAssignmentOperation(ast.operation) && |
| 142 | ast.left instanceof PropertyRead && |
| 143 | (ast.left.receiver instanceof ImplicitReceiver || ast.left.receiver instanceof ThisReceiver) |
| 144 | ) { |
| 145 | const read = ast.left; |
| 146 | const target = this.tcb.boundTarget.getExpressionTarget(read); |
| 147 | if (target === null) { |
| 148 | return null; |
| 149 | } |
| 150 | |
| 151 | const targetExpression = this.getTargetNodeExpression(target, read); |
| 152 | const expr = this.translate(ast.right); |
| 153 | const result = new TcbExpr(`(${targetExpression.print()} = ${expr.print()})`); |
| 154 | result.addParseSpanInfo(read.sourceSpan); |
| 155 | |
| 156 | // Ignore diagnostics from TS produced for writes to `@let` and re-report them using |
| 157 | // our own infrastructure. We can't rely on the TS reporting, because it includes |
| 158 | // the name of the auto-generated TCB variable name. |
| 159 | if (target instanceof LetDeclaration) { |
nothing calls this directly
no outgoing calls
no test coverage detected
searching dependent graphs…