Преглед изворни кода

Merge pull request #70464 from vonagam/unify-assignables

Unify typing of variables, constants and parameters in GDScript
Rémi Verschelde пре 2 година
родитељ
комит
95ce236b7d

+ 134 - 305
modules/gdscript/gdscript_analyzer.cpp

@@ -758,80 +758,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
 		switch (member.type) {
 			case GDScriptParser::ClassNode::Member::VARIABLE: {
 				check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable);
-
 				member.variable->set_datatype(resolving_datatype);
-
-				GDScriptParser::DataType datatype;
-				datatype.kind = GDScriptParser::DataType::VARIANT;
-				datatype.type_source = GDScriptParser::DataType::UNDETECTED;
-
-				GDScriptParser::DataType specified_type;
-				if (member.variable->datatype_specifier != nullptr) {
-					specified_type = resolve_datatype(member.variable->datatype_specifier);
-					specified_type.is_meta_type = false;
-				}
-
-				if (member.variable->initializer != nullptr) {
-					reduce_expression(member.variable->initializer);
-					if ((member.variable->infer_datatype || (member.variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && member.variable->initializer->type == GDScriptParser::Node::ARRAY) {
-						// Typed array.
-						GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.variable->initializer);
-						// Can only infer typed array if it has elements.
-						if ((member.variable->infer_datatype && array->elements.size() > 0) || member.variable->datatype_specifier != nullptr) {
-							update_array_literal_element_type(specified_type, array);
-						}
-					}
-					datatype = member.variable->initializer->get_datatype();
-
-					if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) {
-						datatype.type_source = GDScriptParser::DataType::INFERRED;
-					}
-
-					if (!datatype.is_set()) {
-						push_error(vformat(R"(Could not resolve initializer for member "%s".)", member.variable->identifier->name), member.variable->initializer);
-						datatype.kind = GDScriptParser::DataType::VARIANT;
-					}
-				}
-
-				if (member.variable->datatype_specifier != nullptr) {
-					datatype = specified_type;
-
-					if (member.variable->initializer != nullptr) {
-						if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
-							// Try reverse test since it can be a masked subtype.
-							if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
-								push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
-							} else {
-								// TODO: Add warning.
-								mark_node_unsafe(member.variable->initializer);
-								member.variable->use_conversion_assign = true;
-							}
-						} else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
-#ifdef DEBUG_ENABLED
-							parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
-						}
-						if (member.variable->initializer->get_datatype().is_variant()) {
-							// TODO: Warn unsafe assign.
-							mark_node_unsafe(member.variable->initializer);
-							member.variable->use_conversion_assign = true;
-						}
-					}
-				} else if (member.variable->infer_datatype) {
-					if (member.variable->initializer == nullptr) {
-						push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
-					} else if (!datatype.is_set() || datatype.has_no_type()) {
-						push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
-					} else if (datatype.is_variant()) {
-						push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
-					} else if (datatype.builtin_type == Variant::NIL) {
-						push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
-					}
-					datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
-				}
-
-				datatype.is_constant = false;
-				member.variable->set_datatype(datatype);
+				resolve_variable(member.variable, false);
 
 				// Apply annotations.
 				for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
@@ -840,56 +768,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
 			} break;
 			case GDScriptParser::ClassNode::Member::CONSTANT: {
 				check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant);
-
 				member.constant->set_datatype(resolving_datatype);
