diff --git a/Zend/tests/type_declarations/gh14874.phpt b/Zend/tests/type_declarations/gh14874.phpt new file mode 100644 index 000000000000..70da4f1db861 --- /dev/null +++ b/Zend/tests/type_declarations/gh14874.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-14874 (Incorrect lineno for property and class const variance check) +--FILE-- + +--EXPECTF-- +Fatal error: Type of C::X must be compatible with P::X of type string in %s on line 4 diff --git a/Zend/tests/type_declarations/typed_properties_006.phpt b/Zend/tests/type_declarations/typed_properties_006.phpt index 71bd67166640..22c19eac8de0 100644 --- a/Zend/tests/type_declarations/typed_properties_006.phpt +++ b/Zend/tests/type_declarations/typed_properties_006.phpt @@ -11,4 +11,4 @@ class Bar extends Foo { } ?> --EXPECTF-- -Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 6 +Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 7 diff --git a/Zend/tests/type_declarations/typed_properties_007.phpt b/Zend/tests/type_declarations/typed_properties_007.phpt index 5d50a9db4fbd..ab68ab308b96 100644 --- a/Zend/tests/type_declarations/typed_properties_007.phpt +++ b/Zend/tests/type_declarations/typed_properties_007.phpt @@ -14,4 +14,4 @@ class Bar extends Foo { } ?> --EXPECTF-- -Fatal error: Type of Bar::$qux must be Whatever (as in class Foo) in %s on line 9 +Fatal error: Type of Bar::$qux must be Whatever (as in class Foo) in %s on line 10 diff --git a/Zend/tests/type_declarations/typed_properties_008.phpt b/Zend/tests/type_declarations/typed_properties_008.phpt index 529d549e6832..4abc3145a608 100644 --- a/Zend/tests/type_declarations/typed_properties_008.phpt +++ b/Zend/tests/type_declarations/typed_properties_008.phpt @@ -11,4 +11,4 @@ class Bar extends Foo { } ?> --EXPECTF-- -Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 6 +Fatal error: Type of Bar::$qux must be int (as in class Foo) in %s on line 7 diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 004b4fd81e98..5abe45aa9292 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -4561,6 +4561,7 @@ ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, z property_info->name = zend_new_interned_string(property_info->name); property_info->flags = access_type; + property_info->line = 0; property_info->doc_comment = doc_comment; property_info->attributes = NULL; property_info->prototype = property_info; @@ -4805,6 +4806,7 @@ ZEND_API zend_class_constant *zend_declare_typed_class_constant(zend_class_entry c->attributes = NULL; c->ce = ce; c->type = type; + c->line = 0; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ee57e1dafb87..a0b3ef69880a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8264,6 +8264,7 @@ static void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32 scope, name, &default_value, property_flags | (zend_property_is_virtual(scope, name, hooks_ast) ? ZEND_ACC_VIRTUAL : 0) | ZEND_ACC_PROMOTED, doc_comment, type); + prop->line = param_ast->lineno; if (hooks_ast) { const zend_ast_list *hooks = zend_ast_get_list(hooks_ast); 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 } info = zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type); + info->line = prop_ast->lineno; if (hooks_ast) { 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 } c = zend_declare_typed_class_constant(ce, name, &value_zv, flags, doc_comment, type); + c->line = const_ast->lineno; if (attr_ast) { 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) zend_class_constant *c = zend_declare_class_constant_ex(enum_class, enum_case_name, &value_zv, ZEND_ACC_PUBLIC, doc_comment); ZEND_CLASS_CONST_FLAGS(c) |= ZEND_CLASS_CONST_IS_CASE; + c->line = ast->lineno; zend_ast_destroy(const_enum_init_ast); zend_ast *attr_ast = ast->child[3]; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 0e31332c97f0..48803d6a6585 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -467,6 +467,7 @@ typedef struct _zend_property_info { uint32_t offset; /* property offset for object properties or property index for static properties */ uint32_t flags; + uint32_t line; zend_string *name; zend_string *doc_comment; HashTable *attributes; @@ -493,6 +494,7 @@ typedef struct _zend_class_constant { HashTable *attributes; zend_class_entry *ce; zend_type type; + uint32_t line; } zend_class_constant; #define ZEND_CLASS_CONST_FLAGS(c) Z_CONSTANT_FLAGS((c)->value) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index d70d08f5f1c4..4be096dc4586 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1308,27 +1308,48 @@ static inheritance_status full_property_types_compatible( static ZEND_COLD void emit_incompatible_property_error( const zend_property_info *child, const zend_property_info *parent, prop_variance variance) { zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce); - zend_error_noreturn(E_COMPILE_ERROR, - "Type of %s::$%s must be %s%s (as in class %s)", - ZSTR_VAL(child->ce->name), - zend_get_unmangled_property_name(child->name), - variance == PROP_INVARIANT ? "" : - variance == PROP_COVARIANT ? "subtype of " : "supertype of ", - ZSTR_VAL(type_str), - ZSTR_VAL(parent->ce->name)); + if (child->line && child->ce->type == ZEND_USER_CLASS) { + zend_error_at_noreturn(E_COMPILE_ERROR, child->ce->info.user.filename, child->line, + "Type of %s::$%s must be %s%s (as in class %s)", + ZSTR_VAL(child->ce->name), + zend_get_unmangled_property_name(child->name), + variance == PROP_INVARIANT ? "" : + variance == PROP_COVARIANT ? "subtype of " : "supertype of ", + ZSTR_VAL(type_str), + ZSTR_VAL(parent->ce->name)); + } else { + zend_error_noreturn(E_COMPILE_ERROR, + "Type of %s::$%s must be %s%s (as in class %s)", + ZSTR_VAL(child->ce->name), + zend_get_unmangled_property_name(child->name), + variance == PROP_INVARIANT ? "" : + variance == PROP_COVARIANT ? "subtype of " : "supertype of ", + ZSTR_VAL(type_str), + ZSTR_VAL(parent->ce->name)); + } } static ZEND_COLD void emit_set_hook_type_error(const zend_property_info *child, const zend_property_info *parent) { zend_type set_type = parent->hooks[ZEND_PROPERTY_HOOK_SET]->common.arg_info[0].type; zend_string *type_str = zend_type_to_string_resolved(set_type, parent->ce); - zend_error_noreturn(E_COMPILE_ERROR, - "Set type of %s::$%s must be supertype of %s (as in %s %s)", - ZSTR_VAL(child->ce->name), - zend_get_unmangled_property_name(child->name), - ZSTR_VAL(type_str), - zend_get_object_type_case(parent->ce, false), - ZSTR_VAL(parent->ce->name)); + if (child->line && child->ce->type == ZEND_USER_CLASS) { + zend_error_at_noreturn(E_COMPILE_ERROR, child->ce->info.user.filename, child->line, + "Set type of %s::$%s must be supertype of %s (as in %s %s)", + ZSTR_VAL(child->ce->name), + zend_get_unmangled_property_name(child->name), + ZSTR_VAL(type_str), + zend_get_object_type_case(parent->ce, false), + ZSTR_VAL(parent->ce->name)); + } else { + zend_error_noreturn(E_COMPILE_ERROR, + "Set type of %s::$%s must be supertype of %s (as in %s %s)", + ZSTR_VAL(child->ce->name), + zend_get_unmangled_property_name(child->name), + ZSTR_VAL(type_str), + zend_get_object_type_case(parent->ce, false), + ZSTR_VAL(parent->ce->name)); + } } 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 static void emit_incompatible_class_constant_error( const zend_class_constant *child, const zend_class_constant *parent, const zend_string *const_name) { zend_string *type_str = zend_type_to_string_resolved(parent->type, parent->ce); - zend_error_noreturn(E_COMPILE_ERROR, - "Type of %s::%s must be compatible with %s::%s of type %s", - ZSTR_VAL(child->ce->name), - ZSTR_VAL(const_name), - ZSTR_VAL(parent->ce->name), - ZSTR_VAL(const_name), - ZSTR_VAL(type_str)); + if (child->line && child->ce->type == ZEND_USER_CLASS) { + zend_error_at_noreturn(E_COMPILE_ERROR, child->ce->info.user.filename, child->line, + "Type of %s::%s must be compatible with %s::%s of type %s", + ZSTR_VAL(child->ce->name), + ZSTR_VAL(const_name), + ZSTR_VAL(parent->ce->name), + ZSTR_VAL(const_name), + ZSTR_VAL(type_str)); + } else { + zend_error_noreturn(E_COMPILE_ERROR, + "Type of %s::%s must be compatible with %s::%s of type %s", + ZSTR_VAL(child->ce->name), + ZSTR_VAL(const_name), + ZSTR_VAL(parent->ce->name), + ZSTR_VAL(const_name), + ZSTR_VAL(type_str)); + } } static inheritance_status class_constant_types_compatible(const zend_class_constant *parent, const zend_class_constant *child)