Skip to content

Commit 4525613

Browse files
committed
Fix GH-14874: report correct line for property/const variance errors
Add a uint32_t line field to zend_property_info and zend_class_constant, populated from the AST node lineno at compile time. The variance emitters route through zend_error_at_noreturn so the error points at the offending child member rather than the class declaration. Members left at line 0 (internal classes, trait-copied properties) fall back to zend_error_noreturn (legacy behavior, no regression). Fixes GH-14874
1 parent fe52e5b commit 4525613

8 files changed

Lines changed: 80 additions & 25 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
GH-14874 (Incorrect lineno for property and class const variance check)
3+
--FILE--
4+
<?php
5+
6+
class C extends P {
7+
public const int X = 42;
8+
}
9+
10+
class P {
11+
public const string X = 'X';
12+
}
13+
14+
?>
15+
--EXPECTF--
16+
Fatal error: Type of C::X must be compatible with P::X of type string in %s on line 4

Zend/tests/type_declarations/typed_properties_006.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ class Bar extends Foo {
1111
}
1212
?>
1313
--EXPECTF--
14-
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 6
14+
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 7

Zend/tests/type_declarations/typed_properties_007.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ class Bar extends Foo {
1414
}
1515
?>
1616
--EXPECTF--
17-
Fatal error: Type of Bar::$qux must be Whatever (as in class Foo) in %s on line 9
17+
Fatal error: Type of Bar::$qux must be Whatever (as in class Foo) in %s on line 10

Zend/tests/type_declarations/typed_properties_008.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ class Bar extends Foo {
1111
}
1212
?>
1313
--EXPECTF--
14-
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 6
14+
Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 7

Zend/zend_API.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4561,6 +4561,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z
45614561

45624562
property_info->name = zend_new_interned_string(property_info->name);
45634563
property_info->flags = access_type;
4564+
property_info->line = 0;
45644565
property_info->doc_comment = doc_comment;
45654566
property_info->attributes = NULL;
45664567
property_info->prototype = property_info;
@@ -4805,6 +4806,7 @@ ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry
48054806
c->attributes = NULL;
48064807
c->ce = ce;
48074808
c->type = type;
4809+
c->line = 0;
48084810

48094811
if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
48104812
ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;

Zend/zend_compile.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8264,6 +8264,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32
82648264
scope, name, &default_value,
82658265
property_flags | (zend_property_is_virtual(scope, name, hooks_ast) ? ZEND_ACC_VIRTUAL : 0) | ZEND_ACC_PROMOTED,
82668266
doc_comment, type);
8267+
prop->line = param_ast->lineno;
82678268
if (hooks_ast) {
82688269
const zend_ast_list *hooks = zend_ast_get_list(hooks_ast);
82698270
zend_compile_property_hooks(prop, name, type_ast, hooks);
@@ -9253,6 +9254,7 @@ static void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t f
92539254
}
92549255

92559256
info = zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type);
9257+
info->line = prop_ast->lineno;
92569258

92579259
if (hooks_ast) {
92589260
zend_compile_property_hooks(info, name, type_ast, zend_ast_get_list(hooks_ast));
@@ -9339,6 +9341,7 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as
93399341
}
93409342

93419343
c = zend_declare_typed_class_constant(ce, name, &value_zv, flags, doc_comment, type);
9344+
c->line = const_ast->lineno;
93429345

93439346
if (attr_ast) {
93449347
zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST, 0);
@@ -9800,6 +9803,7 @@ static void zend_compile_enum_case(zend_ast *ast)
98009803

98019804
zend_class_constant *c = zend_declare_class_constant_ex(enum_class, enum_case_name, &value_zv, ZEND_ACC_PUBLIC, doc_comment);
98029805
ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE;
9806+
c->line = ast->lineno;
98039807
zend_ast_destroy(const_enum_init_ast);
98049808

98059809
zend_ast *attr_ast = ast->child[3];

