Skip to content

Commit 9e09782

Browse files
committed
Fix lineno for all constant expressions
Fixes GH-8821 Closes GH-8855
1 parent c8cc355 commit 9e09782

17 files changed

+111
-12
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ PHP NEWS
2020
effects). (ilutov)
2121
. Implement GH-10217 (Use strlen() for determining the class_name length).
2222
(Dennis Buteyn)
23+
. Fix bug GH-8821 (Improve line numbers for errors in constant expressions).
24+
(ilutov)
2325

2426
- Exif:
2527
. Removed unneeded codepaths in exif_process_TIFF_in_JPEG(). (nielsdos)

Zend/tests/bug41633_2.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ echo Foo::A."\n";
1010
--EXPECTF--
1111
Fatal error: Uncaught Error: Undefined constant self::B in %s:%d
1212
Stack trace:
13-
#0 {main}
13+
#0 %s(%d): [constant expression]()
14+
#1 {main}
1415
thrown in %sbug41633_2.php on line 3

Zend/tests/bug41633_3.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ echo Foo::A;
1111
--EXPECTF--
1212
Fatal error: Uncaught Error: Cannot declare self-referencing constant Foo::B in %s:%d
1313
Stack trace:
14-
#0 {main}
14+
#0 %s(%d): [constant expression]()
15+
#1 {main}
1516
thrown in %sbug41633_3.php on line %d

Zend/tests/bug47572.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ $foo = new Foo();
1616
--EXPECTF--
1717
Fatal error: Uncaught Error: Undefined constant "FOO" in %s:%d
1818
Stack trace:
19-
#0 {main}
19+
#0 %s(%d): [constant expression]()
20+
#1 {main}
2021
thrown in %s on line %d

Zend/tests/bug74657.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ var_dump((new C)->options);
2121
--EXPECTF--
2222
Fatal error: Uncaught Error: Undefined constant I::FOO in %s:%d
2323
Stack trace:
24-
#0 {main}
24+
#0 %s(%d): [constant expression]()
25+
#1 {main}
2526
thrown in %sbug74657.php on line %d

Zend/tests/constant_expressions_self_referencing_array.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ var_dump(A::FOO);
1111
--EXPECTF--
1212
Fatal error: Uncaught Error: Cannot declare self-referencing constant self::BAR in %s:%d
1313
Stack trace:
14-
#0 {main}
14+
#0 %s(%d): [constant expression]()
15+
#1 {main}
1516
thrown in %s on line %d

Zend/tests/enum/offsetGet-in-const-expr.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ var_dump(X::FOO_BAR);
2525
--EXPECTF--
2626
Fatal error: Uncaught Error: Cannot use [] on objects in constant expression in %s:%d
2727
Stack trace:
28-
#0 {main}
28+
#0 %s(%d): [constant expression]()
29+
#1 {main}
2930
thrown in %s on line %d

Zend/tests/gh7771_1.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ new Foo();
1111
--EXPECTF--
1212
Fatal error: Uncaught Error: Class "NonExistent" not found in %sgh7771_1_definition.inc:4
1313
Stack trace:
14-
#0 {main}
14+
#0 %sgh7771_1.php(5): [constant expression]()
15+
#1 {main}
1516
thrown in %sgh7771_1_definition.inc on line 4

Zend/tests/gh7771_2.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ new Foo();
1111
--EXPECTF--
1212
Fatal error: Uncaught Error: Class "NonExistent" not found in %sgh7771_2_definition.inc:6
1313
Stack trace:
14-
#0 {main}
14+
#0 %sgh7771_2.php(5): [constant expression]()
15+
#1 {main}
1516
thrown in %sgh7771_2_definition.inc on line 6

Zend/tests/gh8821.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
GH-8821: Fix reported line number of constant expression
3+
--FILE--
4+
<?php
5+
6+
enum Alpha {
7+
case Foo;
8+
}
9+
10+
class Bravo {
11+
public const C = [Alpha::Foo => 3];
12+
}
13+
14+
new Bravo();
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Uncaught TypeError: Illegal offset type in %sgh8821.php:8
19+
Stack trace:
20+
#0 %sgh8821.php(11): [constant expression]()
21+
#1 {main}
22+
thrown in %sgh8821.php on line 8

Zend/tests/type_declarations/typed_properties_022.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ $foo = new Foo();
1111
--EXPECTF--
1212
Fatal error: Uncaught Error: Class "BAR" not found in %s:%d
1313
Stack trace:
14-
#0 {main}
14+
#0 %s(%d): [constant expression]()
15+
#1 {main}
1516
thrown in %s on line %d

Zend/zend_ast.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,12 +496,43 @@ zend_class_entry *zend_ast_fetch_class(zend_ast *ast, zend_class_entry *scope)
496496
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);
497497
}
498498

