mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 16:03:29 +00:00
GDScript: Fix bug with identifier shadowed below in current scope
This commit is contained in:
parent
202e4b2c1e
commit
d53fc92b4c
19 changed files with 367 additions and 213 deletions
|
@ -430,6 +430,12 @@
|
||||||
<member name="debug/gdscript/warnings/confusable_identifier" type="int" setter="" getter="" default="1">
|
<member name="debug/gdscript/warnings/confusable_identifier" type="int" setter="" getter="" default="1">
|
||||||
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier contains characters that can be confused with something else, like when mixing different alphabets.
|
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier contains characters that can be confused with something else, like when mixing different alphabets.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="debug/gdscript/warnings/confusable_local_declaration" type="int" setter="" getter="" default="1">
|
||||||
|
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier declared in the nested block has the same name as an identifier declared below in the parent block.
|
||||||
|
</member>
|
||||||
|
<member name="debug/gdscript/warnings/confusable_local_usage" type="int" setter="" getter="" default="1">
|
||||||
|
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when an identifier that will be shadowed below in the block is used.
|
||||||
|
</member>
|
||||||
<member name="debug/gdscript/warnings/constant_used_as_function" type="int" setter="" getter="" default="1">
|
<member name="debug/gdscript/warnings/constant_used_as_function" type="int" setter="" getter="" default="1">
|
||||||
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a constant is used as a function.
|
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a constant is used as a function.
|
||||||
</member>
|
</member>
|
||||||
|
|
|
@ -1772,6 +1772,15 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi
|
||||||
|
|
||||||
bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
|
bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (p_assignable->identifier != nullptr && p_assignable->identifier->suite != nullptr && p_assignable->identifier->suite->parent_block != nullptr) {
|
||||||
|
if (p_assignable->identifier->suite->parent_block->has_local(p_assignable->identifier->name)) {
|
||||||
|
const GDScriptParser::SuiteNode::Local &local = p_assignable->identifier->suite->parent_block->get_local(p_assignable->identifier->name);
|
||||||
|
parser->push_warning(p_assignable->identifier, GDScriptWarning::CONFUSABLE_LOCAL_DECLARATION, local.get_name(), p_assignable->identifier->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
GDScriptParser::DataType specified_type;
|
GDScriptParser::DataType specified_type;
|
||||||
bool has_specified_type = p_assignable->datatype_specifier != nullptr;
|
bool has_specified_type = p_assignable->datatype_specifier != nullptr;
|
||||||
if (has_specified_type) {
|
if (has_specified_type) {
|
||||||
|
@ -3518,12 +3527,14 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
||||||
case GDScriptParser::ClassNode::Member::FUNCTION: {
|
case GDScriptParser::ClassNode::Member::FUNCTION: {
|
||||||
if (is_base && (!base.is_meta_type || member.function->is_static)) {
|
if (is_base && (!base.is_meta_type || member.function->is_static)) {
|
||||||
p_identifier->set_datatype(make_callable_type(member.function->info));
|
p_identifier->set_datatype(make_callable_type(member.function->info));
|
||||||
|
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_FUNCTION;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case GDScriptParser::ClassNode::Member::CLASS: {
|
case GDScriptParser::ClassNode::Member::CLASS: {
|
||||||
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
|
reduce_identifier_from_base_set_class(p_identifier, member.get_datatype());
|
||||||
|
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CLASS;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3662,9 +3673,17 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
||||||
found_source = true;
|
found_source = true;
|
||||||
} break;
|
} break;
|
||||||
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
|
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
|
||||||
|
case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
|
||||||
|
case GDScriptParser::IdentifierNode::MEMBER_CLASS:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (!found_source && p_identifier->suite != nullptr && p_identifier->suite->has_local(p_identifier->name)) {
|
||||||
|
parser->push_warning(p_identifier, GDScriptWarning::CONFUSABLE_LOCAL_USAGE, p_identifier->name);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Not a local, so check members.
|
// Not a local, so check members.
|
||||||
if (!found_source) {
|
if (!found_source) {
|
||||||
reduce_identifier_from_base(p_identifier);
|
reduce_identifier_from_base(p_identifier);
|
||||||
|
|
|
@ -225,194 +225,211 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
|
||||||
|
|
||||||
StringName identifier = in->name;
|
StringName identifier = in->name;
|
||||||
|
|
||||||
// Try function parameters.
|
switch (in->source) {
|
||||||
if (codegen.parameters.has(identifier)) {
|
// LOCALS.
|
||||||
return codegen.parameters[identifier];
|
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
|
||||||
}
|
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
|
||||||
|
case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
|
||||||
// Try local variables and constants.
|
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
|
||||||
if (!p_initializer && codegen.locals.has(identifier)) {
|
case GDScriptParser::IdentifierNode::LOCAL_BIND: {
|
||||||
return codegen.locals[identifier];
|
// Try function parameters.
|
||||||
}
|
if (codegen.parameters.has(identifier)) {
|
||||||
|
return codegen.parameters[identifier];
|
||||||
// Try class members.
|
|
||||||
if (_is_class_member_property(codegen, identifier)) {
|
|
||||||
// Get property.
|
|
||||||
GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
|
|
||||||
gen->write_get_member(temp, identifier);
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try members.
|
|
||||||
if (!codegen.function_node || !codegen.function_node->is_static) {
|
|
||||||
// Try member variables.
|
|
||||||
if (codegen.script->member_indices.has(identifier)) {
|
|
||||||
if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
|
|
||||||
// Perform getter.
|
|
||||||
GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type);
|
|
||||||
Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
|
|
||||||
gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
|
|
||||||
return temp;
|
|
||||||
} else {
|
|
||||||
// No getter or inside getter: direct member access.
|
|
||||||
int idx = codegen.script->member_indices[identifier].index;
|
|
||||||
return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try static variables.
|
// Try local variables and constants.
|
||||||
{
|
if (!p_initializer && codegen.locals.has(identifier)) {
|
||||||
GDScript *scr = codegen.script;
|
return codegen.locals[identifier];
|
||||||
while (scr) {
|
}
|
||||||
if (scr->static_variables_indices.has(identifier)) {
|
} break;
|
||||||
if (scr->static_variables_indices[identifier].getter != StringName() && scr->static_variables_indices[identifier].getter != codegen.function_name) {
|
|
||||||
// Perform getter.
|
// MEMBERS.
|
||||||
GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
|
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
|
||||||
GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS);
|
case GDScriptParser::IdentifierNode::INHERITED_VARIABLE: {
|
||||||
Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
|
// Try class members.
|
||||||
gen->write_call(temp, class_addr, scr->static_variables_indices[identifier].getter, args);
|
if (_is_class_member_property(codegen, identifier)) {
|
||||||
return temp;
|
// Get property.
|
||||||
} else {
|
GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script));
|
||||||
// No getter or inside getter: direct variable access.
|
gen->write_get_member(temp, identifier);
|
||||||
GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
|
return temp;
|
||||||
GDScriptCodeGenerator::Address _class = codegen.add_constant(scr);
|
}
|
||||||
int index = scr->static_variables_indices[identifier].index;
|
|
||||||
gen->write_get_static_variable(temp, _class, index);
|
// Try members.
|
||||||
return temp;
|
if (!codegen.function_node || !codegen.function_node->is_static) {
|
||||||
|
// Try member variables.
|
||||||
|
if (codegen.script->member_indices.has(identifier)) {
|
||||||
|
if (codegen.script->member_indices[identifier].getter != StringName() && codegen.script->member_indices[identifier].getter != codegen.function_name) {
|
||||||
|
// Perform getter.
|
||||||
|
GDScriptCodeGenerator::Address temp = codegen.add_temporary(codegen.script->member_indices[identifier].data_type);
|
||||||
|
Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
|
||||||
|
gen->write_call_self(temp, codegen.script->member_indices[identifier].getter, args);
|
||||||
|
return temp;
|
||||||
|
} else {
|
||||||
|
// No getter or inside getter: direct member access.
|
||||||
|
int idx = codegen.script->member_indices[identifier].index;
|
||||||
|
return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::MEMBER, idx, codegen.script->get_member_type(identifier));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scr = scr->_base;
|
} break;
|
||||||
}
|
case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
|
||||||
}
|
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL: {
|
||||||
|
// Try methods and signals (can be Callable and Signal).
|
||||||
|
|
||||||
// Try class constants.
|
// Search upwards through parent classes:
|
||||||
{
|
const GDScriptParser::ClassNode *base_class = codegen.class_node;
|
||||||
GDScript *owner = codegen.script;
|
while (base_class != nullptr) {
|
||||||
while (owner) {
|
if (base_class->has_member(identifier)) {
|
||||||
GDScript *scr = owner;
|
const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
|
||||||
|
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
|
||||||
|
// Get like it was a property.
|
||||||
|
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
|
||||||
|
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
|
||||||
|
|
||||||
|
gen->write_get_named(temp, identifier, self);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base_class = base_class->base_type.class_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try in native base.
|
||||||
|
GDScript *scr = codegen.script;
|
||||||
GDScriptNativeClass *nc = nullptr;
|
GDScriptNativeClass *nc = nullptr;
|
||||||
while (scr) {
|
while (scr) {
|
||||||
if (scr->constants.has(identifier)) {
|
|
||||||
return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
|
|
||||||
}
|
|
||||||
if (scr->native.is_valid()) {
|
if (scr->native.is_valid()) {
|
||||||
nc = scr->native.ptr();
|
nc = scr->native.ptr();
|
||||||
}
|
}
|
||||||
scr = scr->_base;
|
scr = scr->_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Class C++ integer constant.
|
if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
|
||||||
if (nc) {
|
// Get like it was a property.
|
||||||
bool success = false;
|
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
|
||||||
int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
|
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
|
||||||
if (success) {
|
|
||||||
return codegen.add_constant(constant);
|
gen->write_get_named(temp, identifier, self);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
|
||||||
|
case GDScriptParser::IdentifierNode::MEMBER_CLASS: {
|
||||||
|
// Try class constants.
|
||||||
|
GDScript *owner = codegen.script;
|
||||||
|
while (owner) {
|
||||||
|
GDScript *scr = owner;
|
||||||
|
GDScriptNativeClass *nc = nullptr;
|
||||||
|
while (scr) {
|
||||||
|
if (scr->constants.has(identifier)) {
|
||||||
|
return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
|
||||||
|
}
|
||||||
|
if (scr->native.is_valid()) {
|
||||||
|
nc = scr->native.ptr();
|
||||||
|
}
|
||||||
|
scr = scr->_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class C++ integer constant.
|
||||||
|
if (nc) {
|
||||||
|
bool success = false;
|
||||||
|
int64_t constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
|
||||||
|
if (success) {
|
||||||
|
return codegen.add_constant(constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
owner = owner->_owner;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case GDScriptParser::IdentifierNode::STATIC_VARIABLE: {
|
||||||
|
// Try static variables.
|
||||||
|
GDScript *scr = codegen.script;
|
||||||
|
while (scr) {
|
||||||
|
if (scr->static_variables_indices.has(identifier)) {
|
||||||
|
if (scr->static_variables_indices[identifier].getter != StringName() && scr->static_variables_indices[identifier].getter != codegen.function_name) {
|
||||||
|
// Perform getter.
|
||||||
|
GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
|
||||||
|
GDScriptCodeGenerator::Address class_addr(GDScriptCodeGenerator::Address::CLASS);
|
||||||
|
Vector<GDScriptCodeGenerator::Address> args; // No argument needed.
|
||||||
|
gen->write_call(temp, class_addr, scr->static_variables_indices[identifier].getter, args);
|
||||||
|
return temp;
|
||||||
|
} else {
|
||||||
|
// No getter or inside getter: direct variable access.
|
||||||
|
GDScriptCodeGenerator::Address temp = codegen.add_temporary(scr->static_variables_indices[identifier].data_type);
|
||||||
|
GDScriptCodeGenerator::Address _class = codegen.add_constant(scr);
|
||||||
|
int index = scr->static_variables_indices[identifier].index;
|
||||||
|
gen->write_get_static_variable(temp, _class, index);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scr = scr->_base;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// GLOBALS.
|
||||||
|
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE: {
|
||||||
|
// Try globals.
|
||||||
|
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
|
||||||
|
// If it's an autoload singleton, we postpone to load it at runtime.
|
||||||
|
// This is so one autoload doesn't try to load another before it's compiled.
|
||||||
|
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
|
||||||
|
if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
|
||||||
|
GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
|
||||||
|
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
|
||||||
|
gen->write_store_global(global, idx);
|
||||||
|
return global;
|
||||||
|
} else {
|
||||||
|
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
|
||||||
|
Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
|
||||||
|
return codegen.add_constant(global);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
owner = owner->_owner;
|
// Try global classes.
|
||||||
}
|
if (ScriptServer::is_global_class(identifier)) {
|
||||||
}
|
const GDScriptParser::ClassNode *class_node = codegen.class_node;
|
||||||
|
while (class_node->outer) {
|
||||||
// Try signals and methods (can be made callables).
|
class_node = class_node->outer;
|
||||||
{
|
|
||||||
// Search upwards through parent classes:
|
|
||||||
const GDScriptParser::ClassNode *base_class = codegen.class_node;
|
|
||||||
while (base_class != nullptr) {
|
|
||||||
if (base_class->has_member(identifier)) {
|
|
||||||
const GDScriptParser::ClassNode::Member &member = base_class->get_member(identifier);
|
|
||||||
if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
|
|
||||||
// Get like it was a property.
|
|
||||||
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
|
|
||||||
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
|
|
||||||
|
|
||||||
gen->write_get_named(temp, identifier, self);
|
|
||||||
return temp;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
base_class = base_class->base_type.class_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try in native base.
|
Ref<Resource> res;
|
||||||
GDScript *scr = codegen.script;
|
|
||||||
GDScriptNativeClass *nc = nullptr;
|
|
||||||
while (scr) {
|
|
||||||
if (scr->native.is_valid()) {
|
|
||||||
nc = scr->native.ptr();
|
|
||||||
}
|
|
||||||
scr = scr->_base;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
|
if (class_node->identifier && class_node->identifier->name == identifier) {
|
||||||
// Get like it was a property.
|
res = Ref<GDScript>(main_script);
|
||||||
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
|
} else {
|
||||||
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
|
String global_class_path = ScriptServer::get_global_class_path(identifier);
|
||||||
|
if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
|
||||||
gen->write_get_named(temp, identifier, self);
|
Error err = OK;
|
||||||
return temp;
|
res = GDScriptCache::get_full_script(global_class_path, err);
|
||||||
}
|
if (err != OK) {
|
||||||
}
|
_set_error("Can't load global class " + String(identifier), p_expression);
|
||||||
|
r_error = ERR_COMPILATION_FAILED;
|
||||||
// Try globals.
|
return GDScriptCodeGenerator::Address();
|
||||||
if (GDScriptLanguage::get_singleton()->get_global_map().has(identifier)) {
|
}
|
||||||
// If it's an autoload singleton, we postpone to load it at runtime.
|
} else {
|
||||||
// This is so one autoload doesn't try to load another before it's compiled.
|
res = ResourceLoader::load(global_class_path);
|
||||||
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
|
if (res.is_null()) {
|
||||||
if (autoloads.has(identifier) && autoloads[identifier].is_singleton) {
|
_set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
|
||||||
GDScriptCodeGenerator::Address global = codegen.add_temporary(_gdtype_from_datatype(in->get_datatype(), codegen.script));
|
r_error = ERR_COMPILATION_FAILED;
|
||||||
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
|
return GDScriptCodeGenerator::Address();
|
||||||
gen->write_store_global(global, idx);
|
}
|
||||||
return global;
|
}
|
||||||
} else {
|
|
||||||
int idx = GDScriptLanguage::get_singleton()->get_global_map()[identifier];
|
|
||||||
Variant global = GDScriptLanguage::get_singleton()->get_global_array()[idx];
|
|
||||||
return codegen.add_constant(global);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try global classes.
|
|
||||||
if (ScriptServer::is_global_class(identifier)) {
|
|
||||||
const GDScriptParser::ClassNode *class_node = codegen.class_node;
|
|
||||||
while (class_node->outer) {
|
|
||||||
class_node = class_node->outer;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<Resource> res;
|
|
||||||
|
|
||||||
if (class_node->identifier && class_node->identifier->name == identifier) {
|
|
||||||
res = Ref<GDScript>(main_script);
|
|
||||||
} else {
|
|
||||||
String global_class_path = ScriptServer::get_global_class_path(identifier);
|
|
||||||
if (ResourceLoader::get_resource_type(global_class_path) == "GDScript") {
|
|
||||||
Error err = OK;
|
|
||||||
res = GDScriptCache::get_full_script(global_class_path, err);
|
|
||||||
if (err != OK) {
|
|
||||||
_set_error("Can't load global class " + String(identifier), p_expression);
|
|
||||||
r_error = ERR_COMPILATION_FAILED;
|
|
||||||
return GDScriptCodeGenerator::Address();
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
res = ResourceLoader::load(global_class_path);
|
|
||||||
if (res.is_null()) {
|
|
||||||
_set_error("Can't load global class " + String(identifier) + ", cyclic reference?", p_expression);
|
|
||||||
r_error = ERR_COMPILATION_FAILED;
|
|
||||||
return GDScriptCodeGenerator::Address();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return codegen.add_constant(res);
|
return codegen.add_constant(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
|
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
|
||||||
GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.
|
GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.
|
||||||
gen->write_store_named_global(global, identifier);
|
gen->write_store_named_global(global, identifier);
|
||||||
return global;
|
return global;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
// Not found, error.
|
// Not found, error.
|
||||||
_set_error("Identifier not found: " + String(identifier), p_expression);
|
_set_error("Identifier not found: " + String(identifier), p_expression);
|
||||||
r_error = ERR_COMPILATION_FAILED;
|
r_error = ERR_COMPILATION_FAILED;
|
||||||
|
|
|
@ -1393,7 +1393,7 @@ struct RecursionCheck {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
|
static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type);
|
||||||
static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
|
static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
|
||||||
static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
|
static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
|
||||||
|
|
||||||
|
@ -1457,7 +1457,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
|
||||||
} break;
|
} break;
|
||||||
case GDScriptParser::Node::IDENTIFIER: {
|
case GDScriptParser::Node::IDENTIFIER: {
|
||||||
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
|
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
|
||||||
found = _guess_identifier_type(p_context, id->name, r_type);
|
found = _guess_identifier_type(p_context, id, r_type);
|
||||||
} break;
|
} break;
|
||||||
case GDScriptParser::Node::DICTIONARY: {
|
case GDScriptParser::Node::DICTIONARY: {
|
||||||
// Try to recreate the dictionary.
|
// Try to recreate the dictionary.
|
||||||
|
@ -1900,7 +1900,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {
|
static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type) {
|
||||||
static int recursion_depth = 0;
|
static int recursion_depth = 0;
|
||||||
RecursionCheck recursion(&recursion_depth);
|
RecursionCheck recursion(&recursion_depth);
|
||||||
if (unlikely(recursion.check())) {
|
if (unlikely(recursion.check())) {
|
||||||
|
@ -1914,36 +1914,49 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
|
||||||
GDScriptParser::SuiteNode *suite = p_context.current_suite;
|
GDScriptParser::SuiteNode *suite = p_context.current_suite;
|
||||||
bool is_function_parameter = false;
|
bool is_function_parameter = false;
|
||||||
|
|
||||||
if (suite) {
|
bool can_be_local = true;
|
||||||
if (suite->has_local(p_identifier)) {
|
switch (p_identifier->source) {
|
||||||
const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier);
|
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
|
||||||
|
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
|
||||||
|
case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:
|
||||||
|
case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:
|
||||||
|
case GDScriptParser::IdentifierNode::MEMBER_CLASS:
|
||||||
|
case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
|
||||||
|
case GDScriptParser::IdentifierNode::STATIC_VARIABLE:
|
||||||
|
can_be_local = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
id_type = local.get_datatype();
|
if (can_be_local && suite && suite->has_local(p_identifier->name)) {
|
||||||
|
const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);
|
||||||
|
|
||||||
// Check initializer as the first assignment.
|
id_type = local.get_datatype();
|
||||||
switch (local.type) {
|
|
||||||
case GDScriptParser::SuiteNode::Local::VARIABLE:
|
// Check initializer as the first assignment.
|
||||||
if (local.variable->initializer) {
|
switch (local.type) {
|
||||||
last_assign_line = local.variable->initializer->end_line;
|
case GDScriptParser::SuiteNode::Local::VARIABLE:
|
||||||
last_assigned_expression = local.variable->initializer;
|
if (local.variable->initializer) {
|
||||||
}
|
last_assign_line = local.variable->initializer->end_line;
|
||||||
break;
|
last_assigned_expression = local.variable->initializer;
|
||||||
case GDScriptParser::SuiteNode::Local::CONSTANT:
|
}
|
||||||
if (local.constant->initializer) {
|
break;
|
||||||
last_assign_line = local.constant->initializer->end_line;
|
case GDScriptParser::SuiteNode::Local::CONSTANT:
|
||||||
last_assigned_expression = local.constant->initializer;
|
if (local.constant->initializer) {
|
||||||
}
|
last_assign_line = local.constant->initializer->end_line;
|
||||||
break;
|
last_assigned_expression = local.constant->initializer;
|
||||||
case GDScriptParser::SuiteNode::Local::PARAMETER:
|
}
|
||||||
if (local.parameter->initializer) {
|
break;
|
||||||
last_assign_line = local.parameter->initializer->end_line;
|
case GDScriptParser::SuiteNode::Local::PARAMETER:
|
||||||
last_assigned_expression = local.parameter->initializer;
|
if (local.parameter->initializer) {
|
||||||
}
|
last_assign_line = local.parameter->initializer->end_line;
|
||||||
is_function_parameter = true;
|
last_assigned_expression = local.parameter->initializer;
|
||||||
break;
|
}
|
||||||
default:
|
is_function_parameter = true;
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1958,7 +1971,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
|
||||||
const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]);
|
const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]);
|
||||||
if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) {
|
if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) {
|
||||||
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee);
|
const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee);
|
||||||
if (id->name == p_identifier) {
|
if (id->name == p_identifier->name && id->source == p_identifier->source) {
|
||||||
last_assign_line = assign->assigned_value->end_line;
|
last_assign_line = assign->assigned_value->end_line;
|
||||||
last_assigned_expression = assign->assigned_value;
|
last_assigned_expression = assign->assigned_value;
|
||||||
}
|
}
|
||||||
|
@ -1976,7 +1989,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
|
||||||
// Credit: Zylann.
|
// Credit: Zylann.
|
||||||
// TODO: this could be hacked to detect ANDed conditions too...
|
// TODO: this could be hacked to detect ANDed conditions too...
|
||||||
const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(suite->parent_if->condition);
|
const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(suite->parent_if->condition);
|
||||||
if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier) {
|
if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier->name && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->source == p_identifier->source) {
|
||||||
// Bingo.
|
// Bingo.
|
||||||
GDScriptParser::CompletionContext c = p_context;
|
GDScriptParser::CompletionContext c = p_context;
|
||||||
c.current_line = type_test->operand->start_line;
|
c.current_line = type_test->operand->start_line;
|
||||||
|
@ -2012,8 +2025,8 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
|
||||||
case GDScriptParser::DataType::CLASS:
|
case GDScriptParser::DataType::CLASS:
|
||||||
if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {
|
if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {
|
||||||
GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;
|
GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;
|
||||||
if (parent_function->parameters_indices.has(p_identifier)) {
|
if (parent_function->parameters_indices.has(p_identifier->name)) {
|
||||||
const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier]];
|
const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]];
|
||||||
if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
|
if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
|
||||||
id_type = parameter->get_datatype();
|
id_type = parameter->get_datatype();
|
||||||
}
|
}
|
||||||
|
@ -2038,7 +2051,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
|
||||||
MethodInfo info;
|
MethodInfo info;
|
||||||
if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) {
|
if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) {
|
||||||
for (const PropertyInfo &E : info.arguments) {
|
for (const PropertyInfo &E : info.arguments) {
|
||||||
if (E.name == p_identifier) {
|
if (E.name == p_identifier->name) {
|
||||||
r_type = _type_from_property(E);
|
r_type = _type_from_property(E);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2066,14 +2079,14 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
|
||||||
base.type.class_type = p_context.current_class;
|
base.type.class_type = p_context.current_class;
|
||||||
base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;
|
base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;
|
||||||
|
|
||||||
if (_guess_identifier_type_from_base(p_context, base, p_identifier, r_type)) {
|
if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, r_type)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check global scripts.
|
// Check global scripts.
|
||||||
if (ScriptServer::is_global_class(p_identifier)) {
|
if (ScriptServer::is_global_class(p_identifier->name)) {
|
||||||
String script = ScriptServer::get_global_class_path(p_identifier);
|
String script = ScriptServer::get_global_class_path(p_identifier->name);
|
||||||
if (script.to_lower().ends_with(".gd")) {
|
if (script.to_lower().ends_with(".gd")) {
|
||||||
Error err = OK;
|
Error err = OK;
|
||||||
Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err);
|
Ref<GDScriptParserRef> parser = GDScriptCache::get_parser(script, GDScriptParserRef::INTERFACE_SOLVED, err);
|
||||||
|
@ -2089,7 +2102,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier));
|
Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier->name));
|
||||||
if (scr.is_valid()) {
|
if (scr.is_valid()) {
|
||||||
r_type = _type_from_variant(scr);
|
r_type = _type_from_variant(scr);
|
||||||
r_type.type.is_meta_type = true;
|
r_type.type.is_meta_type = true;
|
||||||
|
@ -2100,20 +2113,20 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check global variables (including autoloads).
|
// Check global variables (including autoloads).
|
||||||
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier)) {
|
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier->name)) {
|
||||||
r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier]);
|
r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check ClassDB.
|
// Check ClassDB.
|
||||||
if (ClassDB::class_exists(p_identifier) && ClassDB::is_class_exposed(p_identifier)) {
|
if (ClassDB::class_exists(p_identifier->name) && ClassDB::is_class_exposed(p_identifier->name)) {
|
||||||
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
||||||
r_type.type.kind = GDScriptParser::DataType::NATIVE;
|
r_type.type.kind = GDScriptParser::DataType::NATIVE;
|
||||||
r_type.type.native_type = p_identifier;
|
r_type.type.native_type = p_identifier->name;
|
||||||
r_type.type.is_constant = true;
|
r_type.type.is_constant = true;
|
||||||
if (Engine::get_singleton()->has_singleton(p_identifier)) {
|
if (Engine::get_singleton()->has_singleton(p_identifier->name)) {
|
||||||
r_type.type.is_meta_type = false;
|
r_type.type.is_meta_type = false;
|
||||||
r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier);
|
r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier->name);
|
||||||
} else {
|
} else {
|
||||||
r_type.type.is_meta_type = true;
|
r_type.type.is_meta_type = true;
|
||||||
r_type.value = Variant();
|
r_type.value = Variant();
|
||||||
|
|
|
@ -2267,6 +2267,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
|
||||||
IdentifierNode *identifier = alloc_node<IdentifierNode>();
|
IdentifierNode *identifier = alloc_node<IdentifierNode>();
|
||||||
complete_extents(identifier);
|
complete_extents(identifier);
|
||||||
identifier->name = previous.get_identifier();
|
identifier->name = previous.get_identifier();
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
identifier->suite = current_suite;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
|
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
|
||||||
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
|
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
|
||||||
|
|
|
@ -843,19 +843,24 @@ public:
|
||||||
|
|
||||||
struct IdentifierNode : public ExpressionNode {
|
struct IdentifierNode : public ExpressionNode {
|
||||||
StringName name;
|
StringName name;
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
SuiteNode *suite = nullptr; // The block in which the identifier is used.
|
||||||
|
#endif
|
||||||
|
|
||||||
enum Source {
|
enum Source {
|
||||||
UNDEFINED_SOURCE,
|
UNDEFINED_SOURCE,
|
||||||
FUNCTION_PARAMETER,
|
FUNCTION_PARAMETER,
|
||||||
LOCAL_CONSTANT,
|
|
||||||
LOCAL_VARIABLE,
|
LOCAL_VARIABLE,
|
||||||
|
LOCAL_CONSTANT,
|
||||||
LOCAL_ITERATOR, // `for` loop iterator.
|
LOCAL_ITERATOR, // `for` loop iterator.
|
||||||
LOCAL_BIND, // Pattern bind.
|
LOCAL_BIND, // Pattern bind.
|
||||||
MEMBER_SIGNAL,
|
|
||||||
MEMBER_VARIABLE,
|
MEMBER_VARIABLE,
|
||||||
STATIC_VARIABLE,
|
|
||||||
MEMBER_CONSTANT,
|
MEMBER_CONSTANT,
|
||||||
|
MEMBER_FUNCTION,
|
||||||
|
MEMBER_SIGNAL,
|
||||||
|
MEMBER_CLASS,
|
||||||
INHERITED_VARIABLE,
|
INHERITED_VARIABLE,
|
||||||
|
STATIC_VARIABLE,
|
||||||
};
|
};
|
||||||
Source source = UNDEFINED_SOURCE;
|
Source source = UNDEFINED_SOURCE;
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,12 @@ String GDScriptWarning::get_message() const {
|
||||||
case CONFUSABLE_IDENTIFIER:
|
case CONFUSABLE_IDENTIFIER:
|
||||||
CHECK_SYMBOLS(1);
|
CHECK_SYMBOLS(1);
|
||||||
return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]);
|
return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]);
|
||||||
|
case CONFUSABLE_LOCAL_DECLARATION:
|
||||||
|
CHECK_SYMBOLS(2);
|
||||||
|
return vformat(R"(The %s "%s" is declared below in the parent block.)", symbols[0], symbols[1]);
|
||||||
|
case CONFUSABLE_LOCAL_USAGE:
|
||||||
|
CHECK_SYMBOLS(1);
|
||||||
|
return vformat(R"(The identifier "%s" will be shadowed below in the block.)", symbols[0]);
|
||||||
case INFERENCE_ON_VARIANT:
|
case INFERENCE_ON_VARIANT:
|
||||||
CHECK_SYMBOLS(1);
|
CHECK_SYMBOLS(1);
|
||||||
return vformat("The %s type is being inferred from a Variant value, so it will be typed as Variant.", symbols[0]);
|
return vformat("The %s type is being inferred from a Variant value, so it will be typed as Variant.", symbols[0]);
|
||||||
|
@ -213,6 +219,8 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
|
||||||
"DEPRECATED_KEYWORD",
|
"DEPRECATED_KEYWORD",
|
||||||
"RENAMED_IN_GODOT_4_HINT",
|
"RENAMED_IN_GODOT_4_HINT",
|
||||||
"CONFUSABLE_IDENTIFIER",
|
"CONFUSABLE_IDENTIFIER",
|
||||||
|
"CONFUSABLE_LOCAL_DECLARATION",
|
||||||
|
"CONFUSABLE_LOCAL_USAGE",
|
||||||
"INFERENCE_ON_VARIANT",
|
"INFERENCE_ON_VARIANT",
|
||||||
"NATIVE_METHOD_OVERRIDE",
|
"NATIVE_METHOD_OVERRIDE",
|
||||||
"GET_NODE_DEFAULT_WITHOUT_ONREADY",
|
"GET_NODE_DEFAULT_WITHOUT_ONREADY",
|
||||||
|
|
|
@ -83,6 +83,8 @@ public:
|
||||||
DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
|
DEPRECATED_KEYWORD, // The keyword is deprecated and should be replaced.
|
||||||
RENAMED_IN_GODOT_4_HINT, // A variable or function that could not be found has been renamed in Godot 4.
|
RENAMED_IN_GODOT_4_HINT, // A variable or function that could not be found has been renamed in Godot 4.
|
||||||
CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e").
|
CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e").
|
||||||
|
CONFUSABLE_LOCAL_DECLARATION, // The parent block declares an identifier with the same name below.
|
||||||
|
CONFUSABLE_LOCAL_USAGE, // The identifier will be shadowed below in the block.
|
||||||
INFERENCE_ON_VARIANT, // The declaration uses type inference but the value is typed as Variant.
|
INFERENCE_ON_VARIANT, // The declaration uses type inference but the value is typed as Variant.
|
||||||
NATIVE_METHOD_OVERRIDE, // The script method overrides a native one, this may not work as intended.
|
NATIVE_METHOD_OVERRIDE, // The script method overrides a native one, this may not work as intended.
|
||||||
GET_NODE_DEFAULT_WITHOUT_ONREADY, // A class variable uses `get_node()` (or the `$` notation) as its default value, but does not use the @onready annotation.
|
GET_NODE_DEFAULT_WITHOUT_ONREADY, // A class variable uses `get_node()` (or the `$` notation) as its default value, but does not use the @onready annotation.
|
||||||
|
@ -128,6 +130,8 @@ public:
|
||||||
WARN, // DEPRECATED_KEYWORD
|
WARN, // DEPRECATED_KEYWORD
|
||||||
WARN, // RENAMED_IN_GODOT_4_HINT
|
WARN, // RENAMED_IN_GODOT_4_HINT
|
||||||
WARN, // CONFUSABLE_IDENTIFIER
|
WARN, // CONFUSABLE_IDENTIFIER
|
||||||
|
WARN, // CONFUSABLE_LOCAL_DECLARATION
|
||||||
|
WARN, // CONFUSABLE_LOCAL_USAGE
|
||||||
ERROR, // INFERENCE_ON_VARIANT // Most likely done by accident, usually inference is trying for a particular type.
|
ERROR, // INFERENCE_ON_VARIANT // Most likely done by accident, usually inference is trying for a particular type.
|
||||||
ERROR, // NATIVE_METHOD_OVERRIDE // May not work as expected.
|
ERROR, // NATIVE_METHOD_OVERRIDE // May not work as expected.
|
||||||
ERROR, // GET_NODE_DEFAULT_WITHOUT_ONREADY // May not work as expected.
|
ERROR, // GET_NODE_DEFAULT_WITHOUT_ONREADY // May not work as expected.
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
var v1 = v1
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(v1)
|
|
@ -0,0 +1,2 @@
|
||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Could not resolve member "v1": Cyclic reference.
|
|
@ -126,7 +126,7 @@ func test():
|
||||||
assert(a_objects.get_typed_builtin() == TYPE_OBJECT)
|
assert(a_objects.get_typed_builtin() == TYPE_OBJECT)
|
||||||
assert(a_objects.get_typed_script() == A)
|
assert(a_objects.get_typed_script() == A)
|
||||||
|
|
||||||
var a_passed = (func check_a_passing(a_objects: Array[A]): return a_objects.size()).call(a_objects)
|
var a_passed = (func check_a_passing(p_objects: Array[A]): return p_objects.size()).call(a_objects)
|
||||||
assert(a_passed == 4)
|
assert(a_passed == 4)
|
||||||
|
|
||||||
var b_passed = (func check_b_passing(basic: Array): return basic[0] != null).call(b_objects)
|
var b_passed = (func check_b_passing(basic: Array): return basic[0] != null).call(b_objects)
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
func test():
|
||||||
|
if true:
|
||||||
|
var a = 1
|
||||||
|
print(a)
|
||||||
|
var a = 2
|
||||||
|
print(a)
|
|
@ -0,0 +1,7 @@
|
||||||
|
GDTEST_OK
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 3
|
||||||
|
>> CONFUSABLE_LOCAL_DECLARATION
|
||||||
|
>> The variable "a" is declared below in the parent block.
|
||||||
|
1
|
||||||
|
2
|
|
@ -0,0 +1,6 @@
|
||||||
|
var a = 1
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(a)
|
||||||
|
var a = 2
|
||||||
|
print(a)
|
|
@ -0,0 +1,11 @@
|
||||||
|
GDTEST_OK
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 4
|
||||||
|
>> CONFUSABLE_LOCAL_USAGE
|
||||||
|
>> The identifier "a" will be shadowed below in the block.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 5
|
||||||
|
>> SHADOWED_VARIABLE
|
||||||
|
>> The local variable "a" is shadowing an already-declared variable at line 1.
|
||||||
|
1
|
||||||
|
2
|
|
@ -0,0 +1,6 @@
|
||||||
|
var a = 1
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print(a)
|
||||||
|
var a = a + 1
|
||||||
|
print(a)
|
|
@ -0,0 +1,15 @@
|
||||||
|
GDTEST_OK
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 4
|
||||||
|
>> CONFUSABLE_LOCAL_USAGE
|
||||||
|
>> The identifier "a" will be shadowed below in the block.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 5
|
||||||
|
>> CONFUSABLE_LOCAL_USAGE
|
||||||
|
>> The identifier "a" will be shadowed below in the block.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 5
|
||||||
|
>> SHADOWED_VARIABLE
|
||||||
|
>> The local variable "a" is shadowing an already-declared variable at line 1.
|
||||||
|
1
|
||||||
|
2
|
|
@ -0,0 +1,7 @@
|
||||||
|
var a = 1
|
||||||
|
|
||||||
|
func test():
|
||||||
|
for _i in 3:
|
||||||
|
print(a)
|
||||||
|
var a = 2
|
||||||
|
print(a)
|
|
@ -0,0 +1,15 @@
|
||||||
|
GDTEST_OK
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 5
|
||||||
|
>> CONFUSABLE_LOCAL_USAGE
|
||||||
|
>> The identifier "a" will be shadowed below in the block.
|
||||||
|
>> WARNING
|
||||||
|
>> Line: 6
|
||||||
|
>> SHADOWED_VARIABLE
|
||||||
|
>> The local variable "a" is shadowing an already-declared variable at line 1.
|
||||||
|
1
|
||||||
|
2
|
||||||
|
1
|
||||||
|
2
|
||||||
|
1
|
||||||
|
2
|
Loading…
Add table
Add a link
Reference in a new issue