-
-				GDScriptParser::DataType specified_type;
-				if (member.constant->datatype_specifier != nullptr) {
-					specified_type = resolve_datatype(member.constant->datatype_specifier);
-					specified_type.is_meta_type = false;
-				}
-
-				GDScriptParser::DataType datatype;
-				if (member.constant->initializer) {
-					reduce_expression(member.constant->initializer);
-					datatype = member.constant->initializer->get_datatype();
-
-					if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) {
-						GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer);
-						const_fold_array(array);
-
-						// Can only infer typed array if it has elements.
-						if (array->elements.size() > 0 || (member.constant->datatype_specifier != nullptr && specified_type.has_container_element_type())) {
-							update_array_literal_element_type(specified_type, array);
-						}
-					} else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
-						const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer));
-					}
-
-					if (!datatype.is_set()) {
-						push_error(vformat(R"(Could not resolve initializer for member "%s".)", member.constant->identifier->name), member.constant->initializer);
-						datatype.kind = GDScriptParser::DataType::VARIANT;
-					}
-
-					if (!member.constant->initializer->is_constant) {
-						push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer);
-					}
-
-					if (member.constant->datatype_specifier != nullptr) {
-						datatype = specified_type;
-
-						if (!is_type_compatible(datatype, member.constant->initializer->get_datatype(), true)) {
-							push_error(vformat(R"(Value of type "%s" cannot be initialized to constant of type "%s".)", member.constant->initializer->get_datatype().to_string(), datatype.to_string()), member.constant->initializer);
-						} else if (datatype.builtin_type == Variant::INT && member.constant->initializer->get_datatype().builtin_type == Variant::FLOAT) {
-#ifdef DEBUG_ENABLED
-							parser->push_warning(member.constant->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
-						}
-					}
-				}
-				datatype.is_constant = true;
-
-				member.constant->set_datatype(datatype);
+				resolve_constant(member.constant, false);
 
 				// Apply annotations.
 				for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) {
@@ -1310,7 +1190,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root
 			}
 			break;
 		case GDScriptParser::Node::CONSTANT:
-			resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node));
+			resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node), true);
 			break;
 		case GDScriptParser::Node::FOR:
 			resolve_for(static_cast<GDScriptParser::ForNode *>(p_node));
@@ -1326,7 +1206,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root
 			resolve_suite(static_cast<GDScriptParser::SuiteNode *>(p_node));
 			break;
 		case GDScriptParser::Node::VARIABLE:
-			resolve_variable(static_cast<GDScriptParser::VariableNode *>(p_node));
+			resolve_variable(static_cast<GDScriptParser::VariableNode *>(p_node), true);
 			break;
 		case GDScriptParser::Node::WHILE:
 			resolve_while(static_cast<GDScriptParser::WhileNode *>(p_node));
@@ -1426,11 +1306,11 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
 		is_shadowing(p_function->parameters[i]->identifier, "function parameter");
 #endif // DEBUG_ENABLED
 #ifdef TOOLS_ENABLED