499+
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
500+
zval *result,
501+
zend_ast *ast,
502+
zend_class_entry *scope,
503+
bool *short_circuited_ptr,
504+
zend_ast_evaluate_ctx *ctx
505+
);
506+
499507
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_ex(
500508
zval *result,
501509
zend_ast *ast,
502510
zend_class_entry *scope,
503511
bool *short_circuited_ptr,
504512
zend_ast_evaluate_ctx *ctx
513+
) {
514+
zend_string *previous_filename;
515+
zend_long previous_lineno;
516+
if (scope) {
517+
previous_filename = EG(filename_override);
518+
previous_lineno = EG(lineno_override);
519+
EG(filename_override) = scope->info.user.filename;
520+
EG(lineno_override) = zend_ast_get_lineno(ast);
521+
}
522+
zend_result r = zend_ast_evaluate_inner(result, ast, scope, short_circuited_ptr, ctx);
523+
if (scope) {
524+
EG(filename_override) = previous_filename;
525+
EG(lineno_override) = previous_lineno;
526+
}
527+
return r;
528+
}
529+
530+
ZEND_API zend_result ZEND_FASTCALL zend_ast_evaluate_inner(
531+
zval *result,
532+
zend_ast *ast,
533+
zend_class_entry *scope,
534+
bool *short_circuited_ptr,
535+
zend_ast_evaluate_ctx *ctx
505536
) {
506537
zval op1, op2;
507538
zend_result ret = SUCCESS;
@@ -1023,11 +1054,13 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
10231054
new->attr = ast->attr;
10241055
ZVAL_COPY(&new->val, zend_ast_get_zval(ast));
10251056
buf = (void*)((char*)buf + sizeof(zend_ast_zval));
1057+
// Lineno gets copied with ZVAL_COPY
10261058
} else if (ast->kind == ZEND_AST_CONSTANT) {
10271059
zend_ast_zval *new = (zend_ast_zval*)buf;
10281060
new->kind = ZEND_AST_CONSTANT;
10291061
new->attr = ast->attr;
10301062
ZVAL_STR_COPY(&new->val, zend_ast_get_constant_name(ast));
1063+
Z_LINENO(new->val) = zend_ast_get_lineno(ast);
10311064
buf = (void*)((char*)buf + sizeof(zend_ast_zval));
10321065
} else if (zend_ast_is_list(ast)) {
10331066
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)
10361069
new->kind = list->kind;
10371070
new->attr = list->attr;
10381071
new->children = list->children;
1072+
new->lineno = list->lineno;
10391073
buf = (void*)((char*)buf + zend_ast_list_size(list->children));
10401074
for (i = 0; i < list->children; i++) {
10411075
if (list->child[i]) {

Zend/zend_ast.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,9 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) {
352352
if (ast->kind == ZEND_AST_ZVAL) {
353353
zval *zv = zend_ast_get_zval(ast);
354354
return Z_LINENO_P(zv);
355+
} else if (ast->kind == ZEND_AST_CONSTANT) {
356+
zval *zv = &((zend_ast_zval *) ast)->val;
357+
return Z_LINENO_P(zv);
355358
} else {
356359
return ast->lineno;
357360
}

Zend/zend_builtin_functions.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,32 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
17241724
return;
17251725
}
17261726

1727+
if (EG(filename_override)) {
1728+
// Add the current execution point to the frame so we don't lose it
1729+
zend_string *filename_override = EG(filename_override);
1730+
zend_long lineno_override = EG(lineno_override);
1731+
EG(filename_override) = NULL;
1732+
EG(lineno_override) = -1;
1733+
1734+
zend_string *filename = zend_get_executed_filename_ex();
1735+
zend_long lineno = zend_get_executed_lineno();
1736+
if (filename && (!zend_string_equals(filename, filename_override) || lineno != lineno_override)) {
1737+
stack_frame = zend_new_array(8);
1738+
zend_hash_real_init_mixed(stack_frame);
1739+
ZVAL_STR_COPY(&tmp, filename);
1740+
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FILE), &tmp, 1);
1741+
ZVAL_LONG(&tmp, lineno);
1742+
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_LINE), &tmp, 1);
1743+
ZVAL_STR_COPY(&tmp, ZSTR_KNOWN(ZEND_STR_CONST_EXPR_PLACEHOLDER));
1744+
_zend_hash_append_ex(stack_frame, ZSTR_KNOWN(ZEND_STR_FUNCTION), &tmp, 1);
1745+
ZVAL_ARR(&tmp, stack_frame);
1746+
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
1747+
}
1748+
1749+
EG(filename_override) = filename_override;
1750+
EG(lineno_override) = lineno_override;
1751+
}
1752+
17271753
if (skip_last) {
17281754
/* skip debug_backtrace() */
17291755
call = call->prev_execute_data;

Zend/zend_string.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ EMPTY_SWITCH_DEFAULT_CASE()
594594
_(ZEND_STR_AUTOGLOBAL_REQUEST, "_REQUEST") \
595595
_(ZEND_STR_COUNT, "count") \
596596
_(ZEND_STR_SENSITIVEPARAMETER, "SensitiveParameter") \
597+
_(ZEND_STR_CONST_EXPR_PLACEHOLDER, "[constant expression]") \
597598

598599

599600
typedef enum _zend_known_string_id {

ext/standard/tests/streams/bug77664.phpt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ file_get_contents('error://test');
1212
--EXPECTF--
1313
Fatal error: Uncaught Error: Undefined constant self::INVALID in %s:%d
1414
Stack trace:
15-
#0 %sbug77664.php(%d): file_get_contents('error://test')
16-
#1 {main}
15+
#0 %s(%d): [constant expression]()
16+
#1 %s(%d): file_get_contents('error://test')
17+
#2 {main}
1718
thrown in %sbug77664.php on line %d

tests/classes/constants_error_004.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ Class constant whose initial value references a non-existent class
1212
--EXPECTF--
1313
Fatal error: Uncaught Error: Class "D" not found in %s:%d
1414
Stack trace:
15-
#0 {main}
15+
#0 %s(%d): [constant expression]()
16+
#1 {main}
1617
thrown in %s on line %d

0 commit comments

Comments
 (0)