diff --git a/NEWS b/NEWS index 858ca396189a..109653a2cb6d 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,8 @@ PHP NEWS in string interpolation). (timwolla) . Fixed bug GH-22373 (AST pretty-printing drops meaningful parentheses surrounding property access). (timwolla) + . Fixed bug GH-22387 (AST pretty-printing drops meaningful parentheses around + RHS of instanceof). (timwolla) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 57faedc06f9b..68bacf2773f5 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1691,6 +1691,19 @@ static ZEND_COLD void zend_ast_export_ns_name(smart_str *str, zend_ast *ast, int } zend_ast_export_ex(str, ast, priority, indent); } +static ZEND_COLD void zend_ast_export_ns_name_or_expression(smart_str *str, zend_ast *ast, int priority, int indent) +{ + switch (ast->kind) { + case ZEND_AST_ZVAL: + case ZEND_AST_VAR: + zend_ast_export_ns_name(str, ast, priority, indent); + break; + default: + smart_str_appendc(str, '('); + zend_ast_export_ex(str, ast, priority, indent); + smart_str_appendc(str, ')'); + } +} static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len) { @@ -2529,25 +2542,12 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_var(str, ast->child[1], indent); break; case ZEND_AST_STATIC_PROP: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appends(str, "::$"); zend_ast_export_var(str, ast->child[1], indent); break; case ZEND_AST_CALL: { - zend_ast *left = ast->child[0]; - switch (left->kind) { - /* ZEND_AST_ZVAL is a regular function call. */ - case ZEND_AST_ZVAL: - /* ZEND_AST_VAR ($foo()) is unambiguous without parens. */ - case ZEND_AST_VAR: - zend_ast_export_ns_name(str, left, 0, indent); - break; - default: - smart_str_appendc(str, '('); - zend_ast_export_ex(str, left, 0, indent); - smart_str_appendc(str, ')'); - break; - } + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2559,7 +2559,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio goto simple_list; } case ZEND_AST_CLASS_CONST: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appends(str, "::"); zend_ast_export_name(str, ast->child[1], 0, indent); break; @@ -2576,7 +2576,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio default: ZEND_UNREACHABLE(); } } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); } smart_str_appends(str, "::class"); break; @@ -2655,7 +2655,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio } zend_ast_export_class_no_header(str, decl, indent); } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); @@ -2664,7 +2664,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_INSTANCEOF: zend_ast_export_ex(str, ast->child[0], 0, indent); smart_str_appends(str, " instanceof "); - zend_ast_export_ns_name(str, ast->child[1], 0, indent); + zend_ast_export_ns_name_or_expression(str, ast->child[1], 0, indent); break; case ZEND_AST_YIELD: if (priority > 70) smart_str_appendc(str, '('); @@ -2856,14 +2856,36 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_ex(str, ast->child[2], 0, indent); smart_str_appendc(str, ')'); break; - case ZEND_AST_STATIC_CALL: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + case ZEND_AST_STATIC_CALL: { + zend_ast *class_ast = ast->child[0]; + zend_ast *method_ast = ast->child[1]; + + /* see zend_compile_parent_property_hook_call() */ + bool parent_hook = !( + class_ast->kind != ZEND_AST_STATIC_PROP + || (class_ast->attr & ZEND_PARENTHESIZED_STATIC_PROP) + || class_ast->child[0]->kind != ZEND_AST_ZVAL + || Z_TYPE_P(zend_ast_get_zval(class_ast->child[0])) != IS_STRING + || zend_get_class_fetch_type(zend_ast_get_str(class_ast->child[0])) != ZEND_FETCH_CLASS_PARENT + || class_ast->child[1]->kind != ZEND_AST_ZVAL + || method_ast->kind != ZEND_AST_ZVAL + || Z_TYPE_P(zend_ast_get_zval(method_ast)) != IS_STRING + || (!zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "get") + && !zend_string_equals_literal_ci(zend_ast_get_str(method_ast), "set")) + ); + if (parent_hook) { + zend_ast_export_ns_name(str, class_ast, 0, indent); + } else { + zend_ast_export_ns_name_or_expression(str, class_ast, 0, indent); + } + smart_str_appends(str, "::"); - zend_ast_export_var(str, ast->child[1], indent); + zend_ast_export_var(str, method_ast, indent); smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[2], 0, indent); smart_str_appendc(str, ')'); break; + } case ZEND_AST_CONDITIONAL: if (priority > 100) smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[0], 100, indent); diff --git a/ext/standard/tests/assert/gh22387.phpt b/ext/standard/tests/assert/gh22387.phpt new file mode 100644 index 000000000000..38c93921608a --- /dev/null +++ b/ext/standard/tests/assert/gh22387.phpt @@ -0,0 +1,62 @@ +--TEST-- +GH-22387: AST pretty-printing drops meaningful parentheses around RHS of instanceof +--FILE-- +getMessage(), PHP_EOL; +} + +try { + assert(!new (bar)()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(!(bar)::m()); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(!(bar)::$p); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(!(bar)::C); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert((baz)::class !== 'stdClass'); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +assert(!$foo instanceof (bar)) +assert(!new (bar)()) +assert(!(bar)::m()) +assert(!(bar)::$p) +assert(!(bar)::C) +assert((baz)::class !== 'stdClass')