-		if (p_function->parameters[i]->default_value) {
+		if (p_function->parameters[i]->initializer) {
 			default_value_count++;
 
-			if (p_function->parameters[i]->default_value->is_constant) {
-				p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
+			if (p_function->parameters[i]->initializer->is_constant) {
+				p_function->default_arg_values.push_back(p_function->parameters[i]->initializer->reduced_value);
 			} else {
 				p_function->default_arg_values.push_back(Variant()); // Prevent shift.
 			}
@@ -1601,6 +1481,132 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
 	}
 }
 
+void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assignable, const char *p_kind) {
+	GDScriptParser::DataType type;
+	type.kind = GDScriptParser::DataType::VARIANT;
+
+	bool is_variable = p_assignable->type == GDScriptParser::Node::VARIABLE;
+	bool is_constant = p_assignable->type == GDScriptParser::Node::CONSTANT;
+
+	GDScriptParser::DataType specified_type;
+	bool has_specified_type = p_assignable->datatype_specifier != nullptr;
+	if (has_specified_type) {
+		specified_type = resolve_datatype(p_assignable->datatype_specifier);
+		specified_type.is_meta_type = false;
+		type = specified_type;
+	}
+
+	if (p_assignable->initializer != nullptr) {
+		reduce_expression(p_assignable->initializer);
+
+		if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
+			GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer);
+			if ((p_assignable->infer_datatype && array->elements.size() > 0) || (has_specified_type && specified_type.has_container_element_type())) {
+				update_array_literal_element_type(specified_type, array);
+			}
+		}
+
+		if (is_constant) {
+			if (p_assignable->initializer->type == GDScriptParser::Node::ARRAY) {
+				const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_assignable->initializer));
+			} else if (p_assignable->initializer->type == GDScriptParser::Node::DICTIONARY) {
+				const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_assignable->initializer));
+			}
+			if (!p_assignable->initializer->is_constant) {
+				push_error(vformat(R"(Assigned value for %s "%s" isn't a constant expression.)", p_kind, p_assignable->identifier->name), p_assignable->initializer);
+			}
+		}
+
+		GDScriptParser::DataType initializer_type = p_assignable->initializer->get_datatype();
+
+		if (p_assignable->infer_datatype) {
+			if (!initializer_type.is_set() || initializer_type.has_no_type()) {
+				push_error(vformat(R"(Cannot infer the type of "%s" %s because the value doesn't have a set type.)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
+			} else if (initializer_type.is_variant() && !initializer_type.is_hard_type()) {
+				push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is Variant. Use explicit "Variant" type if this is intended.)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
+			} else if (initializer_type.kind == GDScriptParser::DataType::BUILTIN && initializer_type.builtin_type == Variant::NIL && !is_constant) {
+				push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is "null".)", p_assignable->identifier->name, p_kind), p_assignable->initializer);
+			}
+		} else {
+			if (!initializer_type.is_set()) {
+				push_error(vformat(R"(Could not resolve type for %s "%s".)", p_kind, p_assignable->identifier->name), p_assignable->initializer);
+			}
+		}
+
+		if (!has_specified_type) {
+			type = initializer_type;
+
+			if (!type.is_set() || (type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL && !is_constant)) {
+				type.kind = GDScriptParser::DataType::VARIANT;
+			}
+
+			if (p_assignable->infer_datatype || is_constant) {
+				type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+			} else {
+				type.type_source = GDScriptParser::DataType::INFERRED;
+			}
+		} else if (!specified_type.is_variant()) {
+			if (initializer_type.is_variant() || !initializer_type.is_hard_type()) {
+				mark_node_unsafe(p_assignable->initializer);
+				if (is_variable) {
+					static_cast<GDScriptParser::VariableNode *>(p_assignable)->use_conversion_assign = true;
+				}
+			} else if (!is_type_compatible(specified_type, initializer_type, true, p_assignable->initializer)) {
+				if (is_variable && is_type_compatible(initializer_type, specified_type, true, p_assignable->initializer)) {
+					mark_node_unsafe(p_assignable->initializer);
+					static_cast<GDScriptParser::VariableNode *>(p_assignable)->use_conversion_assign = true;
+				} else {
+					push_error(vformat(R"(Cannot assign a value of type %s to %s "%s" with specified type %s.)", initializer_type.to_string(), p_kind, p_assignable->identifier->name, specified_type.to_string()), p_assignable->initializer);
+				}
+#ifdef DEBUG_ENABLED
+			} else if (specified_type.builtin_type == Variant::INT && initializer_type.builtin_type == Variant::FLOAT) {
+				parser->push_warning(p_assignable->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
+			}
+		}
+	}
+
+	type.is_constant = is_constant;
+	p_assignable->set_datatype(type);
+}
+
+void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable, bool p_is_local) {
+	static constexpr const char *kind = "variable";
+	resolve_assignable(p_variable, kind);
+
+#ifdef DEBUG_ENABLED
+	if (p_is_local) {
+		if (p_variable->usages == 0 && !String(p_variable->identifier->name).begins_with("_")) {
+			parser->push_warning(p_variable, GDScriptWarning::UNUSED_VARIABLE, p_variable->identifier->name);
+		} else if (p_variable->assignments == 0) {
+			parser->push_warning(p_variable, GDScriptWarning::UNASSIGNED_VARIABLE, p_variable->identifier->name);
+		}
+
+		is_shadowing(p_variable->identifier, kind);
+	}
+#endif
+}
+
+void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant, bool p_is_local) {
+	static constexpr const char *kind = "constant";
+	resolve_assignable(p_constant, kind);
+
+#ifdef DEBUG_ENABLED
+	if (p_is_local) {
+		if (p_constant->usages == 0) {
+			parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
+		}
+
+		is_shadowing(p_constant->identifier, kind);
+	}
+#endif
+}
+
+void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parameter) {
+	static constexpr const char *kind = "parameter";
+	resolve_assignable(p_parameter, kind);
+}
+
 void GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode *p_if) {
 	reduce_expression(p_if->condition);
 
@@ -1728,148 +1734,6 @@ void GDScriptAnalyzer::resolve_while(GDScriptParser::WhileNode *p_while) {
 	p_while->set_datatype(p_while->loop->get_datatype());
 }
 
-void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable) {
-	GDScriptParser::DataType type;
-	type.kind = GDScriptParser::DataType::VARIANT; // By default.
-
-	GDScriptParser::DataType specified_type;
-	if (p_variable->datatype_specifier != nullptr) {
-		specified_type = resolve_datatype(p_variable->datatype_specifier);
-		specified_type.is_meta_type = false;
-	}
-
-	if (p_variable->initializer != nullptr) {
-		reduce_expression(p_variable->initializer);
-		if ((p_variable->infer_datatype || (p_variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && p_variable->initializer->type == GDScriptParser::Node::ARRAY) {
-			// Typed array.
-			GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_variable->initializer);
-			// Can only infer typed array if it has elements.
-			if ((p_variable->infer_datatype && array->elements.size() > 0) || p_variable->datatype_specifier != nullptr) {
-				update_array_literal_element_type(specified_type, array);
-			}
-		}
-
-		type = p_variable->initializer->get_datatype();
-
-		if (p_variable->infer_datatype) {
-			type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
-
-			if (type.has_no_type()) {
-				push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value does not have a set type.)", p_variable->identifier->name), p_variable->initializer);
-			} else if (type.is_variant()) {
-				push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is a variant. Use explicit "Variant" type if this is intended.)", p_variable->identifier->name), p_variable->initializer);
-			} else if (type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
-				push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_variable->identifier->name), p_variable->initializer);
-			}
-		} else {
-			type.type_source = GDScriptParser::DataType::INFERRED;
-		}
-	}
-
-	if (p_variable->datatype_specifier != nullptr) {
-		type = specified_type;
-		type.is_meta_type = false;
-
-		if (p_variable->initializer != nullptr) {
-			if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true, p_variable->initializer)) {
-				// Try reverse test since it can be a masked subtype.
-				if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true, p_variable->initializer)) {
-					push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer);
-				} else {
-					// TODO: Add warning.
-					mark_node_unsafe(p_variable->initializer);
-					p_variable->use_conversion_assign = true;
-				}
-#ifdef DEBUG_ENABLED
-			} else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
-				parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
-			}
-			if (p_variable->initializer->get_datatype().is_variant() && !type.is_variant()) {
-				// TODO: Warn unsafe assign.
-				mark_node_unsafe(p_variable->initializer);
-				p_variable->use_conversion_assign = true;
-			}
-		}
-	} else if (p_variable->infer_datatype) {
-		if (type.has_no_type()) {
-			push_error(vformat(R"(Cannot infer the type of variable "%s" because the initial value doesn't have a set type.)", p_variable->identifier->name), p_variable->identifier);
-		}
-		type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
-	}
-
-	type.is_constant = false;
-	p_variable->set_datatype(type);
-
-#ifdef DEBUG_ENABLED
-	if (p_variable->usages == 0 && !String(p_variable->identifier->name).begins_with("_")) {
-		parser->push_warning(p_variable, GDScriptWarning::UNUSED_VARIABLE, p_variable->identifier->name);
-	} else if (p_variable->assignments == 0) {
-		parser->push_warning(p_variable, GDScriptWarning::UNASSIGNED_VARIABLE, p_variable->identifier->name);
-	}
-
-	is_shadowing(p_variable->identifier, "variable");
-#endif
-}
-
-void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) {
-	GDScriptParser::DataType type;
-
-	GDScriptParser::DataType explicit_type;
-	if (p_constant->datatype_specifier != nullptr) {
-		explicit_type = resolve_datatype(p_constant->datatype_specifier);
-		explicit_type.is_meta_type = false;
-	}
-
-	if (p_constant->initializer != nullptr) {
-		reduce_expression(p_constant->initializer);
-		if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
-			GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer);
-			const_fold_array(array);
-
-			// Can only infer typed array if it has elements.
-			if (array->elements.size() > 0 || (p_constant->datatype_specifier != nullptr && explicit_type.has_container_element_type())) {
-				update_array_literal_element_type(explicit_type, array);
-			}
-		} else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
-			const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
-		}
-
-		if (!p_constant->initializer->is_constant) {
-			push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
-		}
-
-		type = p_constant->initializer->get_datatype();
-	}
-
-	if (p_constant->datatype_specifier != nullptr) {
-		if (!is_type_compatible(explicit_type, type, true)) {
-			push_error(vformat(R"(Assigned value for constant "%s" has type %s which is not compatible with defined type %s.)", p_constant->identifier->name, type.to_string(), explicit_type.to_string()), p_constant->initializer);
-#ifdef DEBUG_ENABLED
-		} else if (explicit_type.builtin_type == Variant::INT && type.builtin_type == Variant::FLOAT) {
-			parser->push_warning(p_constant->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
-		}
-		type = explicit_type;
-	} else if (p_constant->infer_datatype) {
-		if (type.has_no_type()) {
-			push_error(vformat(R"(Cannot infer the type of constant "%s" because the initial value doesn't have a set type.)", p_constant->identifier->name), p_constant->identifier);
-		}
-		type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
-	}
-
-	type.is_constant = true;
-	p_constant->set_datatype(type);
-
-#ifdef DEBUG_ENABLED
-	if (p_constant->usages == 0) {
-		parser->push_warning(p_constant, GDScriptWarning::UNUSED_LOCAL_CONSTANT, p_constant->identifier->name);
-	}
-
-	is_shadowing(p_constant->identifier, "constant");
-#endif
-}
-
 void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
 	reduce_expression(p_assert->condition);
 	if (p_assert->message != nullptr) {
@@ -1981,41 +1845,6 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
 	p_match_pattern->set_datatype(result);
 }
 
