Fix lineno for all constant expressions

Fixes GH-8821
Closes GH-8855
This commit is contained in:
Ilija Tovilo 2022-08-04 14:12:45 +02:00
parent c8cc355070
commit 9e097822e8
No known key found for this signature in database
GPG key ID: A4F5D403F118200A
17 changed files with 111 additions and 12 deletions

2
NEWS
View file

@ -20,6 +20,8 @@ PHP NEWS
effects). (ilutov)
. Implement GH-10217 (Use strlen() for determining the class_name length).
(Dennis Buteyn)
. Fix bug GH-8821 (Improve line numbers for errors in constant expressions).
(ilutov)
- Exif:
. Removed unneeded codepaths in exif_process_TIFF_in_JPEG(). (nielsdos)

View file

@ -10,5 +10,6 @@ echo Foo::A."\n";
--EXPECTF--
Fatal error: Uncaught Error: Undefined constant self::B in %s:%d
Stack trace:
#0 {main}
#0 %s(%d): [constant expression]()
#1 {main}
thrown in %sbug41633_2.php on line 3

View file

@ -11,5 +11,6 @@ echo Foo::A;
--EXPECTF--
Fatal error: Uncaught Error: Cannot declare self-referencing constant Foo::B in %s:%d
Stack trace:
#0 {main}
#0 %s(%d): [constant expression]()
#1 {main}
thrown in %sbug41633_3.php on line %d

View file

@ -16,5 +16,6 @@ $foo = new Foo();
--EXPECTF--
Fatal error: Uncaught Error: Undefined constant "FOO" in %s:%d
Stack trace:
#0 {main}
#0 %s(%d): [constant expression]()
#1 {main}
thrown in %s on line %d

View file

@ -21,5 +21,6 @@ var_dump((new C)->options);
--EXPECTF--
Fatal error: Uncaught Error: Undefined constant I::FOO in %s:%d
Stack trace:
#0 {main}
#0 %s(%d): [constant expression]()
#1 {main}
thrown in %sbug74657.php on line %d

View file

@ -11,5 +11,6 @@ var_dump(A::FOO);
--EXPECTF--
Fatal error: Uncaught Error: Cannot declare self-referencing constant self::BAR in %s:%d
Stack trace:
#0 {main}
#0 %s(%d): [constant expression]()
#1 {main}
thrown in %s on line %d

View file

@ -25,5 +25,6 @@ var_dump(X::FOO_BAR);
--EXPECTF--
Fatal error: Uncaught Error: Cannot use [] on objects in constant expression in %s:%d
Stack trace:
#0 {main}
#0 %s(%d): [constant expression]()
#1 {main}
thrown in %s on line %d

View file

@ -11,5 +11,6 @@ new Foo();
--EXPECTF--
Fatal error: Uncaught Error: Class "NonExistent" not found in %sgh7771_1_definition.inc:4
Stack trace:
#0 {main}
#0 %sgh7771_1.php(5): [constant expression]()
#1 {main}
thrown in %sgh7771_1_definition.inc on line 4

View file

@ -11,5 +11,6 @@ new Foo();
--EXPECTF--
Fatal error: Uncaught Error: Class "NonExistent" not found in %sgh7771_2_definition.inc:6
Stack trace:
#0 {main}
#0 %sgh7771_2.php(5): [constant expression]()
#1 {main}
thrown in %sgh7771_2_definition.inc on line 6

22
Zend/tests/gh8821.phpt Normal file
View file

@ -0,0 +1,22 @@
--TEST--
GH-8821: Fix reported line number of constant expression
--FILE--
<?php
enum Alpha {
case Foo;
}
class Bravo {
public const C = [Alpha::Foo => 3];
}
new Bravo();
?>
--EXPECTF--
Fatal error: Uncaught TypeError: Illegal offset type in %sgh8821.php:8
Stack trace:
#0 %sgh8821.php(11): [constant expression]()
#1 {main}
thrown in %sgh8821.php on line 8

View file

@ -11,5 +11,6 @@ $foo = new Foo();
--EXPECTF--
Fatal error: Uncaught Error: Class "BAR" not found in %s:%d
Stack trace:
#0 {main}
#0 %s(%d): [constant expression]()
#1 {main}
thrown in %s on line %d

View file