Zend/zend_compile.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ typedef struct _zend_property_info {
467467
uint32_t offset; /* property offset for object properties or
468468
property index for static properties */
469469
uint32_t flags;
470+
uint32_t line;
470471
zend_string *name;
471472
zend_string *doc_comment;
472473
HashTable *attributes;
@@ -493,6 +494,7 @@ typedef struct _zend_class_constant {
493494
HashTable *attributes;
494495
zend_class_entry *ce;
495496
zend_type type;
497+
uint32_t line;
496498
} zend_class_constant;
497499

498500
#define ZEND_CLASS_CONST_FLAGS(c) Z_CONSTANT_FLAGS((c)->value)

Zend/zend_inheritance.c

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,27 +1308,48 @@ static inheritance_status full_property_types_compatible(
13081308
static ZEND_COLD void emit_incompatible_property_error(
13091309
const zend_property_info *child, const zend_property_info *parent, prop_variance variance) {
13101310
zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce);
1311-
zend_error_noreturn(E_COMPILE_ERROR,
1312-
"Type of %s::$%s must be %s%s (as in class %s)",
1313-
ZSTR_VAL(child->ce->name),
1314-
zend_get_unmangled_property_name(child->name),
1315-
variance == PROP_INVARIANT ? "" :
1316-
variance == PROP_COVARIANT ? "subtype of " : "supertype of ",
1317-
ZSTR_VAL(type_str),
1318-
ZSTR_VAL(parent->ce->name));
1311+
if (child->line && child->ce->type == ZEND_USER_CLASS) {
1312+
zend_error_at_noreturn(E_COMPILE_ERROR, child->ce->info.user.filename, child->line,
1313+
"Type of %s::$%s must be %s%s (as in class %s)",
1314+
ZSTR_VAL(child->ce->name),
1315+
zend_get_unmangled_property_name(child->name),
1316+
variance == PROP_INVARIANT ? "" :
1317+
variance == PROP_COVARIANT ? "subtype of " : "supertype of ",
1318+
ZSTR_VAL(type_str),
1319+
ZSTR_VAL(parent->ce->name));
1320+
} else {
1321+
zend_error_noreturn(E_COMPILE_ERROR,
1322+
"Type of %s::$%s must be %s%s (as in class %s)",
1323+
ZSTR_VAL(child->ce->name),
1324+
zend_get_unmangled_property_name(child->name),
1325+
variance == PROP_INVARIANT ? "" :
1326+
variance == PROP_COVARIANT ? "subtype of " : "supertype of ",
1327+
ZSTR_VAL(type_str),
1328+
ZSTR_VAL(parent->ce->name));
1329+
}
13191330
}
13201331

13211332
static ZEND_COLD void emit_set_hook_type_error(const zend_property_info *child, const zend_property_info *parent)
13221333
{
13231334
zend_type set_type = parent->hooks[ZEND_PROPERTY_HOOK_SET]->common.arg_info[0].type;
13241335
zend_string *type_str = zend_type_to_string_resolved(set_type, parent->ce);
1325-
zend_error_noreturn(E_COMPILE_ERROR,
1326-
"Set type of %s::$%s must be supertype of %s (as in %s %s)",
1327-
ZSTR_VAL(child->ce->name),
1328-
zend_get_unmangled_property_name(child->name),
1329-
ZSTR_VAL(type_str),
1330-
zend_get_object_type_case(parent->ce, false),
1331-
ZSTR_VAL(parent->ce->name));
1336+
if (child->line && child->ce->type == ZEND_USER_CLASS) {
1337+
zend_error_at_noreturn(E_COMPILE_ERROR, child->ce->info.user.filename, child->line,
1338+
"Set type of %s::$%s must be supertype of %s (as in %s %s)",
1339+
ZSTR_VAL(child->ce->name),
1340+
zend_get_unmangled_property_name(child->name),
1341+
ZSTR_VAL(type_str),
1342+
zend_get_object_type_case(parent->ce, false),
1343+
ZSTR_VAL(parent->ce->name));
1344+
} else {
1345+
zend_error_noreturn(E_COMPILE_ERROR,
1346+
"Set type of %s::$%s must be supertype of %s (as in %s %s)",
1347+
ZSTR_VAL(child->ce->name),
1348+
zend_get_unmangled_property_name(child->name),
1349+
ZSTR_VAL(type_str),
1350+
zend_get_object_type_case(parent->ce, false),
1351+
ZSTR_VAL(parent->ce->name));
1352+
}
13321353
}
13331354

13341355
static inheritance_status verify_property_type_compatibility(
@@ -1621,13 +1642,23 @@ static void zend_do_inherit_interfaces(zend_class_entry *ce, const zend_class_en
16211642
static void emit_incompatible_class_constant_error(
16221643
const zend_class_constant *child, const zend_class_constant *parent, const zend_string *const_name) {
16231644
zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce);
1624-
zend_error_noreturn(E_COMPILE_ERROR,
1625-
"Type of %s::%s must be compatible with %s::%s of type %s",
1626-
ZSTR_VAL(child->ce->name),
1627-
ZSTR_VAL(const_name),
1628-
ZSTR_VAL(parent->ce->name),
1629-
ZSTR_VAL(const_name),
1630-
ZSTR_VAL(type_str));
1645+
if (child->line && child->ce->type == ZEND_USER_CLASS) {
1646+
zend_error_at_noreturn(E_COMPILE_ERROR, child->ce->info.user.filename, child->line,
1647+
"Type of %s::%s must be compatible with %s::%s of type %s",
1648+
ZSTR_VAL(child->ce->name),
1649+
ZSTR_VAL(const_name),
1650+
ZSTR_VAL(parent->ce->name),
1651+
ZSTR_VAL(const_name),
1652+
ZSTR_VAL(type_str));
1653+
} else {
1654+
zend_error_noreturn(E_COMPILE_ERROR,
1655+
"Type of %s::%s must be compatible with %s::%s of type %s",
1656+
ZSTR_VAL(child->ce->name),
1657+
ZSTR_VAL(const_name),
1658+
ZSTR_VAL(parent->ce->name),
1659+
ZSTR_VAL(const_name),
1660+
ZSTR_VAL(type_str));
1661+
}
16311662
}
16321663

16331664
static inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child)

0 commit comments

Comments
 (0)