-void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parameter) {
-	GDScriptParser::DataType result;
-	result.kind = GDScriptParser::DataType::VARIANT;
-
-	if (p_parameter->default_value != nullptr) {
-		reduce_expression(p_parameter->default_value);
-		result = p_parameter->default_value->get_datatype();
-		if (p_parameter->infer_datatype) {
-			result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
-		} else {
-			result.type_source = GDScriptParser::DataType::INFERRED;
-		}
-	}
-
-	if (p_parameter->datatype_specifier != nullptr) {
-		result = resolve_datatype(p_parameter->datatype_specifier);
-		result.is_meta_type = false;
-
-		if (p_parameter->default_value != nullptr) {
-			if (!is_type_compatible(result, p_parameter->default_value->get_datatype())) {
-				push_error(vformat(R"(Type of default value for parameter "%s" (%s) is not compatible with parameter type (%s).)", p_parameter->identifier->name, p_parameter->default_value->get_datatype().to_string(), p_parameter->datatype_specifier->get_datatype().to_string()), p_parameter->default_value);
-			} else if (p_parameter->default_value->get_datatype().is_variant()) {
-				mark_node_unsafe(p_parameter);
-			}
-		}
-	}
-
-	if (result.builtin_type == Variant::Type::NIL && result.type_source == GDScriptParser::DataType::ANNOTATED_INFERRED && p_parameter->datatype_specifier == nullptr) {
-		push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_parameter->identifier->name), p_parameter->default_value);
-	}
-
-	result.is_constant = false;
-	p_parameter->set_datatype(result);
-}
-
 void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
 	GDScriptParser::DataType result;
 
