Browse Source

Merge pull request #54949 from Chaosus/fix_warning

Rémi Verschelde 3 years ago
parent
commit
3ba2d17d2f

+ 3 - 0
doc/classes/ProjectSettings.xml

@@ -372,6 +372,9 @@
 		<member name="debug/gdscript/warnings/return_value_discarded" type="bool" setter="" getter="" default="true">
 		<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.
 			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>
+		<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">
 		<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).
 			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>
 		</member>

+ 24 - 27
modules/gdscript/gdscript_parser.cpp

@@ -741,20 +741,22 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
 
 
 	if (member->identifier != nullptr) {
 	if (member->identifier != nullptr) {
 		if (!((String)member->identifier->name).is_empty()) { // Enums may be unnamed.
 		if (!((String)member->identifier->name).is_empty()) { // Enums may be unnamed.
+
+#ifdef DEBUG_ENABLED
 			List<MethodInfo> gdscript_funcs;
 			List<MethodInfo> gdscript_funcs;
 			GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
 			GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
 			for (MethodInfo &info : gdscript_funcs) {
 			for (MethodInfo &info : gdscript_funcs) {
 				if (info.name == member->identifier->name) {
 				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)) {
 			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);
 				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 {
 			} else {
 				current_class->add_member(member);
 				current_class->add_member(member);
 			}
 			}
@@ -827,21 +829,18 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
 
 
 	GDScriptParser::IdentifierNode *identifier = parse_identifier();
 	GDScriptParser::IdentifierNode *identifier = parse_identifier();
 
 
+#ifdef DEBUG_ENABLED
 	List<MethodInfo> gdscript_funcs;
 	List<MethodInfo> gdscript_funcs;
 	GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
 	GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
 	for (MethodInfo &info : gdscript_funcs) {
 	for (MethodInfo &info : gdscript_funcs) {
 		if (info.name == identifier->name) {
 		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)) {
 	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>();
 	VariableNode *variable = alloc_node<VariableNode>();
 	variable->identifier = identifier;
 	variable->identifier = identifier;
@@ -1099,22 +1098,20 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
 	}
 	}
 
 
 	GDScriptParser::IdentifierNode *identifier = parse_identifier();
 	GDScriptParser::IdentifierNode *identifier = parse_identifier();
-
+#ifdef DEBUG_ENABLED
 	List<MethodInfo> gdscript_funcs;
 	List<MethodInfo> gdscript_funcs;
 	GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
 	GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
 	for (MethodInfo &info : gdscript_funcs) {
 	for (MethodInfo &info : gdscript_funcs) {
 		if (info.name == identifier->name) {
 		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)) {
 	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)) {
 	} 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>();
 	ParameterNode *parameter = alloc_node<ParameterNode>();
 	parameter->identifier = identifier;
 	parameter->identifier = identifier;
@@ -1195,8 +1192,10 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
 
 
 	HashMap<StringName, int> elements;
 	HashMap<StringName, int> elements;
 
 
+#ifdef DEBUG_ENABLED
 	List<MethodInfo> gdscript_funcs;
 	List<MethodInfo> gdscript_funcs;
 	GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
 	GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+#endif
 
 
 	do {
 	do {
 		if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
 		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.)")) {
 		if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) {
 			EnumNode::Value item;
 			EnumNode::Value item;
 			GDScriptParser::IdentifierNode *identifier = parse_identifier();
 			GDScriptParser::IdentifierNode *identifier = parse_identifier();
-
+#ifdef DEBUG_ENABLED
 			for (MethodInfo &info : gdscript_funcs) {
 			for (MethodInfo &info : gdscript_funcs) {
 				if (info.name == identifier->name) {
 				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)) {
 			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)) {
 			} 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.identifier = identifier;
 			item.parent_enum = enum_node;
 			item.parent_enum = enum_node;
 			item.line = previous.start_line;
 			item.line = previous.start_line;

+ 5 - 0
modules/gdscript/gdscript_warning.cpp

@@ -148,6 +148,10 @@ String GDScriptWarning::get_message() const {
 		case EMPTY_FILE: {
 		case EMPTY_FILE: {
 			return "Empty script 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:
 		case WARNING_MAX:
 			break; // Can't happen, but silences warning
 			break; // Can't happen, but silences warning
 	}
 	}
@@ -194,6 +198,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
 		"ASSERT_ALWAYS_FALSE",
 		"ASSERT_ALWAYS_FALSE",
 		"REDUNDANT_AWAIT",
 		"REDUNDANT_AWAIT",
 		"EMPTY_FILE",
 		"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.");
 	static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");

+ 1 - 0
modules/gdscript/gdscript_warning.h

@@ -69,6 +69,7 @@ public:
 		ASSERT_ALWAYS_FALSE, // Expression for assert argument is always false.
 		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).
 		REDUNDANT_AWAIT, // await is used but expression is synchronous (not a signal nor a coroutine).
 		EMPTY_FILE, // A script file is empty.
 		EMPTY_FILE, // A script file is empty.
+		SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable.
 		WARNING_MAX,
 		WARNING_MAX,
 	};
 	};
 
 

+ 2 - 0
modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.gd

@@ -0,0 +1,2 @@
+func test():
+	var abs = "This variable has the same name as the built-in function."

+ 9 - 0
modules/gdscript/tests/scripts/parser/warnings/shadowed_global_identifier.out

@@ -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'