-
-
Notifications
You must be signed in to change notification settings - Fork 22.8k
GDScript: Be lenient with member names shadowing native classes #108108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Doesn't work properly with variable type hints: extends Node
const Node3D = preload("res://a.gd")
var a: Node3D = Node3D.new() # Incompatible, typehint is the native type
var Node2D = Viewport
var b: Node2D # Should throw an errors since the variable is not constant, but does not |
This improves backward compatibility, since if we introduce a new class that matches an existing member, it will still work. Also fix an issue where the native class is still considered in the compiler even when shadowed. And prevent non-exposed classes from triggering the warning about shadowing.
cb82517
to
b735b7e
Compare
@HolonProduction added support for that (and tests for it). |
if (!type_found && parser->current_class->has_member(first_id->name)) { | ||
GDScriptParser::ClassNode::Member member = parser->current_class->get_member(first_id->name); | ||
switch (member.type) { | ||
case GDScriptParser::ClassNode::Member::CONSTANT: { | ||
result = member.constant->get_datatype(); | ||
if (!result.is_set()) { | ||
resolve_constant(member.constant, false); | ||
result = member.constant->get_datatype(); | ||
} | ||
if (result.is_meta_type) { | ||
type_found = true; | ||
} else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) { | ||
Ref<GDScript> gdscript = member.constant->initializer->reduced_value; | ||
if (gdscript.is_valid()) { | ||
Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(gdscript->get_script_path()); | ||
if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { | ||
push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), first_id); | ||
return bad_type; | ||
} | ||
result = ref->get_parser()->head->get_datatype(); | ||
} else { | ||
result = make_script_meta_type(member.constant->initializer->reduced_value); | ||
} | ||
type_found = true; | ||
} else { | ||
push_error(vformat(R"(Constant "%s" is not a valid type.)", first), first_id); | ||
return bad_type; | ||
} | ||
} break; | ||
case GDScriptParser::ClassNode::Member::VARIABLE: | ||
case GDScriptParser::ClassNode::Member::FUNCTION: | ||
case GDScriptParser::ClassNode::Member::SIGNAL: | ||
case GDScriptParser::ClassNode::Member::ENUM_VALUE: | ||
push_error(vformat(R"(Member %s "%s" cannot be used as a type.)", member.get_type_name(), first), first_id); | ||
return bad_type; | ||
default: | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this duplicate the logic from here:
godot/modules/gdscript/gdscript_analyzer.cpp
Lines 845 to 897 in 53be3b7
// Classes in current scope. | |
List<GDScriptParser::ClassNode *> script_classes; | |
bool found = false; | |
get_class_node_current_scope_classes(parser->current_class, &script_classes, p_type); | |
for (GDScriptParser::ClassNode *script_class : script_classes) { | |
if (found) { | |
break; | |
} | |
if (script_class->identifier && script_class->identifier->name == first) { | |
result = script_class->get_datatype(); | |
break; | |
} | |
if (script_class->members_indices.has(first)) { | |
resolve_class_member(script_class, first, p_type); | |
GDScriptParser::ClassNode::Member member = script_class->get_member(first); | |
switch (member.type) { | |
case GDScriptParser::ClassNode::Member::CLASS: | |
result = member.get_datatype(); | |
found = true; | |
break; | |
case GDScriptParser::ClassNode::Member::ENUM: | |
result = member.get_datatype(); | |
found = true; | |
break; | |
case GDScriptParser::ClassNode::Member::CONSTANT: | |
if (member.get_datatype().is_meta_type) { | |
result = member.get_datatype(); | |
found = true; | |
break; | |
} else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) { | |
Ref<GDScript> gdscript = member.constant->initializer->reduced_value; | |
if (gdscript.is_valid()) { | |
Ref<GDScriptParserRef> ref = parser->get_depended_parser_for(gdscript->get_script_path()); | |
if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { | |
push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type); | |
return bad_type; | |
} | |
result = ref->get_parser()->head->get_datatype(); | |
} else { | |
result = make_script_meta_type(member.constant->initializer->reduced_value); | |
} | |
found = true; | |
break; | |
} | |
[[fallthrough]]; | |
default: | |
push_error(vformat(R"("%s" is a %s but does not contain a type.)", first, member.get_type_name()), p_type); | |
return bad_type; | |
} | |
} | |
} |
Shouldn't we move the entire linked code block up here? The way it is right now, we have the constant resolving implemented twice and there are some inconsistencies. E.g. shadowing works fine for constants but not for enums. Also shadowing with constants does not work properly when inheritance is involved.
This improves backward compatibility, since if we introduce a new class that matches an existing member, it will still work.
Also fix an issue where the native class is still considered in the compiler even when shadowed. And prevent non-exposed classes from triggering the warning about shadowing.
Fix #96743
Fix #105362