Allow using built-in names for variables, push warnings instead
This commit is contained in:
parent
3e214a8838
commit
3396b25489
@ -368,6 +368,9 @@
|
||||
<member name="debug/gdscript/warnings/return_value_discarded" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], enables warnings when calling a function without using its return value (by assigning it to a variable or using it as a function argument). Such return values are sometimes used to denote possible errors using the [enum Error] enum.
|
||||
</member>
|
||||
<member name="debug/gdscript/warnings/shadowed_global_identifier" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], enables warnings when defining a local or subclass member variable, signal, or enum that would have the same name as a built-in function or global class name, which possibly shadow it.
|
||||
</member>
|
||||
<member name="debug/gdscript/warnings/shadowed_variable" type="bool" setter="" getter="" default="true">
|
||||
If [code]true[/code], enables warnings when defining a local or subclass member variable that would shadow a variable at an upper level (such as a member variable).
|
||||
</member>
|
||||
|
@ -741,20 +741,22 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
|
||||
|
||||
if (member->identifier != nullptr) {
|
||||
if (!((String)member->identifier->name).is_empty()) { // Enums may be unnamed.
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
List<MethodInfo> gdscript_funcs;
|
||||
GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
|
||||
for (MethodInfo &info : gdscript_funcs) {
|
||||
if (info.name == member->identifier->name) {
|
||||
push_error(vformat(R"(%s "%s" has the same name as a built-in function.)", p_member_kind.capitalize(), member->identifier->name), member->identifier);
|
||||
return;
|
||||
push_warning(member->identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_member_kind, member->identifier->name, "built-in function");
|
||||
}
|
||||
}
|
||||
if (Variant::has_utility_function(member->identifier->name)) {
|
||||
push_warning(member->identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, p_member_kind, member->identifier->name, "built-in function");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (current_class->members_indices.has(member->identifier->name)) {
|
||||
push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
|
||||
} else if (Variant::has_utility_function(member->identifier->name)) {
|
||||
push_error(vformat(R"(%s "%s" has the same name as a built-in function.)", p_member_kind.capitalize(), member->identifier->name), member->identifier);
|
||||
} else if (ClassDB::class_exists(member->identifier->name)) {
|
||||
push_error(vformat(R"(%s "%s" has the same name as a global class.)", p_member_kind.capitalize(), member->identifier->name), member->identifier);
|
||||
} else {
|
||||
current_class->add_member(member);
|
||||
}
|
||||
@ -827,21 +829,18 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
|
||||
|
||||
GDScriptParser::IdentifierNode *identifier = parse_identifier();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
List<MethodInfo> gdscript_funcs;
|
||||
GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
|
||||
for (MethodInfo &info : gdscript_funcs) {
|
||||
if (info.name == identifier->name) {
|
||||
push_error(vformat(R"(Local var "%s" has the same name as a built-in function.)", identifier->name), identifier);
|
||||
return nullptr;
|
||||
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "local variable", identifier->name, "built-in function");
|
||||
}
|
||||
}
|
||||
if (Variant::has_utility_function(identifier->name)) {
|
||||
push_error(vformat(R"(Local var "%s" has the same name as a built-in function.)", identifier->name), identifier);
|
||||
return nullptr;
|
||||
} else if (ClassDB::class_exists(identifier->name)) {
|
||||
push_error(vformat(R"(Local var "%s" has the same name as a global class.)", identifier->name), identifier);
|
||||
return nullptr;
|
||||
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "local variable", identifier->name, "built-in function");
|
||||
}
|
||||
#endif
|
||||
|
||||
VariableNode *variable = alloc_node<VariableNode>();
|
||||
variable->identifier = identifier;
|
||||
@ -1099,22 +1098,20 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
|
||||
}
|
||||
|
||||
GDScriptParser::IdentifierNode *identifier = parse_identifier();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
List<MethodInfo> gdscript_funcs;
|
||||
GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
|
||||
for (MethodInfo &info : gdscript_funcs) {
|
||||
if (info.name == identifier->name) {
|
||||
push_error(vformat(R"(Parameter "%s" has the same name as a built-in function.)", identifier->name), identifier);
|
||||
return nullptr;
|
||||
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "parameter", identifier->name, "built-in function");
|
||||
}
|
||||
}
|
||||
if (Variant::has_utility_function(identifier->name)) {
|
||||
push_error(vformat(R"(Parameter "%s" has the same name as a built-in function.)", identifier->name), identifier);
|
||||
return nullptr;
|
||||
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "parameter", identifier->name, "built-in function");
|
||||
} else if (ClassDB::class_exists(identifier->name)) {
|
||||
push_error(vformat(R"(Parameter "%s" has the same name as a global class.)", identifier->name), identifier);
|
||||
return nullptr;
|
||||
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "parameter", identifier->name, "global class");
|
||||
}
|
||||
#endif
|
||||
|
||||
ParameterNode *parameter = alloc_node<ParameterNode>();
|
||||
parameter->identifier = identifier;
|
||||
@ -1195,8 +1192,10 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
|
||||
|
||||
HashMap<StringName, int> elements;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
List<MethodInfo> gdscript_funcs;
|
||||
GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
|
||||
#endif
|
||||
|
||||
do {
|
||||
if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
|
||||
@ -1205,20 +1204,18 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
|
||||
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) {
|
||||
EnumNode::Value item;
|
||||
GDScriptParser::IdentifierNode *identifier = parse_identifier();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
for (MethodInfo &info : gdscript_funcs) {
|
||||
if (info.name == identifier->name) {
|
||||
push_error(vformat(R"(Enum member "%s" has the same name as a built-in function.)", identifier->name), identifier);
|
||||
return nullptr;
|
||||
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
|
||||
}
|
||||
}
|
||||
if (Variant::has_utility_function(identifier->name)) {
|
||||
push_error(vformat(R"(Enum member "%s" has the same name as a built-in function.)", identifier->name), identifier);
|
||||
return nullptr;
|
||||
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "built-in function");
|
||||
} else if (ClassDB::class_exists(identifier->name)) {
|
||||
push_error(vformat(R"(Enum member "%s" has the same name as a global class.)", identifier->name), identifier);
|
||||
return nullptr;
|
||||
push_warning(identifier, GDScriptWarning::SHADOWED_GLOBAL_IDENTIFIER, "enum member", identifier->name, "global class");
|
||||
}
|
||||
#endif
|
||||
item.identifier = identifier;
|
||||
item.parent_enum = enum_node;
|
||||
item.line = previous.start_line;
|
||||
|
@ -148,6 +148,10 @@ String GDScriptWarning::get_message() const {
|
||||
case EMPTY_FILE: {
|
||||
return "Empty script file.";
|
||||
}
|
||||
case SHADOWED_GLOBAL_IDENTIFIER: {
|
||||
CHECK_SYMBOLS(3);
|
||||
return vformat(R"(The %s '%s' has the same name as a %s.)", symbols[0], symbols[1], symbols[2]);
|
||||
}
|
||||
case WARNING_MAX:
|
||||
break; // Can't happen, but silences warning
|
||||
}
|
||||
@ -194,6 +198,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
|
||||
"ASSERT_ALWAYS_FALSE",
|
||||
"REDUNDANT_AWAIT",
|
||||
"EMPTY_FILE",
|
||||
"SHADOWED_GLOBAL_IDENTIFIER",
|
||||
};
|
||||
|
||||
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
|
||||
|
@ -69,6 +69,7 @@ public:
|
||||
ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false.
|
||||
REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
|
||||
EMPTY_FILE, // A script file is empty.
|
||||
SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
|
||||
WARNING_MAX,
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,2 @@
|
||||
func test():
|
||||
var abs = "This variable has the same name as the built-in function."
|
@ -0,0 +1,9 @@
|
||||
GDTEST_OK
|
||||
>> WARNING
|
||||
>> Line: 2
|
||||
>> SHADOWED_GLOBAL_IDENTIFIER
|
||||
>> The local variable 'abs' has the same name as a built-in function.
|
||||
>> WARNING
|
||||
>> Line: 2
|
||||
>> UNUSED_VARIABLE
|
||||
>> The local variable 'abs' is declared but never used in the block. If this is intended, prefix it with an underscore: '_abs'
|
Loading…
Reference in New Issue
Block a user