@@ -4171,7 +4000,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
 		r_static = p_is_constructor || found_function->is_static;
 		for (int i = 0; i < found_function->parameters.size(); i++) {
 			r_par_types.push_back(found_function->parameters[i]->get_datatype());
-			if (found_function->parameters[i]->default_value != nullptr) {
+			if (found_function->parameters[i]->initializer != nullptr) {
 				r_default_arg_count++;
 			}
 		}

+ 4 - 3
modules/gdscript/gdscript_analyzer.h

@@ -69,16 +69,17 @@ class GDScriptAnalyzer {
 	void resolve_function_body(GDScriptParser::FunctionNode *p_function);
 	void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true);
 	void resolve_suite(GDScriptParser::SuiteNode *p_suite);
+	void resolve_assignable(GDScriptParser::AssignableNode *p_assignable, const char *p_kind);
+	void resolve_variable(GDScriptParser::VariableNode *p_variable, bool p_is_local);
+	void resolve_constant(GDScriptParser::ConstantNode *p_constant, bool p_is_local);
+	void resolve_parameter(GDScriptParser::ParameterNode *p_parameter);
 	void resolve_if(GDScriptParser::IfNode *p_if);
 	void resolve_for(GDScriptParser::ForNode *p_for);
 	void resolve_while(GDScriptParser::WhileNode *p_while);
-	void resolve_variable(GDScriptParser::VariableNode *p_variable);
-	void resolve_constant(GDScriptParser::ConstantNode *p_constant);
 	void resolve_assert(GDScriptParser::AssertNode *p_assert);
 	void resolve_match(GDScriptParser::MatchNode *p_match);
 	void resolve_match_branch(GDScriptParser::MatchBranchNode *p_match_branch, GDScriptParser::ExpressionNode *p_match_test);
 	void resolve_match_pattern(GDScriptParser::PatternNode *p_match_pattern, GDScriptParser::ExpressionNode *p_match_test);
-	void resolve_parameter(GDScriptParser::ParameterNode *p_parameter);
 	void resolve_return(GDScriptParser::ReturnNode *p_return);
 
 	// Reduction functions.

+ 3 - 3
modules/gdscript/gdscript_compiler.cpp

@@ -2022,10 +2022,10 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
 		for (int i = 0; i < p_func->parameters.size(); i++) {
 			const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
 			GDScriptDataType par_type = _gdtype_from_datatype(parameter->get_datatype(), p_script);
-			uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->default_value != nullptr, par_type);
+			uint32_t par_addr = codegen.generator->add_parameter(parameter->identifier->name, parameter->initializer != nullptr, par_type);
 			codegen.parameters[parameter->identifier->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, par_type);
 
-			if (p_func->parameters[i]->default_value != nullptr) {
+			if (p_func->parameters[i]->initializer != nullptr) {
 				optional_parameters++;
 			}
 		}
@@ -2097,7 +2097,7 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
 			codegen.generator->start_parameters();
 			for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
 				const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
-				GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value);
+				GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->initializer);
 				if (r_error) {
 					memdelete(codegen.generator);
 					return nullptr;

+ 13 - 13
modules/gdscript/gdscript_editor.cpp

@@ -683,37 +683,37 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
 			arghint += par->identifier->name.operator String() + ": " + par->get_datatype().to_string();
 		}
 
