@@ -832,3 +832,68 @@ fn test_object_with_to_primitive_related_properties_overridden() {
832832 test ( "+{ ...{ valueOf() { return Symbol() } } }" , true ) ;
833833 test ( "+{ ...{ [Symbol.toPrimitive]() { return Symbol() } } }" , true ) ;
834834}
835+
836+ #[ test]
837+ fn test_typeof_guard_patterns ( ) {
838+ // Test patterns with global variables that should be side-effect-free
839+ // when properly guarded with typeof checks
840+
841+ // Logical AND patterns - typeof x !== 'undefined' && x
842+ test_with_global_variables ( "typeof x !== 'undefined' && x" , vec ! [ "x" . to_string( ) ] , false ) ;
843+ test_with_global_variables ( "typeof x != 'undefined' && x" , vec ! [ "x" . to_string( ) ] , false ) ;
844+ test_with_global_variables ( "'undefined' !== typeof x && x" , vec ! [ "x" . to_string( ) ] , false ) ;
845+ test_with_global_variables ( "'undefined' != typeof x && x" , vec ! [ "x" . to_string( ) ] , false ) ;
846+
847+ // Logical OR patterns - typeof x === 'undefined' || x
848+ test_with_global_variables ( "typeof x === 'undefined' || x" , vec ! [ "x" . to_string( ) ] , false ) ;
849+ test_with_global_variables ( "typeof x == 'undefined' || x" , vec ! [ "x" . to_string( ) ] , false ) ;
850+ test_with_global_variables ( "'undefined' === typeof x || x" , vec ! [ "x" . to_string( ) ] , false ) ;
851+ test_with_global_variables ( "'undefined' == typeof x || x" , vec ! [ "x" . to_string( ) ] , false ) ;
852+
853+ // String comparison patterns - typeof x < 'u' && x
854+ test_with_global_variables ( "typeof x < 'u' && x" , vec ! [ "x" . to_string( ) ] , false ) ;
855+ test_with_global_variables ( "typeof x <= 'u' && x" , vec ! [ "x" . to_string( ) ] , false ) ;
856+ test_with_global_variables ( "'u' > typeof x && x" , vec ! [ "x" . to_string( ) ] , false ) ;
857+ test_with_global_variables ( "'u' >= typeof x && x" , vec ! [ "x" . to_string( ) ] , false ) ;
858+
859+ // Conditional patterns - typeof x === 'undefined' ? fallback : x
860+ test_with_global_variables ( "typeof x === 'undefined' ? 0 : x" , vec ! [ "x" . to_string( ) ] , false ) ;
861+ test_with_global_variables ( "typeof x == 'undefined' ? 0 : x" , vec ! [ "x" . to_string( ) ] , false ) ;
862+ test_with_global_variables ( "'undefined' === typeof x ? 0 : x" , vec ! [ "x" . to_string( ) ] , false ) ;
863+ test_with_global_variables ( "'undefined' == typeof x ? 0 : x" , vec ! [ "x" . to_string( ) ] , false ) ;
864+
865+ // Conditional patterns - typeof x !== 'undefined' ? x : fallback
866+ test_with_global_variables ( "typeof x !== 'undefined' ? x : 0" , vec ! [ "x" . to_string( ) ] , false ) ;
867+ test_with_global_variables ( "typeof x != 'undefined' ? x : 0" , vec ! [ "x" . to_string( ) ] , false ) ;
868+ test_with_global_variables ( "'undefined' !== typeof x ? x : 0" , vec ! [ "x" . to_string( ) ] , false ) ;
869+ test_with_global_variables ( "'undefined' != typeof x ? x : 0" , vec ! [ "x" . to_string( ) ] , false ) ;
870+
871+ // These should still have side effects because the fallback/other expressions have side effects
872+ test_with_global_variables ( "typeof x !== 'undefined' && (x + foo())" , vec ! [ "x" . to_string( ) ] , true ) ;
873+ test_with_global_variables ( "typeof x === 'undefined' || (x + foo())" , vec ! [ "x" . to_string( ) ] , true ) ;
874+ test_with_global_variables ( "typeof x === 'undefined' ? foo() : x" , vec ! [ "x" . to_string( ) ] , true ) ;
875+ test_with_global_variables ( "typeof x !== 'undefined' ? x : foo()" , vec ! [ "x" . to_string( ) ] , true ) ;
876+
877+ // These should still have side effects because the guard condition itself has side effects
878+ test_with_global_variables ( "typeof foo() !== 'undefined' && x" , vec ! [ "x" . to_string( ) ] , true ) ;
879+ test_with_global_variables ( "typeof foo() === 'undefined' || x" , vec ! [ "x" . to_string( ) ] , true ) ;
880+ test_with_global_variables ( "typeof foo() === 'undefined' ? 0 : x" , vec ! [ "x" . to_string( ) ] , true ) ;
881+
882+ // These should still have side effects because the variable name doesn't match
883+ test_with_global_variables ( "typeof y !== 'undefined' && x" , vec ! [ "x" . to_string( ) , "y" . to_string( ) ] , true ) ;
884+ test_with_global_variables ( "typeof y === 'undefined' || x" , vec ! [ "x" . to_string( ) , "y" . to_string( ) ] , true ) ;
885+ test_with_global_variables ( "typeof y === 'undefined' ? 0 : x" , vec ! [ "x" . to_string( ) , "y" . to_string( ) ] , true ) ;
886+
887+ // Test that accessing non-global variables is side-effect-free
888+ test ( "localVar" , false ) ;
889+
890+ // These should be side-effect-free when the variable is not declared as global
891+ // because such variables are treated as having no side effects when accessed
892+ test ( "typeof localVar !== 'undefined' && localVar" , false ) ;
893+ test ( "typeof localVar === 'undefined' || localVar" , false ) ;
894+ test ( "typeof localVar === 'undefined' ? 0 : localVar" , false ) ;
895+
896+ // Test multiple variables
897+ test_with_global_variables ( "typeof x !== 'undefined' && typeof y !== 'undefined' && x && y" , vec ! [ "x" . to_string( ) , "y" . to_string( ) ] , true ) ; // Still has side effects because both x and y are accessed
898+ test_with_global_variables ( "typeof x !== 'undefined' && x" , vec ! [ "x" . to_string( ) , "y" . to_string( ) ] , false ) ;
899+ }
0 commit comments