@ -496,12 +496,43 @@ zend_class_entry *zend_ast_fetch_class(zend_ast *ast, zend_class_entry *scope)
return zend_fetch_class_with_scope(zend_ast_get_str(ast), (ast->attr >> ZEND_CONST_EXPR_NEW_FETCH_TYPE_SHIFT) | ZEND_FETCH_CLASS_EXCEPTION, scope);
}
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
zval *result,
zend_ast *ast,
zend_class_entry *scope,
bool *short_circuited_ptr,
zend_ast_evaluate_ctx *ctx
);
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(
zval *result,
zend_ast *ast,
zend_class_entry *scope,
bool *short_circuited_ptr,
zend_ast_evaluate_ctx *ctx
) {
zend_string *previous_filename;
zend_long previous_lineno;
if (scope) {
previous_filename = EG(filename_override);
previous_lineno = EG(lineno_override);
EG(filename_override) = scope->info.user.filename;
EG(lineno_override) = zend_ast_get_lineno(ast);
}
zend_result r = zend_ast_evaluate_inner(result, ast, scope, short_circuited_ptr, ctx);
if (scope) {
EG(filename_override) = previous_filename;
EG(lineno_override) = previous_lineno;
}
return r;
}
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
zval *result,
zend_ast *ast,
zend_class_entry *scope,
bool *short_circuited_ptr,
zend_ast_evaluate_ctx *ctx
) {
zval op1, op2;
zend_result ret = SUCCESS;
@ -1023,11 +1054,13 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
new->attr = ast->attr;
ZVAL_COPY(&new->val, zend_ast_get_zval(ast));
buf = (void*)((char*)buf + sizeof(zend_ast_zval));
// Lineno gets copied with ZVAL_COPY
} else if (ast->kind == ZEND_AST_CONSTANT) {
zend_ast_zval *new = (zend_ast_zval*)buf;
new->kind = ZEND_AST_CONSTANT;
new->attr = ast->attr;
ZVAL_STR_COPY(&new->val, zend_ast_get_constant_name(ast));
Z_LINENO(new->val) = zend_ast_get_lineno(ast);
buf = (void*)((char*)buf + sizeof(zend_ast_zval));
} else if (zend_ast_is_list(ast)) {
zend_ast_list *list = zend_ast_get_list(ast);
@ -1036,6 +1069,7 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
new->kind = list->kind;
new->attr = list->attr;
new->children = list->children;
new->lineno = list->lineno;
buf = (void*)((char*)buf + zend_ast_list_size(list->children));
for (i = 0; i < list->children; i++) {
if (list->child[i]) {

View file

@ -352,6 +352,9 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) {
if (ast->kind == ZEND_AST_ZVAL) {
zval *zv = zend_ast_get_zval(ast);
return Z_LINENO_P(zv);
} else if (ast->kind == ZEND_AST_CONSTANT) {
zval *zv = &((zend_ast_zval *) ast)->val;
return Z_LINENO_P(zv);
} else {
return ast->lineno;
}

View file

@ -1724,6 +1724,32 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
return;
}
if (EG(filename_override)) {
// Add the current execution point to the frame so we don't lose it
zend_string *filename_override = EG(filename_override);
zend_long lineno_override = EG(lineno_override);
EG(filename_override) = NULL;
EG(lineno_override) = -1;
zend_string *filename = zend_get_executed_filename_ex();
zend_long lineno = zend_get_executed_lineno();
if (filename && (!zend_string_equals(filename, filename_override) || lineno != lineno_override)) {
stack_frame = zend_new_array(8);
zend_hash_real_init_mixed(stack_frame);
ZVAL_STR_COPY(&tmp, filename);
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1);
ZVAL_LONG(&tmp, lineno);
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp, 1);
ZVAL_STR_COPY(&tmp, ZSTR_KNOWN(ZEND_STR_CONST_EXPR_PLACEHOLDER));
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1);
ZVAL_ARR(&tmp, stack_frame);
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
}
EG(filename_override) = filename_override;
EG(lineno_override) = lineno_override;
}
if (skip_last) {
/* skip debug_backtrace() */
call = call->prev_execute_data;

View file

@ -594,6 +594,7 @@ EMPTY_SWITCH_DEFAULT_CASE()
_(ZEND_STR_AUTOGLOBAL_REQUEST, "_REQUEST") \
_(ZEND_STR_COUNT, "count") \
_(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \
_(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \
typedef enum _zend_known_string_id {

View file

@ -12,6 +12,7 @@ file_get_contents('error://test');
--EXPECTF--
Fatal error: Uncaught Error: Undefined constant self::INVALID in %s:%d
Stack trace:
#0 %sbug77664.php(%d): file_get_contents('error://test')
#1 {main}
#0 %s(%d): [constant expression]()
#1 %s(%d): file_get_contents('error://test')
#2 {main}
thrown in %sbug77664.php on line %d

View file

@ -12,5 +12,6 @@ Class constant whose initial value references a non-existent class
--EXPECTF--
Fatal error: Uncaught Error: Class "D" not found in %s:%d
Stack trace:
#0 {main}
#0 %s(%d): [constant expression]()
#1 {main}
thrown in %s on line %d