-		if (par->default_value) {
+		if (par->initializer) {
 			String def_val = "<unknown>";
-			switch (par->default_value->type) {
+			switch (par->initializer->type) {
 				case GDScriptParser::Node::LITERAL: {
-					const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->default_value);
+					const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->initializer);
 					def_val = literal->value.get_construct_string();
 				} break;
 				case GDScriptParser::Node::IDENTIFIER: {
-					const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->default_value);
+					const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->initializer);
 					def_val = id->name.operator String();
 				} break;
 				case GDScriptParser::Node::CALL: {
-					const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->default_value);
+					const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->initializer);
 					if (call->is_constant && call->reduced) {
 						def_val = call->function_name.operator String() + call->reduced_value.operator String();
 					}
 				} break;
 				case GDScriptParser::Node::ARRAY: {
-					const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->default_value);
+					const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->initializer);
 					if (arr->is_constant && arr->reduced) {
 						def_val = arr->reduced_value.operator String();
 					}
 				} break;
 				case GDScriptParser::Node::DICTIONARY: {
-					const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->default_value);
+					const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->initializer);
 					if (dict->is_constant && dict->reduced) {
 						def_val = dict->reduced_value.operator String();
 					}
 				} break;
 				case GDScriptParser::Node::SUBSCRIPT: {
-					const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->default_value);
+					const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->initializer);
 					if (sub->is_constant) {
 						if (sub->datatype.kind == GDScriptParser::DataType::ENUM) {
 							def_val = sub->get_datatype().to_string();
@@ -1856,9 +1856,9 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
 					}
 					break;
 				case GDScriptParser::SuiteNode::Local::PARAMETER:
-					if (local.parameter->default_value) {
-						last_assign_line = local.parameter->default_value->end_line;
-						last_assigned_expression = local.parameter->default_value;
+					if (local.parameter->initializer) {
+						last_assign_line = local.parameter->initializer->end_line;
+						last_assigned_expression = local.parameter->initializer;
 					}
 					is_function_parameter = true;
 					break;
@@ -1939,12 +1939,12 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
 						if ((!id_type.is_set() || id_type.is_variant()) && parameter->get_datatype().is_hard_type()) {
 							id_type = parameter->get_datatype();
 						}
-						if (parameter->default_value) {
+						if (parameter->initializer) {
 							GDScriptParser::CompletionContext c = p_context;
 							c.current_function = parent_function;
 							c.current_class = base_type.class_type;
 							c.base = nullptr;
-							if (_guess_expression_type(c, parameter->default_value, r_type)) {
+							if (_guess_expression_type(c, parameter->initializer, r_type)) {
 								return true;
 							}
 						}

+ 5 - 5
modules/gdscript/gdscript_parser.cpp

@@ -1219,7 +1219,7 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
 
 	if (match(GDScriptTokenizer::Token::EQUAL)) {
 		// Default value.
-		parameter->default_value = parse_expression(false);
+		parameter->initializer = parse_expression(false);
 	}
 
 	complete_extents(parameter);
@@ -1250,7 +1250,7 @@ GDScriptParser::SignalNode *GDScriptParser::parse_signal() {
 				push_error("Expected signal parameter name.");
 				break;
 			}
-			if (parameter->default_value != nullptr) {
+			if (parameter->initializer != nullptr) {
 				push_error(R"(Signal parameters cannot have a default value.)");
 			}
 			if (signal->parameters_indices.has(parameter->identifier->name)) {
@@ -1395,7 +1395,7 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
 			if (parameter == nullptr) {
 				break;
 			}
-			if (parameter->default_value != nullptr) {
+			if (parameter->initializer != nullptr) {
 				default_used = true;
 			} else {
 				if (default_used) {
@@ -4776,9 +4776,9 @@ void GDScriptParser::TreePrinter::print_parameter(ParameterNode *p_parameter) {
 		push_text(" : ");
 		print_type(p_parameter->datatype_specifier);
 	}
-	if (p_parameter->default_value != nullptr) {
+	if (p_parameter->initializer != nullptr) {
 		push_text(" = ");
-		print_expression(p_parameter->default_value);
+		print_expression(p_parameter->initializer);
 	}
 }
 

+ 17 - 20
modules/gdscript/gdscript_parser.h

@@ -57,6 +57,7 @@ public:
 	struct AnnotationNode;
 	struct ArrayNode;
 	struct AssertNode;
+	struct AssignableNode;
 	struct AssignmentNode;
 	struct AwaitNode;
 	struct BinaryOpNode;
@@ -354,6 +355,19 @@ public:
 		}
 	};
 
+	struct AssignableNode : public Node {
+		IdentifierNode *identifier = nullptr;
+		ExpressionNode *initializer = nullptr;
+		TypeNode *datatype_specifier = nullptr;
+		bool infer_datatype = false;
+		int usages = 0;
+
+		virtual ~AssignableNode() {}
+
+	protected:
+		AssignableNode() {}
+	};
+
 	struct AssignmentNode : public ExpressionNode {
 		// Assignment is not really an expression but it's easier to parse as if it were.
 		enum Operation {
@@ -732,12 +746,7 @@ public:
 		}
 	};
 
-	struct ConstantNode : public Node {
-		IdentifierNode *identifier = nullptr;
-		ExpressionNode *initializer = nullptr;
-		TypeNode *datatype_specifier = nullptr;
-		bool infer_datatype = false;
-		int usages = 0;
+	struct ConstantNode : public AssignableNode {
 #ifdef TOOLS_ENABLED
 		String doc_description;
 #endif // TOOLS_ENABLED
@@ -902,13 +911,7 @@ public:
 		}
 	};
 
-	struct ParameterNode : public Node {
-		IdentifierNode *identifier = nullptr;
-		ExpressionNode *default_value = nullptr;
-		TypeNode *datatype_specifier = nullptr;
-		bool infer_datatype = false;
-		int usages = 0;
-
+	struct ParameterNode : public AssignableNode {
 		ParameterNode() {
 			type = PARAMETER;
 		}
@@ -1157,18 +1160,13 @@ public:
 		}
 	};
 
-	struct VariableNode : public Node {
+	struct VariableNode : public AssignableNode {
 		enum PropertyStyle {
 			PROP_NONE,
 			PROP_INLINE,
 			PROP_SETGET,
 		};
 
-		IdentifierNode *identifier = nullptr;
-		ExpressionNode *initializer = nullptr;
-		TypeNode *datatype_specifier = nullptr;
-		bool infer_datatype = false;
-
 		PropertyStyle property = PROP_NONE;
 		union {
 			FunctionNode *setter = nullptr;
@@ -1184,7 +1182,6 @@ public:
 		bool onready = false;
 		PropertyInfo export_info;
 		int assignments = 0;
-		int usages = 0;
 		bool use_conversion_assign = false;
 #ifdef TOOLS_ENABLED
 		String doc_description;

+ 4 - 4
modules/gdscript/language_server/gdscript_extend_parser.cpp

@@ -350,8 +350,8 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
 		if (parameter->get_datatype().is_hard_type()) {
 			parameters += ": " + parameter->get_datatype().to_string();
 		}
-		if (parameter->default_value != nullptr) {
-			parameters += " = " + parameter->default_value->reduced_value.to_json_string();
+		if (parameter->initializer != nullptr) {
+			parameters += " = " + parameter->initializer->reduced_value.to_json_string();
 		}
 	}
 	r_symbol.detail += parameters + ")";
@@ -695,8 +695,8 @@ Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::Functio
 		Dictionary arg;
 		arg["name"] = p_func->parameters[i]->identifier->name;
 		arg["type"] = p_func->parameters[i]->get_datatype().to_string();
-		if (p_func->parameters[i]->default_value != nullptr) {
-			arg["default_value"] = p_func->parameters[i]->default_value->reduced_value;
+		if (p_func->parameters[i]->initializer != nullptr) {
+			arg["default_value"] = p_func->parameters[i]->initializer->reduced_value;
 		}
 		parameters.push_back(arg);
 	}

+ 1 - 1
modules/gdscript/tests/scripts/analyzer/errors/enum_class_var_init_with_wrong_enum_type.out

@@ -1,2 +1,2 @@
 GDTEST_ANALYZER_ERROR
-Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
+Cannot assign a value of type MyOtherEnum (enum) to variable "class_var" with specified type MyEnum (enum).

+ 1 - 1
modules/gdscript/tests/scripts/analyzer/errors/enum_local_var_init_with_wrong_enum_type.out

@@ -1,2 +1,2 @@
 GDTEST_ANALYZER_ERROR
-Value of type "MyOtherEnum (enum)" cannot be assigned to a variable of type "MyEnum (enum)".
+Cannot assign a value of type MyOtherEnum (enum) to variable "local_var" with specified type MyEnum (enum).

+ 1 - 1
modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out

@@ -1,2 +1,2 @@
 GDTEST_ANALYZER_ERROR
-Value of type "int" cannot be assigned to a variable of type "String".
+Cannot assign a value of type int to variable "x" with specified type String.

+ 6 - 0
modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.gd

@@ -0,0 +1,6 @@
+func check(arg: float = 3):
+	return typeof(arg) == typeof(3.0)
+
+func test():
+	if check():
+		print('ok')

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/features/default_arg_convertable.out

@@ -0,0 +1,2 @@
+GDTEST_OK
+ok

+ 32 - 0
modules/gdscript/tests/scripts/analyzer/features/null_initializer.gd

@@ -0,0 +1,32 @@
+func check(input: int) -> bool:
+	return input == 1
+
+var recur = null
+var prop = null
+
+func check_arg(arg = null) -> void:
+	if arg != null:
+		print(check(arg))
+
+func check_recur() -> void:
+	if recur != null:
+		print(check(recur))
+	else:
+		recur = 1
+		check_recur()
+
+func test() -> void:
+	check_arg(1)
+
+	check_recur()
+
+	if prop == null:
+		set('prop', 1)
+		print(check(prop))
+		set('prop', null)
+
+	var loop = null
+	while loop != 2:
+		if loop != null:
+			print(check(loop))
+		loop = 1 if loop == null else 2

+ 5 - 0
modules/gdscript/tests/scripts/analyzer/features/null_initializer.out

@@ -0,0 +1,5 @@
+GDTEST_OK
+true
+true
+true
+true

+ 5 - 0
modules/gdscript/tests/scripts/analyzer/features/weak_initializer.gd

@@ -0,0 +1,5 @@
+func test():
+	var bar = 1
+	var foo: float = bar
+	print(typeof(foo))
+	print(foo is float)

+ 3 - 0
modules/gdscript/tests/scripts/analyzer/features/weak_initializer.out

@@ -0,0 +1,3 @@
+GDTEST_OK
+3
+true

+ 1 - 1
modules/gdscript/tests/scripts/analyzer/typed_array_assignment.out

@@ -1,2 +1,2 @@
 GDTEST_ANALYZER_ERROR
-Assigned value for constant "arr" has type Array[String] which is not compatible with defined type Array[int].
+Cannot assign a value of type Array[String] to constant "arr" with specified type Array[int].