@@ -4374,3 +4374,67 @@ export class TestComponent {
43744374 assert ! ( code. contains( "i18n_0" ) , "Should have i18n_0 variable. Output:\n {code}" ) ;
43754375 assert ! ( code. contains( "i18n_1" ) , "Should have i18n_1 variable. Output:\n {code}" ) ;
43764376}
4377+
4378+ /// Test that pipe inside binary expression with safe navigation generates a temporary variable.
4379+ ///
4380+ /// When a pipe result is wrapped in a binary expression (e.g., `(data$ | async) || fallback`)
4381+ /// and then used with safe navigation (`?.`), the compiler should generate a temporary variable
4382+ /// to avoid calling the pipe twice.
4383+ ///
4384+ /// This is a port of the Angular TS behavior where `needsTemporaryInSafeAccess` checks through
4385+ /// `BinaryOperatorExpr` to find nested pipe expressions that need temporaries.
4386+ ///
4387+ /// Without the fix, the compiler generates:
4388+ /// `(pipeBind1(...) || fallback) == null ? null : (pipeBind1(...) || fallback).prop`
4389+ /// (pipe called twice, slot indices doubled)
4390+ ///
4391+ /// With the fix:
4392+ /// `(tmp_0_0 = pipeBind1(...) || fallback) == null ? null : tmp_0_0.prop`
4393+ /// (pipe called once, stored in temp variable)
4394+ #[ test]
4395+ fn test_pipe_in_binary_with_safe_nav_uses_temp_variable ( ) {
4396+ let js = compile_template_to_js (
4397+ r#"<div [title]="((data$ | async) || defaultVal)?.name"></div>"# ,
4398+ "TestComponent" ,
4399+ ) ;
4400+ eprintln ! ( "OUTPUT:\n {js}" ) ;
4401+
4402+ // Should use a temporary variable to avoid double pipe evaluation
4403+ assert ! (
4404+ js. contains( "tmp_0_0" ) ,
4405+ "Should generate tmp_0_0 for pipe inside binary with safe nav. Output:\n {js}"
4406+ ) ;
4407+
4408+ // The pipe should only appear ONCE in the expression (stored in tmp)
4409+ let pipe_count = js. matches ( "pipeBind1(" ) . count ( ) ;
4410+ assert_eq ! (
4411+ pipe_count, 1 ,
4412+ "pipeBind1 should appear exactly once (not duplicated). Found {pipe_count} occurrences. Output:\n {js}"
4413+ ) ;
4414+ }
4415+
4416+ /// Test pipe in binary with safe navigation and chained property access.
4417+ ///
4418+ /// More complex case: `((data$ | async) || fallback)?.nested?.value`
4419+ /// The entire safe navigation chain should use the temp variable.
4420+ #[ test]
4421+ fn test_pipe_in_binary_with_safe_nav_chain ( ) {
4422+ let js = compile_template_to_js (
4423+ r#"<div [title]="((data$ | async) || defaultVal)?.nested?.value"></div>"# ,
4424+ "TestComponent" ,
4425+ ) ;
4426+ eprintln ! ( "OUTPUT:\n {js}" ) ;
4427+
4428+ // Should use a temporary variable
4429+ assert ! (
4430+ js. contains( "tmp_0_0" ) ,
4431+ "Should generate tmp_0_0 for pipe inside binary with safe nav chain. Output:\n {js}"
4432+ ) ;
4433+
4434+ // The pipe should only appear ONCE
4435+ let pipe_count = js. matches ( "pipeBind1(" ) . count ( ) ;
4436+ assert_eq ! (
4437+ pipe_count, 1 ,
4438+ "pipeBind1 should appear exactly once. Found {pipe_count}. Output:\n {js}"
4439+ ) ;
4440+ }
0 commit comments