summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/extension/gdextension_interface.cpp10
-rw-r--r--core/extension/gdextension_interface.h24
-rw-r--r--doc/classes/AStarGrid2D.xml9
-rw-r--r--editor/editor_node.cpp28
-rw-r--r--editor/editor_undo_redo_manager.cpp101
-rw-r--r--editor/editor_undo_redo_manager.h3
-rw-r--r--editor/project_manager.cpp2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp648
-rw-r--r--modules/gdscript/gdscript_analyzer.h19
-rw-r--r--modules/gdscript/gdscript_cache.cpp14
-rw-r--r--modules/gdscript/gdscript_cache.h1
-rw-r--r--modules/gdscript/gdscript_compiler.cpp1
-rw-r--r--modules/gdscript/gdscript_parser.cpp52
-rw-r--r--modules/gdscript/gdscript_parser.h82
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd12
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd50
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order.out15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd39
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd12
-rw-r--r--modules/mono/editor/bindings_generator.cpp5
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java2
-rw-r--r--scene/gui/rich_text_label.cpp7
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp16
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl16
42 files changed, 923 insertions, 324 deletions
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 94755bc4d4..2074495aa9 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -80,7 +80,7 @@ static void gdextension_variant_destroy(GDExtensionVariantPtr p_self) {
// variant type
-static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
+static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
Variant *self = (Variant *)p_self;
const StringName method = *reinterpret_cast<const StringName *>(p_method);
const Variant **args = (const Variant **)p_args;
@@ -96,7 +96,7 @@ static void gdextension_variant_call(GDExtensionVariantPtr p_self, GDExtensionCo
}
}
-static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
+static void gdextension_variant_call_static(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argcount, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
Variant::Type type = (Variant::Type)p_type;
const StringName method = *reinterpret_cast<const StringName *>(p_method);
const Variant **args = (const Variant **)p_args;
@@ -486,7 +486,7 @@ static GDExtensionPtrConstructor gdextension_variant_get_ptr_constructor(GDExten
static GDExtensionPtrDestructor gdextension_variant_get_ptr_destructor(GDExtensionVariantType p_type) {
return (GDExtensionPtrDestructor)Variant::get_ptr_destructor(Variant::Type(p_type));
}
-static void gdextension_variant_construct(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error) {
+static void gdextension_variant_construct(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error) {
memnew_placement(p_base, Variant);
Callable::CallError error;
@@ -814,7 +814,7 @@ static GDExtensionVariantPtr gdextension_dictionary_operator_index_const(GDExten
/* OBJECT API */
-static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
+static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
const MethodBind *mb = reinterpret_cast<const MethodBind *>(p_method_bind);
Object *o = (Object *)p_instance;
const Variant **args = (const Variant **)p_args;
@@ -830,7 +830,7 @@ static void gdextension_object_method_bind_call(GDExtensionMethodBindPtr p_metho
}
}
-static void gdextension_object_method_bind_ptrcall(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr p_ret) {
+static void gdextension_object_method_bind_ptrcall(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr p_ret) {
const MethodBind *mb = reinterpret_cast<const MethodBind *>(p_method_bind);
Object *o = (Object *)p_instance;
mb->ptrcall(o, (const void **)p_args, p_ret);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 66fca76aa8..0b7615ffdd 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -178,8 +178,8 @@ typedef struct {
typedef void (*GDExtensionVariantFromTypeConstructorFunc)(GDExtensionVariantPtr, GDExtensionTypePtr);
typedef void (*GDExtensionTypeFromVariantConstructorFunc)(GDExtensionTypePtr, GDExtensionVariantPtr);
typedef void (*GDExtensionPtrOperatorEvaluator)(GDExtensionConstTypePtr p_left, GDExtensionConstTypePtr p_right, GDExtensionTypePtr r_result);
-typedef void (*GDExtensionPtrBuiltInMethod)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return, int p_argument_count);
-typedef void (*GDExtensionPtrConstructor)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr *p_args);
+typedef void (*GDExtensionPtrBuiltInMethod)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return, int p_argument_count);
+typedef void (*GDExtensionPtrConstructor)(GDExtensionTypePtr p_base, const GDExtensionConstTypePtr *p_args);
typedef void (*GDExtensionPtrDestructor)(GDExtensionTypePtr p_base);
typedef void (*GDExtensionPtrSetter)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr p_value);
typedef void (*GDExtensionPtrGetter)(GDExtensionConstTypePtr p_base, GDExtensionTypePtr r_value);
@@ -188,7 +188,7 @@ typedef void (*GDExtensionPtrIndexedGetter)(GDExtensionConstTypePtr p_base, GDEx
typedef void (*GDExtensionPtrKeyedSetter)(GDExtensionTypePtr p_base, GDExtensionConstTypePtr p_key, GDExtensionConstTypePtr p_value);
typedef void (*GDExtensionPtrKeyedGetter)(GDExtensionConstTypePtr p_base, GDExtensionConstTypePtr p_key, GDExtensionTypePtr r_value);
typedef uint32_t (*GDExtensionPtrKeyedChecker)(GDExtensionConstVariantPtr p_base, GDExtensionConstVariantPtr p_key);
-typedef void (*GDExtensionPtrUtilityFunction)(GDExtensionTypePtr r_return, GDExtensionConstTypePtr *p_arguments, int p_argument_count);
+typedef void (*GDExtensionPtrUtilityFunction)(GDExtensionTypePtr r_return, const GDExtensionConstTypePtr *p_args, int p_argument_count);
typedef GDExtensionObjectPtr (*GDExtensionClassConstructor)();
@@ -242,7 +242,7 @@ typedef void (*GDExtensionClassNotification)(GDExtensionClassInstancePtr p_insta
typedef void (*GDExtensionClassToString)(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr p_out);
typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance);
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
-typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
+typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_userdata);
typedef void (*GDExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance);
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_userdata, GDExtensionConstStringNamePtr p_name);
@@ -295,8 +295,8 @@ typedef enum {
GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE
} GDExtensionClassMethodArgumentMetadata;
-typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
-typedef void (*GDExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
+typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
+typedef void (*GDExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
typedef struct {
GDExtensionStringNamePtr name;
@@ -344,7 +344,7 @@ typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanc
typedef GDExtensionBool (*GDExtensionScriptInstanceHasMethod)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name);
-typedef void (*GDExtensionScriptInstanceCall)(GDExtensionScriptInstanceDataPtr p_self, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
+typedef void (*GDExtensionScriptInstanceCall)(GDExtensionScriptInstanceDataPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
typedef void (*GDExtensionScriptInstanceNotification)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what);
typedef void (*GDExtensionScriptInstanceToString)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out);
@@ -429,8 +429,8 @@ typedef struct {
void (*variant_destroy)(GDExtensionVariantPtr p_self);
/* variant type */
- void (*variant_call)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
- void (*variant_call_static)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
+ void (*variant_call)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
+ void (*variant_call_static)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
void (*variant_evaluate)(GDExtensionVariantOperator p_op, GDExtensionConstVariantPtr p_a, GDExtensionConstVariantPtr p_b, GDExtensionVariantPtr r_return, GDExtensionBool *r_valid);
void (*variant_set)(GDExtensionVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid);
void (*variant_set_named)(GDExtensionVariantPtr p_self, GDExtensionConstStringNamePtr p_key, GDExtensionConstVariantPtr p_value, GDExtensionBool *r_valid);
@@ -465,7 +465,7 @@ typedef struct {
GDExtensionPtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_method, GDExtensionInt p_hash);
GDExtensionPtrConstructor (*variant_get_ptr_constructor)(GDExtensionVariantType p_type, int32_t p_constructor);
GDExtensionPtrDestructor (*variant_get_ptr_destructor)(GDExtensionVariantType p_type);
- void (*variant_construct)(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error);
+ void (*variant_construct)(GDExtensionVariantType p_type, GDExtensionVariantPtr p_base, const GDExtensionConstVariantPtr *p_args, int32_t p_argument_count, GDExtensionCallError *r_error);
GDExtensionPtrSetter (*variant_get_ptr_setter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member);
GDExtensionPtrGetter (*variant_get_ptr_getter)(GDExtensionVariantType p_type, GDExtensionConstStringNamePtr p_member);
GDExtensionPtrIndexedSetter (*variant_get_ptr_indexed_setter)(GDExtensionVariantType p_type);
@@ -539,8 +539,8 @@ typedef struct {
/* OBJECT */
- void (*object_method_bind_call)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_ret, GDExtensionCallError *r_error);
- void (*object_method_bind_ptrcall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
+ void (*object_method_bind_call)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_arg_count, GDExtensionVariantPtr r_ret, GDExtensionCallError *r_error);
+ void (*object_method_bind_ptrcall)(GDExtensionMethodBindPtr p_method_bind, GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
void (*object_destroy)(GDExtensionObjectPtr p_o);
GDExtensionObjectPtr (*global_get_singleton)(GDExtensionConstStringNamePtr p_name);
diff --git a/doc/classes/AStarGrid2D.xml b/doc/classes/AStarGrid2D.xml
index 8dde3748d7..bffa395770 100644
--- a/doc/classes/AStarGrid2D.xml
+++ b/doc/classes/AStarGrid2D.xml
@@ -103,6 +103,7 @@
<param index="1" name="solid" type="bool" default="true" />
<description>
Disables or enables the specified point for pathfinding. Useful for making an obstacle. By default, all points are enabled.
+ [b]Note:[/b] Calling [method update] is not needed after the call of this function.
</description>
</method>
<method name="update">
@@ -134,20 +135,22 @@
</members>
<constants>
<constant name="HEURISTIC_EUCLIDEAN" value="0" enum="Heuristic">
- The Euclidean heuristic to be used for the pathfinding using the following formula:
+ The [url=https://en.wikipedia.org/wiki/Euclidean_distance]Euclidean heuristic[/url] to be used for the pathfinding using the following formula:
[codeblock]
dx = abs(to_id.x - from_id.x)
dy = abs(to_id.y - from_id.y)
result = sqrt(dx * dx + dy * dy)
[/codeblock]
+ [b]Note:[/b] This is also the internal heuristic used in [AStar3D] and [AStar2D] by default (with the inclusion of possible z-axis coordinate).
</constant>
<constant name="HEURISTIC_MANHATTAN" value="1" enum="Heuristic">
- The Manhattan heuristic to be used for the pathfinding using the following formula:
+ The [url=https://en.wikipedia.org/wiki/Taxicab_geometry]Manhattan heuristic[/url] to be used for the pathfinding using the following formula:
[codeblock]
dx = abs(to_id.x - from_id.x)
dy = abs(to_id.y - from_id.y)
result = dx + dy
[/codeblock]
+ [b]Note:[/b] This heuristic is intended to be used with 4-side orthogonal movements, provided by setting the [member diagonal_mode] to [constant DIAGONAL_MODE_NEVER].
</constant>
<constant name="HEURISTIC_OCTILE" value="2" enum="Heuristic">
The Octile heuristic to be used for the pathfinding using the following formula:
@@ -159,7 +162,7 @@
[/codeblock]
</constant>
<constant name="HEURISTIC_CHEBYSHEV" value="3" enum="Heuristic">
- The Chebyshev heuristic to be used for the pathfinding using the following formula:
+ The [url=https://en.wikipedia.org/wiki/Chebyshev_distance]Chebyshev heuristic[/url] to be used for the pathfinding using the following formula:
[codeblock]
dx = abs(to_id.x - from_id.x)
dy = abs(to_id.y - from_id.y)
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 4c3c9973cb..02cfd3e873 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2754,11 +2754,20 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
log->add_message(TTR("Can't undo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR);
} else {
String action = editor_data.get_undo_redo()->get_current_action_name();
-
+ int id = editor_data.get_undo_redo()->get_current_action_history_id();
if (!editor_data.get_undo_redo()->undo()) {
log->add_message(TTR("Nothing to undo."), EditorLog::MSG_TYPE_EDITOR);
} else if (!action.is_empty()) {
- log->add_message(vformat(TTR("Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
+ switch (id) {
+ case EditorUndoRedoManager::GLOBAL_HISTORY:
+ log->add_message(vformat(TTR("Global Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
+ break;
+ case EditorUndoRedoManager::REMOTE_HISTORY:
+ log->add_message(vformat(TTR("Remote Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
+ break;
+ default:
+ log->add_message(vformat(TTR("Scene Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
+ }
}
}
} break;
@@ -2770,7 +2779,20 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
log->add_message(TTR("Nothing to redo."), EditorLog::MSG_TYPE_EDITOR);
} else {
String action = editor_data.get_undo_redo()->get_current_action_name();
- log->add_message(vformat(TTR("Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
+ if (action.is_empty()) {
+ break;
+ }
+
+ switch (editor_data.get_undo_redo()->get_current_action_history_id()) {
+ case EditorUndoRedoManager::GLOBAL_HISTORY:
+ log->add_message(vformat(TTR("Global Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
+ break;
+ case EditorUndoRedoManager::REMOTE_HISTORY:
+ log->add_message(vformat(TTR("Remote Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
+ break;
+ default:
+ log->add_message(vformat(TTR("Scene Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR);
+ }
}
}
} break;
diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp
index ab33b2d051..780ea2fd23 100644
--- a/editor/editor_undo_redo_manager.cpp
+++ b/editor/editor_undo_redo_manager.cpp
@@ -270,35 +270,9 @@ bool EditorUndoRedoManager::undo() {
return false;
}
- int selected_history = INVALID_HISTORY;
- double global_timestamp = 0;
-
- // Pick the history with greatest last action timestamp (either global or current scene).
- {
- History &history = get_or_create_history(GLOBAL_HISTORY);
- if (!history.undo_stack.is_empty()) {
- selected_history = history.id;
- global_timestamp = history.undo_stack.back()->get().timestamp;
- }
- }
-
- {
- History &history = get_or_create_history(REMOTE_HISTORY);
- if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) {
- selected_history = history.id;
- global_timestamp = history.undo_stack.back()->get().timestamp;
- }
- }
-
- {
- History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
- if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) {
- selected_history = history.id;
- }
- }
-
- if (selected_history != INVALID_HISTORY) {
- return undo_history(selected_history);
+ History *selected_history = _get_newest_undo();
+ if (selected_history) {
+ return undo_history(selected_history->id);
}
return false;
}
@@ -427,33 +401,7 @@ void EditorUndoRedoManager::clear_history(bool p_increase_version, int p_idx) {
String EditorUndoRedoManager::get_current_action_name() {
if (has_undo()) {
- History *selected_history = nullptr;
- double global_timestamp = 0;
-
- // Pick the history with greatest last action timestamp (either global or current scene).
- {
- History &history = get_or_create_history(GLOBAL_HISTORY);
- if (!history.undo_stack.is_empty()) {
- selected_history = &history;
- global_timestamp = history.undo_stack.back()->get().timestamp;
- }
- }
-
- {
- History &history = get_or_create_history(REMOTE_HISTORY);
- if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) {
- selected_history = &history;
- global_timestamp = history.undo_stack.back()->get().timestamp;
- }
- }
-
- {
- History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
- if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) {
- selected_history = &history;
- }
- }
-
+ History *selected_history = _get_newest_undo();
if (selected_history) {
return selected_history->undo_redo->get_current_action_name();
}
@@ -461,6 +409,16 @@ String EditorUndoRedoManager::get_current_action_name() {
return "";
}
+int EditorUndoRedoManager::get_current_action_history_id() {
+ if (has_undo()) {
+ History *selected_history = _get_newest_undo();
+ if (selected_history) {
+ return selected_history->id;
+ }
+ }
+ return INVALID_HISTORY;
+}
+
void EditorUndoRedoManager::discard_history(int p_idx, bool p_erase_from_map) {
ERR_FAIL_COND(!history_map.has(p_idx));
History &history = history_map[p_idx];
@@ -475,6 +433,37 @@ void EditorUndoRedoManager::discard_history(int p_idx, bool p_erase_from_map) {
}
}
+EditorUndoRedoManager::History *EditorUndoRedoManager::_get_newest_undo() {
+ History *selected_history = nullptr;
+ double global_timestamp = 0;
+
+ // Pick the history with greatest last action timestamp (either global or current scene).
+ {
+ History &history = get_or_create_history(GLOBAL_HISTORY);
+ if (!history.undo_stack.is_empty()) {
+ selected_history = &history;
+ global_timestamp = history.undo_stack.back()->get().timestamp;
+ }
+ }
+
+ {
+ History &history = get_or_create_history(REMOTE_HISTORY);
+ if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) {
+ selected_history = &history;
+ global_timestamp = history.undo_stack.back()->get().timestamp;
+ }
+ }
+
+ {
+ History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id());
+ if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) {
+ selected_history = &history;
+ }
+ }
+
+ return selected_history;
+}
+
void EditorUndoRedoManager::_bind_methods() {
ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode", "custom_context"), &EditorUndoRedoManager::create_action, DEFVAL(UndoRedo::MERGE_DISABLE), DEFVAL((Object *)nullptr));
ClassDB::bind_method(D_METHOD("commit_action", "execute"), &EditorUndoRedoManager::commit_action, DEFVAL(true));
diff --git a/editor/editor_undo_redo_manager.h b/editor/editor_undo_redo_manager.h
index 08482b6e2d..74911180f0 100644
--- a/editor/editor_undo_redo_manager.h
+++ b/editor/editor_undo_redo_manager.h
@@ -66,6 +66,8 @@ private:
bool is_committing = false;
+ History *_get_newest_undo();
+
protected:
static void _bind_methods();
@@ -127,6 +129,7 @@ public:
bool has_redo();
String get_current_action_name();
+ int get_current_action_history_id();
void discard_history(int p_idx, bool p_erase_from_map = true);
~EditorUndoRedoManager();
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index a1b2aa3089..1b169076c6 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -446,7 +446,7 @@ private:
String::utf8("\n• ") + TTR("Fast rendering of simple scenes."));
} else if (renderer_type == "gl_compatibility") {
renderer_info->set_text(
- String::utf8("• ") + TTR("Supports desktop, mobile, + web platforms.") +
+ String::utf8("• ") + TTR("Supports desktop, mobile + web platforms.") +
String::utf8("\n• ") + TTR("Least advanced 3D graphics.") +
String::utf8("\n• ") + TTR("Intended for low-end/older devices.") +
String::utf8("\n• ") + TTR("Uses OpenGL 3 backend (OpenGL 3.3/ES 3.0/WebGL2).") +
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 1149749ef7..9ec53b2b66 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -225,6 +225,8 @@ void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::Clas
}
p_list->push_back(p_node);
+ // TODO: Try to solve class inheritance if not yet resolving.
+
// Prioritize node base type over its outer class
if (p_node->base_type.class_type != nullptr) {
get_class_node_current_scope_classes(p_node->base_type.class_type, p_list);
@@ -235,15 +237,58 @@ void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::Clas
}
}
-Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) {
- if (p_class->base_type.is_set()) {
- // Already resolved
+Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) {
+ if (p_source == nullptr && parser->has_class(p_class)) {
+ p_source = p_class;
+ }
+
+ if (p_class->base_type.is_resolving()) {
+ push_error(vformat(R"(Could not resolve class "%s": Cyclic reference.)", type_from_metatype(p_class->get_datatype()).to_string()), p_source);
+ return ERR_PARSE_ERROR;
+ }
+
+ if (!p_class->base_type.has_no_type()) {
+ // Already resolved.
+ return OK;
+ }
+
+ if (!parser->has_class(p_class)) {
+ String script_path = p_class->get_datatype().script_path;
+ Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path);
+ if (parser_ref.is_null()) {
+ push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ return ERR_PARSE_ERROR;
+ }
+
+ Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
+ if (err) {
+ push_error(vformat(R"(Could not parse script "%s": %s.)", script_path, error_names[err]), p_source);
+ return ERR_PARSE_ERROR;
+ }
+
+ ERR_FAIL_COND_V_MSG(!parser_ref->get_parser()->has_class(p_class), ERR_PARSE_ERROR, R"(Parser bug: Mismatched external parser.)");
+
+ GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
+ GDScriptParser *other_parser = parser_ref->get_parser();
+
+ int error_count = other_parser->errors.size();
+ other_analyzer->resolve_class_inheritance(p_class);
+ if (other_parser->errors.size() > error_count) {
+ push_error(vformat(R"(Could not resolve inheritance for class "%s".)", p_class->fqcn), p_source);
+ return ERR_PARSE_ERROR;
+ }
+
return OK;
}
+ GDScriptParser::ClassNode *previous_class = parser->current_class;
+ parser->current_class = p_class;
+
if (p_class->identifier) {
StringName class_name = p_class->identifier->name;
- if (class_exists(class_name)) {
+ if (GDScriptParser::get_builtin_type(class_name) < Variant::VARIANT_MAX) {
+ push_error(vformat(R"(Class "%s" hides a built-in type.)", class_name), p_class->identifier);
+ } else if (class_exists(class_name)) {
push_error(vformat(R"(Class "%s" hides a native class.)", class_name), p_class->identifier);
} else if (ScriptServer::is_global_class(class_name) && (ScriptServer::get_global_class_path(class_name) != parser->script_path || p_class != parser->head)) {
push_error(vformat(R"(Class "%s" hides a global script class.)", class_name), p_class->identifier);
@@ -252,7 +297,9 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
}
}
- GDScriptParser::DataType result;
+ GDScriptParser::DataType resolving_datatype;
+ resolving_datatype.kind = GDScriptParser::DataType::RESOLVING;
+ p_class->base_type = resolving_datatype;
// Set datatype for class.
GDScriptParser::DataType class_type;
@@ -265,6 +312,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
class_type.builtin_type = Variant::OBJECT;
p_class->set_datatype(class_type);
+ GDScriptParser::DataType result;
if (!p_class->extends_used) {
result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
result.kind = GDScriptParser::DataType::NATIVE;
@@ -286,7 +334,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
return ERR_PARSE_ERROR;
}
- Error err = ext_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = ext_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", p_class->extends_path), p_class);
return err;
@@ -313,7 +361,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
return ERR_PARSE_ERROR;
}
- Error err = base_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = base_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
@@ -322,7 +370,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
}
} else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) {
const ProjectSettings::AutoloadInfo &info = ProjectSettings::get_singleton()->get_autoload(name);
- if (info.path.get_extension().to_lower() != ".gd") {
+ if (info.path.get_extension().to_lower() != GDScriptLanguage::get_singleton()->get_extension()) {
push_error(vformat(R"(Singleton %s is not a GDScript.)", info.name), p_class);
return ERR_PARSE_ERROR;
}
@@ -333,11 +381,12 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
return ERR_PARSE_ERROR;
}
- Error err = info_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = info_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
}
+ base = info_parser->get_parser()->head->get_datatype();
} else if (class_exists(name) && ClassDB::can_instantiate(name)) {
base.kind = GDScriptParser::DataType::NATIVE;
base.native_type = name;
@@ -349,7 +398,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
for (GDScriptParser::ClassNode *look_class : script_classes) {
if (look_class->identifier && look_class->identifier->name == name) {
if (!look_class->get_datatype().is_set()) {
- Error err = resolve_inheritance(look_class, false);
+ Error err = resolve_class_inheritance(look_class, p_class);
if (err) {
return err;
}
@@ -358,15 +407,9 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
found = true;
break;
}
- if (look_class->members_indices.has(name) && look_class->get_member(name).type == GDScriptParser::ClassNode::Member::CLASS) {
- GDScriptParser::ClassNode::Member member = look_class->get_member(name);
- if (!member.m_class->get_datatype().is_set()) {
- Error err = resolve_inheritance(member.m_class, false);
- if (err) {
- return err;
- }
- }
- base = member.m_class->get_datatype();
+ if (look_class->has_member(name)) {
+ resolve_class_member(look_class, name, p_class);
+ base = look_class->get_member(name).get_datatype();
found = true;
break;
}
@@ -402,7 +445,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
result = base;
}
- if (!result.is_set()) {
+ if (!result.is_set() || result.has_no_type()) {
// TODO: More specific error messages.
push_error(vformat(R"(Could not resolve inheritance for class "%s".)", p_class->identifier == nullptr ? "<main>" : p_class->identifier->name), p_class);
return ERR_PARSE_ERROR;
@@ -422,10 +465,21 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
class_type.native_type = result.native_type;
p_class->set_datatype(class_type);
+ parser->current_class = previous_class;
+
+ return OK;
+}
+
+Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) {
+ Error err = resolve_class_inheritance(p_class);
+ if (err) {
+ return err;
+ }
+
if (p_recursive) {
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) {
- Error err = resolve_inheritance(p_class->members[i].m_class, true);
+ err = resolve_class_inheritance(p_class->members[i].m_class, true);
if (err) {
return err;
}
@@ -437,14 +491,29 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
}
GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::TypeNode *p_type) {
- GDScriptParser::DataType result;
+ GDScriptParser::DataType bad_type;
+ bad_type.kind = GDScriptParser::DataType::VARIANT;
+ bad_type.type_source = GDScriptParser::DataType::INFERRED;
if (p_type == nullptr) {
- result.kind = GDScriptParser::DataType::VARIANT;
- return result;
+ return bad_type;
+ }
+
+ if (p_type->get_datatype().is_resolving()) {
+ push_error(R"(Could not resolve datatype: Cyclic reference.)", p_type);
+ return bad_type;
+ }
+
+ if (!p_type->get_datatype().has_no_type()) {
+ return p_type->get_datatype();
}
- result.type_source = result.ANNOTATED_EXPLICIT;
+ GDScriptParser::DataType resolving_datatype;
+ resolving_datatype.kind = GDScriptParser::DataType::RESOLVING;
+ p_type->set_datatype(resolving_datatype);
+
+ GDScriptParser::DataType result;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.builtin_type = Variant::OBJECT;
if (p_type->type_chain.is_empty()) {
@@ -458,29 +527,21 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
StringName first = p_type->type_chain[0]->name;
if (first == SNAME("Variant")) {
- result.kind = GDScriptParser::DataType::VARIANT;
if (p_type->type_chain.size() > 1) {
- push_error(R"("Variant" type don't contain nested types.)", p_type->type_chain[1]);
- return GDScriptParser::DataType();
+ // TODO: Variant does actually have a nested Type though.
+ push_error(R"(Variant doesn't contain nested types.)", p_type->type_chain[1]);
+ return bad_type;
}
- return result;
- }
-
- if (first == SNAME("Object")) {
+ result.kind = GDScriptParser::DataType::VARIANT;
+ } else if (first == SNAME("Object")) {
+ // Object is treated like a native type, not a built-in.
result.kind = GDScriptParser::DataType::NATIVE;
result.native_type = SNAME("Object");
- if (p_type->type_chain.size() > 1) {
- push_error(R"("Object" type don't contain nested types.)", p_type->type_chain[1]);
- return GDScriptParser::DataType();
- }
- return result;
- }
-
- if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
+ } else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
// Built-in types.
if (p_type->type_chain.size() > 1) {
push_error(R"(Built-in types don't contain nested types.)", p_type->type_chain[1]);
- return GDScriptParser::DataType();
+ return bad_type;
}
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = GDScriptParser::get_builtin_type(first);
@@ -506,9 +567,9 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
String ext = path.get_extension();
if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
Ref<GDScriptParserRef> ref = get_parser_for(path);
- if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
- return GDScriptParser::DataType();
+ return bad_type;
}
result = ref->get_parser()->head->get_datatype();
} else {
@@ -523,9 +584,9 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
} else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) {
const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first);
Ref<GDScriptParserRef> ref = get_parser_for(autoload.path);
- if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type);
- return GDScriptParser::DataType();
+ return bad_type;
}
result = ref->get_parser()->head->get_datatype();
} else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) {
@@ -541,26 +602,28 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
break;
}
if (script_class->members_indices.has(first)) {
- GDScriptParser::ClassNode::Member member = script_class->members[script_class->members_indices[first]];
+ resolve_class_member(script_class, first, p_type);
+
+ GDScriptParser::ClassNode::Member member = script_class->get_member(first);
switch (member.type) {
case GDScriptParser::ClassNode::Member::CLASS:
- result = member.m_class->get_datatype();
+ result = member.get_datatype();
break;
case GDScriptParser::ClassNode::Member::ENUM:
- result = member.m_enum->get_datatype();
+ result = member.get_datatype();
break;
case GDScriptParser::ClassNode::Member::CONSTANT:
- if (member.constant->get_datatype().is_meta_type) {
- result = member.constant->get_datatype();
+ if (member.get_datatype().is_meta_type) {
+ result = member.get_datatype();
result.is_meta_type = false;
break;
} else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) {
Ref<GDScript> gdscript = member.constant->initializer->reduced_value;
if (gdscript.is_valid()) {
Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path());
- if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type);
- return GDScriptParser::DataType();
+ return bad_type;
}
result = ref->get_parser()->head->get_datatype();
result.is_meta_type = false;
@@ -578,15 +641,14 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
[[fallthrough]];
default:
push_error(vformat(R"("%s" is a %s but does not contain a type.)", first, member.get_type_name()), p_type);
- return GDScriptParser::DataType();
+ return bad_type;
}
}
}
}
if (!result.is_set()) {
push_error(vformat(R"("%s" was not found in the current scope.)", first), p_type);
- result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
- return result;
+ return bad_type;
}
if (p_type->type_chain.size() > 1) {
@@ -597,27 +659,26 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result = p_type->type_chain[i]->get_datatype();
if (!result.is_set()) {
push_error(vformat(R"(Could not find type "%s" under base "%s".)", p_type->type_chain[i]->name, base.to_string()), p_type->type_chain[1]);
- result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
- return result;
+ return bad_type;
} else if (!result.is_meta_type) {
push_error(vformat(R"(Member "%s" under base "%s" is not a valid type.)", p_type->type_chain[i]->name, base.to_string()), p_type->type_chain[1]);
- result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
- return result;
+ return bad_type;
}
}
} else if (result.kind == GDScriptParser::DataType::NATIVE) {
// Only enums allowed for native.
- if (ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) {
- if (p_type->type_chain.size() > 2) {
- push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]);
- } else {
- result = make_native_enum_type(result.native_type, p_type->type_chain[1]->name);
- }
+ if (!ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) {
+ push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]);
+ return bad_type;
+ }
+ if (p_type->type_chain.size() > 2) {
+ push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]);
+ return bad_type;
}
+ result = make_native_enum_type(result.native_type, p_type->type_chain[1]->name);
} else {
push_error(vformat(R"(Could not find nested type "%s" under base "%s".)", p_type->type_chain[1]->name, result.to_string()), p_type->type_chain[1]);
- result.kind = GDScriptParser::DataType::VARIANT; // Leave Variant anyway so future type check don't use an unresolved type.
- return result;
+ return bad_type;
}
}
@@ -629,22 +690,77 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return result;
}
-void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class) {
- if (p_class->resolved_interface) {
+void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source) {
+ ERR_FAIL_COND(!p_class->has_member(p_name));
+ resolve_class_member(p_class, p_class->members_indices[p_name], p_source);
+}
+
+void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source) {
+ ERR_FAIL_INDEX(p_index, p_class->members.size());
+
+ GDScriptParser::ClassNode::Member &member = p_class->members.write[p_index];
+ if (p_source == nullptr && parser->has_class(p_class)) {
+ p_source = member.get_source_node();
+ }
+
+ if (member.get_datatype().is_resolving()) {
+ push_error(vformat(R"(Could not resolve member "%s": Cyclic reference.)", member.get_name()), p_source);
+ return;
+ }
+
+ if (member.get_datatype().is_set()) {
+ return;
+ }
+
+ if (!parser->has_class(p_class)) {
+ String script_path = p_class->get_datatype().script_path;
+ Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path);
+ if (parser_ref.is_null()) {
+ push_error(vformat(R"(Could not find script "%s" (While resolving "%s").)", script_path, member.get_name()), p_source);
+ return;
+ }
+
+ Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
+ if (err) {
+ push_error(vformat(R"(Could not resolve script "%s": %s (While resolving "%s").)", script_path, error_names[err], member.get_name()), p_source);
+ return;
+ }
+
+ ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");
+
+ GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
+ GDScriptParser *other_parser = parser_ref->get_parser();
+
+ int error_count = other_parser->errors.size();
+ other_analyzer->resolve_class_member(p_class, p_index);
+ if (other_parser->errors.size() > error_count) {
+ push_error(vformat(R"(Could not resolve member "%s".)", member.get_name()), p_source);
+ }
+
return;
}
- p_class->resolved_interface = true;
+
+ // If it's already resolving, that's ok.
+ if (!p_class->base_type.is_resolving()) {
+ Error err = resolve_class_inheritance(p_class);
+ if (err) {
+ return;
+ }
+ }
GDScriptParser::ClassNode *previous_class = parser->current_class;
parser->current_class = p_class;
- for (int i = 0; i < p_class->members.size(); i++) {
- GDScriptParser::ClassNode::Member member = p_class->members[i];
+ GDScriptParser::DataType resolving_datatype;
+ resolving_datatype.kind = GDScriptParser::DataType::RESOLVING;
+ {
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;
@@ -656,7 +772,6 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
if (member.variable->initializer != nullptr) {
- member.variable->set_datatype(datatype); // Allow recursive usage.
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.
@@ -667,18 +782,14 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
}
datatype = member.variable->initializer->get_datatype();
+
if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) {
datatype.type_source = GDScriptParser::DataType::INFERRED;
}
- }
- // Check if initializer is an unset identifier (ie: a variable within scope, but declared below)
- if (member.variable->initializer && !member.variable->initializer->get_datatype().is_set()) {
- if (member.variable->initializer->type == GDScriptParser::Node::IDENTIFIER) {
- GDScriptParser::IdentifierNode *initializer_identifier = static_cast<GDScriptParser::IdentifierNode *>(member.variable->initializer);
- push_error(vformat(R"(Identifier "%s" must be declared above current variable.)", initializer_identifier->name), member.variable->initializer);
- } else {
- ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier.");
+ 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;
}
}
@@ -730,10 +841,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
case GDScriptParser::ClassNode::Member::CONSTANT: {
check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant);
- reduce_expression(member.constant->initializer);
+ 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;
@@ -741,7 +851,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
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);
@@ -754,6 +866,11 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
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);
}
@@ -782,6 +899,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
case GDScriptParser::ClassNode::Member::SIGNAL: {
check_class_member_name_conflict(p_class, member.signal->identifier->name, member.signal);
+ member.signal->set_datatype(resolving_datatype);
+
for (int j = 0; j < member.signal->parameters.size(); j++) {
GDScriptParser::DataType signal_type = resolve_datatype(member.signal->parameters[j]->datatype_specifier);
signal_type.is_meta_type = false;
@@ -803,6 +922,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
case GDScriptParser::ClassNode::Member::ENUM: {
check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum);
+ member.m_enum->set_datatype(resolving_datatype);
+
GDScriptParser::DataType enum_type;
enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
enum_type.kind = GDScriptParser::DataType::ENUM;
@@ -812,7 +933,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
enum_type.is_meta_type = true;
enum_type.is_constant = true;
- // Enums can't be nested, so we can safely override this.
+ const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.m_enum;
for (int j = 0; j < member.m_enum->values.size(); j++) {
@@ -840,7 +961,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
enum_type.enum_values[element.identifier->name] = element.value;
}
- current_enum = nullptr;
+ current_enum = prev_enum;
member.m_enum->set_datatype(enum_type);
@@ -850,15 +971,18 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
} break;
case GDScriptParser::ClassNode::Member::FUNCTION:
- resolve_function_signature(member.function);
+ resolve_function_signature(member.function, p_source);
break;
case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
if (member.enum_value.custom_value) {
check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.custom_value);
+ member.enum_value.identifier->set_datatype(resolving_datatype);
+
+ const GDScriptParser::EnumNode *prev_enum = current_enum;
current_enum = member.enum_value.parent_enum;
reduce_expression(member.enum_value.custom_value);
- current_enum = nullptr;
+ current_enum = prev_enum;
if (!member.enum_value.custom_value->is_constant) {
push_error(R"(Enum values must be constant.)", member.enum_value.custom_value);
@@ -878,12 +1002,22 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
member.enum_value.resolved = true;
}
+
// Also update the original references.
- member.enum_value.parent_enum->values.write[member.enum_value.index] = member.enum_value;
- p_class->members.write[i].enum_value = member.enum_value;
+ member.enum_value.parent_enum->values.set(member.enum_value.index, member.enum_value);
+
+ GDScriptParser::DataType datatype;
+ datatype.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ datatype.kind = GDScriptParser::DataType::BUILTIN;
+ datatype.builtin_type = Variant::INT;
+ member.enum_value.identifier->set_datatype(datatype);
} break;
case GDScriptParser::ClassNode::Member::CLASS:
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
+ // If it's already resolving, that's ok.
+ if (!member.m_class->base_type.is_resolving()) {
+ resolve_class_inheritance(member.m_class, p_source);
+ }
break;
case GDScriptParser::ClassNode::Member::GROUP:
// No-op, but needed to silence warnings.
@@ -894,28 +1028,123 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
}
- // Recurse nested classes.
- for (int i = 0; i < p_class->members.size(); i++) {
- GDScriptParser::ClassNode::Member member = p_class->members[i];
- if (member.type != GDScriptParser::ClassNode::Member::CLASS) {
- continue;
+ parser->current_class = previous_class;
+}
+
+void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) {
+ if (p_source == nullptr && parser->has_class(p_class)) {
+ p_source = p_class;
+ }
+
+ if (!p_class->resolved_interface) {
+ if (!parser->has_class(p_class)) {
+ String script_path = p_class->get_datatype().script_path;
+ Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path);
+ if (parser_ref.is_null()) {
+ push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ return;
+ }
+
+ Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
+ if (err) {
+ push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source);
+ return;
+ }
+
+ ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");
+
+ GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
+ GDScriptParser *other_parser = parser_ref->get_parser();
+
+ int error_count = other_parser->errors.size();
+ other_analyzer->resolve_class_interface(p_class);
+ if (other_parser->errors.size() > error_count) {
+ push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source);
+ }
+
+ return;
+ }
+ p_class->resolved_interface = true;
+
+ if (resolve_class_inheritance(p_class) != OK) {
+ return;
+ }
+
+ GDScriptParser::DataType base_type = p_class->base_type;
+ if (base_type.kind == GDScriptParser::DataType::CLASS) {
+ GDScriptParser::ClassNode *base_class = base_type.class_type;
+ resolve_class_interface(base_class, p_class);
}
- resolve_class_interface(member.m_class);
+ for (int i = 0; i < p_class->members.size(); i++) {
+ resolve_class_member(p_class, i);
+ }
}
+}
- parser->current_class = previous_class;
+void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive) {
+ resolve_class_interface(p_class);
+
+ if (p_recursive) {
+ for (int i = 0; i < p_class->members.size(); i++) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
+ resolve_class_interface(member.m_class, true);
+ }
+ }
+ }
}
-void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
+void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source) {
+ if (p_source == nullptr && parser->has_class(p_class)) {
+ p_source = p_class;
+ }
+
if (p_class->resolved_body) {
return;
}
+
+ if (!parser->has_class(p_class)) {
+ String script_path = p_class->get_datatype().script_path;
+ Ref<GDScriptParserRef> parser_ref = get_parser_for(script_path);
+ if (parser_ref.is_null()) {
+ push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
+ return;
+ }
+
+ Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
+ if (err) {
+ push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source);
+ return;
+ }
+
+ ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");
+
+ GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
+ GDScriptParser *other_parser = parser_ref->get_parser();
+
+ int error_count = other_parser->errors.size();
+ other_analyzer->resolve_class_body(p_class);
+ if (other_parser->errors.size() > error_count) {
+ push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source);
+ }
+
+ return;
+ }
+
p_class->resolved_body = true;
GDScriptParser::ClassNode *previous_class = parser->current_class;
parser->current_class = p_class;
+ resolve_class_interface(p_class, p_source);
+
+ GDScriptParser::DataType base_type = p_class->base_type;
+ if (base_type.kind == GDScriptParser::DataType::CLASS) {
+ GDScriptParser::ClassNode *base_class = base_type.class_type;
+ resolve_class_body(base_class, p_class);
+ }
+
// Do functions and properties now.
for (int i = 0; i < p_class->members.size(); i++) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
@@ -958,18 +1187,6 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
}
}
- parser->current_class = previous_class;
-
- // Recurse nested classes.
- for (int i = 0; i < p_class->members.size(); i++) {
- GDScriptParser::ClassNode::Member member = p_class->members[i];
- if (member.type != GDScriptParser::ClassNode::Member::CLASS) {
- continue;
- }
-
- resolve_class_body(member.m_class);
- }
-
// Check unused variables and datatypes of property getters and setters.
for (int i = 0; i < p_class->members.size(); i++) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
@@ -1057,6 +1274,21 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
}
}
}
+
+ parser->current_class = previous_class;
+}
+
+void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, bool p_recursive) {
+ resolve_class_body(p_class);
+
+ if (p_recursive) {
+ for (int i = 0; i < p_class->members.size(); i++) {
+ GDScriptParser::ClassNode::Member member = p_class->members[i];
+ if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
+ resolve_class_body(member.m_class, true);
+ }
+ }
+ }
}
void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root) {
@@ -1066,8 +1298,10 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root
case GDScriptParser::Node::NONE:
break; // Unreachable.
case GDScriptParser::Node::CLASS:
- resolve_class_interface(static_cast<GDScriptParser::ClassNode *>(p_node));
- resolve_class_body(static_cast<GDScriptParser::ClassNode *>(p_node));
+ if (OK == resolve_class_inheritance(static_cast<GDScriptParser::ClassNode *>(p_node), true)) {
+ resolve_class_interface(static_cast<GDScriptParser::ClassNode *>(p_node), true);
+ resolve_class_body(static_cast<GDScriptParser::ClassNode *>(p_node), true);
+ }
break;
case GDScriptParser::Node::CONSTANT:
resolve_constant(static_cast<GDScriptParser::ConstantNode *>(p_node));
@@ -1149,7 +1383,16 @@ void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_anno
// TODO: Add second validation function for annotations, so they can use checked types.
}
-void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function) {
+void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source) {
+ if (p_source == nullptr) {
+ p_source = p_function;
+ }
+
+ if (p_function->get_datatype().is_resolving()) {
+ push_error(vformat(R"(Could not resolve function "%s": Cyclic reference.)", p_function->identifier->name), p_source);
+ return;
+ }
+
if (p_function->resolved_signature) {
return;
}
@@ -1158,6 +1401,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
GDScriptParser::FunctionNode *previous_function = parser->current_function;
parser->current_function = p_function;
+ GDScriptParser::DataType prev_datatype = p_function->get_datatype();
+
+ GDScriptParser::DataType resolving_datatype;
+ resolving_datatype.kind = GDScriptParser::DataType::RESOLVING;
+ p_function->set_datatype(resolving_datatype);
+
#ifdef TOOLS_ENABLED
int default_value_count = 0;
#endif // TOOLS_ENABLED
@@ -1262,6 +1511,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
#endif // TOOLS_ENABLED
}
+ if (p_function->get_datatype().is_resolving()) {
+ p_function->set_datatype(prev_datatype);
+ }
+
parser->current_function = previous_function;
}
@@ -2745,7 +2998,7 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
return type;
}
- Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err) {
push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
type.type_source = GDScriptParser::DataType::UNDETECTED;
@@ -2768,6 +3021,10 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
}
void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base) {
+ if (!p_identifier->get_datatype().has_no_type()) {
+ return;
+ }
+
GDScriptParser::DataType base;
if (p_base == nullptr) {
base = type_from_metatype(parser->current_class->get_datatype());
@@ -2860,16 +3117,16 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
p_identifier->set_datatype(base_class->get_datatype());
return;
}
+
if (base_class->has_member(name)) {
- const GDScriptParser::ClassNode::Member &member = base_class->get_member(name);
+ resolve_class_member(base_class, name, p_identifier);
+
+ GDScriptParser::ClassNode::Member member = base_class->get_member(name);
p_identifier->set_datatype(member.get_datatype());
switch (member.type) {
case GDScriptParser::ClassNode::Member::CONSTANT:
- // For out-of-order resolution:
- reduce_expression(member.constant->initializer);
p_identifier->is_constant = true;
p_identifier->reduced_value = member.constant->initializer->reduced_value;
- p_identifier->set_datatype(member.constant->initializer->get_datatype());
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
p_identifier->constant_source = member.constant;
break;
@@ -2887,14 +3144,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_SIGNAL;
break;
case GDScriptParser::ClassNode::Member::FUNCTION:
- resolve_function_signature(member.function);
p_identifier->set_datatype(make_callable_type(member.function->info));
break;
- case GDScriptParser::ClassNode::Member::CLASS:
- // For out-of-order resolution:
- resolve_class_interface(member.m_class);
- p_identifier->set_datatype(member.m_class->get_datatype());
- break;
default:
break; // Type already set.
}
@@ -2907,33 +3158,28 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
get_class_node_current_scope_classes(parser->current_class, &script_classes);
for (GDScriptParser::ClassNode *script_class : script_classes) {
if (script_class->has_member(name)) {
- const GDScriptParser::ClassNode::Member &member = script_class->get_member(name);
+ resolve_class_member(script_class, name, p_identifier);
+
+ GDScriptParser::ClassNode::Member member = script_class->get_member(name);
switch (member.type) {
- case GDScriptParser::ClassNode::Member::CONSTANT: {
+ case GDScriptParser::ClassNode::Member::CONSTANT:
// TODO: Make sure loops won't cause problem. And make special error message for those.
- // For out-of-order resolution:
- reduce_expression(member.constant->initializer);
p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.constant->initializer->reduced_value;
return;
- } break;
- case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
+ case GDScriptParser::ClassNode::Member::ENUM_VALUE:
p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = true;
p_identifier->reduced_value = member.enum_value.value;
return;
- } break;
- case GDScriptParser::ClassNode::Member::ENUM: {
+ case GDScriptParser::ClassNode::Member::ENUM:
p_identifier->set_datatype(member.get_datatype());
p_identifier->is_constant = false;
return;
- } break;
- case GDScriptParser::ClassNode::Member::CLASS: {
- resolve_class_interface(member.m_class);
- p_identifier->set_datatype(member.m_class->get_datatype());
+ case GDScriptParser::ClassNode::Member::CLASS:
+ p_identifier->set_datatype(member.get_datatype());
return;
- } break;
default:
break;
}
@@ -3139,7 +3385,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (ResourceLoader::get_resource_type(autoload.path) == "GDScript") {
Ref<GDScriptParserRef> singl_parser = get_parser_for(autoload.path);
if (singl_parser.is_valid()) {
- Error err = singl_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err == OK) {
result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
}
@@ -3153,7 +3399,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (scr.is_valid()) {
Ref<GDScriptParserRef> singl_parser = get_parser_for(scr->get_script_path());
if (singl_parser.is_valid()) {
- Error err = singl_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = singl_parser->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
if (err == OK) {
result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
}
@@ -3347,53 +3593,42 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
}
GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
- // If base is a class metatype, use the analyzer instead.
- if (p_subscript->base->is_constant && !(base_type.is_meta_type && base_type.kind == GDScriptParser::DataType::CLASS)) {
+ bool valid = false;
+ // If the base is a metatype, use the analyzer instead.
+ if (p_subscript->base->is_constant && !base_type.is_meta_type) {
// Just try to get it.
- bool valid = false;
Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
-
- // If it's a GDScript instance, try to get the full script. Maybe it's not still completely loaded.
- Ref<GDScript> gdscr = Ref<GDScript>(p_subscript->base->reduced_value);
- if (!valid && gdscr.is_valid()) {
- Error err = OK;
- GDScriptCache::get_full_script(gdscr->get_script_path(), err);
- if (err == OK) {
- value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
- }
- }
-
- if (!valid) {
- push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index);
- result_type.kind = GDScriptParser::DataType::VARIANT;
- } else {
+ if (valid) {
p_subscript->is_constant = true;
p_subscript->reduced_value = value;
result_type = type_from_variant(value, p_subscript);
}
+ } else if (base_type.is_variant() || !base_type.is_hard_type()) {
+ valid = true;
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ mark_node_unsafe(p_subscript);
} else {
- if (base_type.is_variant() || !base_type.is_hard_type()) {
- result_type.kind = GDScriptParser::DataType::VARIANT;
- mark_node_unsafe(p_subscript);
- } else {
- reduce_identifier_from_base(p_subscript->attribute, &base_type);
- GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
- if (attr_type.is_set()) {
- result_type = attr_type;
- p_subscript->is_constant = p_subscript->attribute->is_constant;
- p_subscript->reduced_value = p_subscript->attribute->reduced_value;
- } else {
- if (base_type.kind == GDScriptParser::DataType::BUILTIN) {
- push_error(vformat(R"(Cannot find member "%s" in base "%s".)", p_subscript->attribute->name, base_type.to_string()), p_subscript->attribute);
+ reduce_identifier_from_base(p_subscript->attribute, &base_type);
+ GDScriptParser::DataType attr_type = p_subscript->attribute->get_datatype();
+ if (attr_type.is_set()) {
+ valid = true;
+ result_type = attr_type;
+ p_subscript->is_constant = p_subscript->attribute->is_constant;
+ p_subscript->reduced_value = p_subscript->attribute->reduced_value;
+ } else if (!base_type.is_constant) {
+ valid = base_type.kind != GDScriptParser::DataType::BUILTIN;
#ifdef DEBUG_ENABLED
- } else {
- parser->push_warning(p_subscript, GDScriptWarning::UNSAFE_PROPERTY_ACCESS, p_subscript->attribute->name, base_type.to_string());
-#endif
- }
- result_type.kind = GDScriptParser::DataType::VARIANT;
+ if (valid) {
+ parser->push_warning(p_subscript, GDScriptWarning::UNSAFE_PROPERTY_ACCESS, p_subscript->attribute->name, base_type.to_string());
}
+#endif
+ result_type.kind = GDScriptParser::DataType::VARIANT;
}
}
+ if (!valid) {
+ push_error(vformat(R"(Cannot find member "%s" in base "%s".)", p_subscript->attribute->name, type_from_metatype(base_type).to_string()), p_subscript->attribute);
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ }
} else {
if (p_subscript->index == nullptr) {
return;
@@ -3752,7 +3987,6 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
scr = obj->get_script();
}
if (scr.is_valid()) {
- result.script_type = scr;
result.script_path = scr->get_path();
Ref<GDScript> gds = scr;
if (gds.is_valid()) {
@@ -3774,21 +4008,30 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
error_type.kind = GDScriptParser::DataType::VARIANT;
return error_type;
}
- ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
GDScriptParser::ClassNode *found = ref->get_parser()->head;
- // It should be okay to assume this exists, since we have a complete script already.
for (const StringName &E : class_chain) {
+ if (!found->has_member(E)) {
+ return GDScriptParser::DataType();
+ }
+
+ if (found->get_member(E).type != GDScriptParser::ClassNode::Member::CLASS) {
+ return GDScriptParser::DataType();
+ }
+
+ resolve_class_member(found, E, p_source);
+
found = found->get_member(E).m_class;
}
- result.class_type = found;
- result.script_path = ref->get_parser()->script_path;
+ result = found->get_datatype();
} else {
result.kind = GDScriptParser::DataType::SCRIPT;
+ result.native_type = scr->get_instance_base_type();
}
- result.native_type = scr->get_instance_base_type();
+ result.script_type = scr;
} else {
result.kind = GDScriptParser::DataType::NATIVE;
if (result.native_type == GDScriptNativeClass::get_class_static()) {
@@ -3912,8 +4155,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
push_error(vformat(R"(Member "%s" is not a function.)", function_name), p_source);
return false;
}
+
+ resolve_class_member(base_class, function_name, p_source);
found_function = base_class->get_member(function_name).function;
}
+
+ resolve_class_inheritance(base_class, p_source);
base_class = base_class->base_type.class_type;
}
@@ -4082,12 +4329,10 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
base_class = base_class->base_type.class_type;
}
- StringName base_native = base.native_type;
-
- ERR_FAIL_COND_V_MSG(!class_exists(base_native), false, "Non-existent native base class.");
-
- StringName parent = base_native;
+ StringName parent = base.native_type;
while (parent != StringName()) {
+ ERR_FAIL_COND_V_MSG(!class_exists(parent), false, "Non-existent native base class.");
+
if (ClassDB::has_method(parent, name, true)) {
parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "method", parent);
return true;
@@ -4277,6 +4522,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
+ case GDScriptParser::DataType::RESOLVING:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
@@ -4313,6 +4559,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
case GDScriptParser::DataType::VARIANT:
case GDScriptParser::DataType::BUILTIN:
case GDScriptParser::DataType::ENUM:
+ case GDScriptParser::DataType::RESOLVING:
case GDScriptParser::DataType::UNRESOLVED:
break; // Already solved before.
}
@@ -4327,6 +4574,10 @@ void GDScriptAnalyzer::push_error(const String &p_message, const GDScriptParser:
void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
#ifdef DEBUG_ENABLED
+ if (p_node == nullptr) {
+ return;
+ }
+
for (int i = p_node->start_line; i <= p_node->end_line; i++) {
parser->unsafe_lines.insert(i);
}
@@ -4359,39 +4610,46 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
}
Error GDScriptAnalyzer::resolve_inheritance() {
- return resolve_inheritance(parser->head);
+ return resolve_class_inheritance(parser->head, true);
}
Error GDScriptAnalyzer::resolve_interface() {
- resolve_class_interface(parser->head);
+ resolve_class_interface(parser->head, true);
return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::resolve_body() {
- resolve_class_body(parser->head);
+ resolve_class_body(parser->head, true);
return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
-Error GDScriptAnalyzer::resolve_program() {
- resolve_class_interface(parser->head);
- resolve_class_body(parser->head);
-
+Error GDScriptAnalyzer::resolve_dependencies() {
for (KeyValue<String, Ref<GDScriptParserRef>> &K : depended_parsers) {
if (K.value.is_null()) {
return ERR_PARSE_ERROR;
}
- K.value->raise_status(GDScriptParserRef::FULLY_SOLVED);
+ K.value->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
}
+
return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::analyze() {
parser->errors.clear();
- Error err = resolve_inheritance(parser->head);
+ Error err = OK;
+
+ err = resolve_inheritance();
if (err) {
return err;
}
- return resolve_program();
+
+ resolve_interface();
+ resolve_body();
+ if (!parser->errors.is_empty()) {
+ return ERR_PARSE_ERROR;
+ }
+
+ return resolve_dependencies();
}
GDScriptAnalyzer::GDScriptAnalyzer(GDScriptParser *p_parser) {
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 44ca1593ed..a4d9efb094 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -52,18 +52,20 @@ class GDScriptAnalyzer {
void get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list);
- Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
+ Error resolve_class_inheritance(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
+ Error resolve_class_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive);
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
void decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement);
- // This traverses the tree to resolve all TypeNodes.
- Error resolve_program();
-
void resolve_annotation(GDScriptParser::AnnotationNode *p_annotation);
- void resolve_class_interface(GDScriptParser::ClassNode *p_class);
- void resolve_class_body(GDScriptParser::ClassNode *p_class);
- void resolve_function_signature(GDScriptParser::FunctionNode *p_function);
+ void resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source = nullptr);
+ void resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source = nullptr);
+ void resolve_class_interface(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
+ void resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive);
+ void resolve_class_body(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
+ void resolve_class_body(GDScriptParser::ClassNode *p_class, bool p_recursive);
+ void resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source = nullptr);
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);
@@ -115,7 +117,7 @@ class GDScriptAnalyzer {
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
- void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
+ void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
void mark_lambda_use_self();
bool class_exists(const StringName &p_class) const;
@@ -128,6 +130,7 @@ public:
Error resolve_inheritance();
Error resolve_interface();
Error resolve_body();
+ Error resolve_dependencies();
Error analyze();
GDScriptAnalyzer(GDScriptParser *p_parser);
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index d1467eea95..6faf2dde73 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -50,6 +50,13 @@ GDScriptParser *GDScriptParserRef::get_parser() const {
return parser;
}
+GDScriptAnalyzer *GDScriptParserRef::get_analyzer() {
+ if (analyzer == nullptr) {
+ analyzer = memnew(GDScriptAnalyzer(parser));
+ }
+ return analyzer;
+}
+
Error GDScriptParserRef::raise_status(Status p_new_status) {
ERR_FAIL_COND_V(parser == nullptr, ERR_INVALID_DATA);
@@ -64,23 +71,22 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
result = parser->parse(GDScriptCache::get_source_code(path), path, false);
break;
case PARSED: {
- analyzer = memnew(GDScriptAnalyzer(parser));
status = INHERITANCE_SOLVED;
- Error inheritance_result = analyzer->resolve_inheritance();
+ Error inheritance_result = get_analyzer()->resolve_inheritance();
if (result == OK) {
result = inheritance_result;
}
} break;
case INHERITANCE_SOLVED: {
status = INTERFACE_SOLVED;
- Error interface_result = analyzer->resolve_interface();
+ Error interface_result = get_analyzer()->resolve_interface();
if (result == OK) {
result = interface_result;
}
} break;
case INTERFACE_SOLVED: {
status = FULLY_SOLVED;
- Error body_result = analyzer->resolve_body();
+ Error body_result = get_analyzer()->resolve_body();
if (result == OK) {
result = body_result;
}
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 0ee269f96c..43a45bfef6 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -65,6 +65,7 @@ public:
bool is_valid() const;
Status get_status() const;
GDScriptParser *get_parser() const;
+ GDScriptAnalyzer *get_analyzer();
Error raise_status(Status p_new_status);
void clear();
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 2a98b856ce..4740b9b5a9 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -157,6 +157,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.builtin_type = Variant::INT;
}
break;
+ case GDScriptParser::DataType::RESOLVING:
case GDScriptParser::DataType::UNRESOLVED: {
ERR_PRINT("Parser bug: converting unresolved type.");
return GDScriptDataType();
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index f2aafe9f0c..103269f0f5 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -642,6 +642,53 @@ void GDScriptParser::parse_program() {
clear_unused_annotations();
}
+GDScriptParser::ClassNode *GDScriptParser::find_class(const String &p_qualified_name) const {
+ String first = p_qualified_name.get_slice("::", 0);
+
+ Vector<String> class_names;
+ GDScriptParser::ClassNode *result = nullptr;
+ // Empty initial name means start at the head.
+ if (first.is_empty() || (head->identifier && first == head->identifier->name)) {
+ class_names = p_qualified_name.split("::");
+ result = head;
+ } else if (p_qualified_name.begins_with(script_path)) {
+ // Script path could have a class path separator("::") in it.
+ class_names = p_qualified_name.trim_prefix(script_path).split("::");
+ result = head;
+ } else if (head->has_member(first)) {
+ class_names = p_qualified_name.split("::");
+ GDScriptParser::ClassNode::Member member = head->get_member(first);
+ if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
+ result = member.m_class;
+ }
+ }
+
+ // Starts at index 1 because index 0 was handled above.
+ for (int i = 1; result != nullptr && i < class_names.size(); i++) {
+ String current_name = class_names[i];
+ GDScriptParser::ClassNode *next = nullptr;
+ if (result->has_member(current_name)) {
+ GDScriptParser::ClassNode::Member member = result->get_member(current_name);
+ if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
+ next = member.m_class;
+ }
+ }
+ result = next;
+ }
+
+ return result;
+}
+
+bool GDScriptParser::has_class(const GDScriptParser::ClassNode *p_class) const {
+ if (head->fqcn.is_empty() && p_class->fqcn.get_slice("::", 0).is_empty()) {
+ return p_class == head;
+ } else if (p_class->fqcn.begins_with(head->fqcn)) {
+ return find_class(p_class->fqcn.trim_prefix(head->fqcn)) == p_class;
+ }
+
+ return false;
+}
+
GDScriptParser::ClassNode *GDScriptParser::parse_class() {
ClassNode *n_class = alloc_node<ClassNode>();
@@ -2240,7 +2287,7 @@ GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() {
GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign) {
if (!previous.is_identifier()) {
- ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token.");
+ ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing identifier node without identifier token.");
}
IdentifierNode *identifier = alloc_node<IdentifierNode>();
complete_extents(identifier);
@@ -4042,11 +4089,12 @@ String GDScriptParser::DataType::to_string() const {
}
case ENUM:
return enum_type.operator String() + " (enum)";
+ case RESOLVING:
case UNRESOLVED:
return "<unresolved type>";
}
- ERR_FAIL_V_MSG("<unresolved type", "Kind set outside the enum range.");
+ ERR_FAIL_V_MSG("<unresolved type>", "Kind set outside the enum range.");
}
static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_type) {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index d092a2a5e9..540ef1c561 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -107,6 +107,7 @@ public:
CLASS, // GDScript.
ENUM, // Enumeration.
VARIANT, // Can be any type.
+ RESOLVING, // Currently resolving.
UNRESOLVED,
};
Kind kind = UNRESOLVED;
@@ -133,9 +134,10 @@ public:
MethodInfo method_info; // For callable/signals.
HashMap<StringName, int64_t> enum_values; // For enums.
- _FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
+ _FORCE_INLINE_ bool is_set() const { return kind != RESOLVING && kind != UNRESOLVED; }
+ _FORCE_INLINE_ bool is_resolving() const { return kind == RESOLVING; }
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
- _FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == UNRESOLVED; }
+ _FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == RESOLVING || kind == UNRESOLVED; }
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
String to_string() const;
@@ -188,6 +190,7 @@ public:
return script_type == p_other.script_type;
case CLASS:
return class_type == p_other.class_type;
+ case RESOLVING:
case UNRESOLVED:
break;
}
@@ -516,6 +519,32 @@ public:
};
EnumNode::Value enum_value;
+ String get_name() const {
+ switch (type) {
+ case UNDEFINED:
+ return "<undefined member>";
+ case CLASS:
+ // All class-type members have an id.
+ return m_class->identifier->name;
+ case CONSTANT:
+ return constant->identifier->name;
+ case FUNCTION:
+ return function->identifier->name;
+ case SIGNAL:
+ return signal->identifier->name;
+ case VARIABLE:
+ return variable->identifier->name;
+ case ENUM:
+ // All enum-type members have an id.
+ return m_enum->identifier->name;
+ case ENUM_VALUE:
+ return enum_value.identifier->name;
+ case GROUP:
+ return annotation->export_info.name;
+ }
+ return "";
+ }
+
String get_type_name() const {
switch (type) {
case UNDEFINED:
@@ -576,31 +605,42 @@ public:
return variable->get_datatype();
case ENUM:
return m_enum->get_datatype();
- case ENUM_VALUE: {
- // Always integer.
- DataType out_type;
- out_type.type_source = DataType::ANNOTATED_EXPLICIT;
- out_type.kind = DataType::BUILTIN;
- out_type.builtin_type = Variant::INT;
- return out_type;
- }
- case SIGNAL: {
- DataType out_type;
- out_type.type_source = DataType::ANNOTATED_EXPLICIT;
- out_type.kind = DataType::BUILTIN;
- out_type.builtin_type = Variant::SIGNAL;
- // TODO: Add parameter info.
- return out_type;
- }
- case GROUP: {
+ case ENUM_VALUE:
+ return enum_value.identifier->get_datatype();
+ case SIGNAL:
+ return signal->get_datatype();
+ case GROUP:
return DataType();
- }
case UNDEFINED:
return DataType();
}
ERR_FAIL_V_MSG(DataType(), "Reaching unhandled type.");
}
+ Node *get_source_node() const {
+ switch (type) {
+ case CLASS:
+ return m_class;
+ case CONSTANT:
+ return constant;
+ case FUNCTION:
+ return function;
+ case VARIABLE:
+ return variable;
+ case ENUM:
+ return m_enum;
+ case ENUM_VALUE:
+ return enum_value.identifier;
+ case SIGNAL:
+ return signal;
+ case GROUP:
+ return annotation;
+ case UNDEFINED:
+ return nullptr;
+ }
+ ERR_FAIL_V_MSG(nullptr, "Reaching unhandled type.");
+ }
+
Member() {}
Member(ClassNode *p_class) {
@@ -1430,6 +1470,8 @@ public:
Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion);
ClassNode *get_tree() const { return head; }
bool is_tool() const { return _is_tool; }
+ ClassNode *find_class(const String &p_qualified_name) const;
+ bool has_class(const GDScriptParser::ClassNode *p_class) const;
static Variant::Type get_builtin_type(const StringName &p_type);
CompletionContext get_completion_context() const { return completion_context; }
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
index 87863baf75..b9a1d301ad 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The member "Vector2" cannot have the same name as a builtin type.
+Class "Vector2" hides a built-in type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd
new file mode 100644
index 0000000000..d2f6404cd2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.gd
@@ -0,0 +1,8 @@
+func test():
+ print(InnerA.new())
+
+class InnerA extends InnerB:
+ pass
+
+class InnerB extends InnerA:
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out
new file mode 100644
index 0000000000..75a94baa17
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_inheritance.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cyclic inheritance.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd
new file mode 100644
index 0000000000..4292534951
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.gd
@@ -0,0 +1,5 @@
+func test():
+ print(c1)
+
+const c1 = c2
+const c2 = c1
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out
new file mode 100644
index 0000000000..e71b3fc56a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_const.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "c1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd
new file mode 100644
index 0000000000..1caef3d366
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.gd
@@ -0,0 +1,5 @@
+func test():
+ print(E1.V)
+
+enum E1 {V = E2.V}
+enum E2 {V = E1.V}
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out
new file mode 100644
index 0000000000..1b6569ba3a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "E1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd
new file mode 100644
index 0000000000..237758f340
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.gd
@@ -0,0 +1,5 @@
+func test():
+ print(EV1)
+
+enum {EV1 = EV2}
+enum {EV2 = EV1}
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out
new file mode 100644
index 0000000000..233f5fee25
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_enum_value.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "EV1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd
new file mode 100644
index 0000000000..52e0d60389
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.gd
@@ -0,0 +1,6 @@
+func test():
+ print(v)
+
+var v = A.v
+
+const A = preload("cyclic_ref_external_a.notest.gd")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out
new file mode 100644
index 0000000000..64a6bd417d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "v".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd
new file mode 100644
index 0000000000..9ef1769250
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_external_a.notest.gd
@@ -0,0 +1,3 @@
+const B = preload("cyclic_ref_external.gd")
+
+var v = B.v
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd
new file mode 100644
index 0000000000..b610464c44
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.gd
@@ -0,0 +1,9 @@
+func test():
+ print(f1())
+ print(f2())
+
+static func f1(p := f2()) -> int:
+ return 1
+
+static func f2(p := f1()) -> int:
+ return 2
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out
new file mode 100644
index 0000000000..d3ec4b0692
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_func.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "f1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd
new file mode 100644
index 0000000000..f750715838
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.gd
@@ -0,0 +1,12 @@
+func test():
+ print(v)
+
+var v := InnerA.new().f()
+
+class InnerA:
+ func f(p := InnerB.new().f()) -> int:
+ return 1
+
+class InnerB extends InnerA:
+ func f(p := 1) -> int:
+ return super.f()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out
new file mode 100644
index 0000000000..6bca25b330
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_override.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "f": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd
new file mode 100644
index 0000000000..6913888724
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.gd
@@ -0,0 +1,5 @@
+func test():
+ print(v1)
+
+var v1 := v2
+var v2 := v1
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out
new file mode 100644
index 0000000000..c337882d9c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/cyclic_ref_var.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Could not resolve member "v1": Cyclic reference.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd
new file mode 100644
index 0000000000..069e54c528
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.gd
@@ -0,0 +1,50 @@
+func test():
+ print("v1: ", v1)
+ print("v1 is String: ", v1 is String)
+ print("v2: ", v2)
+ print("v2 is bool: ", v2 is bool)
+ print("c1: ", c1)
+ print("c1 is int: ", c1 is int)
+ print("c2: ", c2)
+ print("c2 is int: ", c2 is int)
+ print("E1.V1: ", E1.V1)
+ print("E1.V2: ", E1.V2)
+ print("E2.V: ", E2.V)
+ print("EV1: ", EV1)
+ print("EV2: ", EV2)
+ print("EV3: ", EV3)
+
+var v1 := InnerA.new().fn()
+
+class InnerA extends InnerAB:
+ func fn(p2 := E1.V2) -> String:
+ return "%s, p2=%s" % [super.fn(), p2]
+
+ class InnerAB:
+ func fn(p1 := c1) -> String:
+ return "p1=%s" % p1
+
+var v2 := f()
+
+func f() -> bool:
+ return true
+
+const c1 := E1.V1
+
+enum E1 {
+ V1 = E2.V + 2,
+ V2 = V1 - 1
+}
+
+enum E2 {V = 2}
+
+const c2 := EV2
+
+enum {
+ EV1 = 42,
+ EV2 = EV3 + 1
+}
+
+enum {
+ EV3 = EV1 + 1
+}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order.out b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.out
new file mode 100644
index 0000000000..b1e75d611d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+v1: p1=4, p2=3
+v1 is String: true
+v2: true
+v2 is bool: true
+c1: 4
+c1 is int: true
+c2: 44
+c2 is int: true
+E1.V1: 4
+E1.V2: 3
+E2.V: 2
+EV1: 42
+EV2: 44
+EV3: 43
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd
new file mode 100644
index 0000000000..0b162bdff8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.gd
@@ -0,0 +1,39 @@
+const B = preload("out_of_order_external_a.notest.gd")
+
+func test():
+ print("v1: ", v1)
+ print("v1 is String: ", v1 is String)
+ print("v2: ", v2)
+ print("v2 is bool: ", v2 is bool)
+ print("c1: ", c1)
+ print("c1 is int: ", c1 is int)
+ print("c2: ", c2)
+ print("c2 is int: ", c2 is int)
+ print("E1.V1: ", E1.V1)
+ print("E1.V2: ", E1.V2)
+ print("B.E2.V: ", B.E2.V)
+ print("EV1: ", EV1)
+ print("EV2: ", EV2)
+ print("B.EV3: ", B.EV3)
+
+var v1 := Inner.new().fn()
+
+class Inner extends B.Inner:
+ func fn(p2 := E1.V2) -> String:
+ return "%s, p2=%s" % [super.fn(), p2]
+
+var v2 := B.new().f()
+
+const c1 := E1.V1
+
+enum E1 {
+ V1 = B.E2.V + 2,
+ V2 = V1 - 1
+}
+
+const c2 := EV2
+
+enum {
+ EV1 = 42,
+ EV2 = B.EV3 + 1
+}
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out
new file mode 100644
index 0000000000..437f782fe6
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external.out
@@ -0,0 +1,15 @@
+GDTEST_OK
+v1: p1=4, p2=3
+v1 is String: true
+v2: true
+v2 is bool: true
+c1: 4
+c1 is int: true
+c2: 44
+c2 is int: true
+E1.V1: 4
+E1.V2: 3
+B.E2.V: 2
+EV1: 42
+EV2: 44
+B.EV3: 43
diff --git a/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd
new file mode 100644
index 0000000000..d276f72fcf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/out_of_order_external_a.notest.gd
@@ -0,0 +1,12 @@
+const A = preload("out_of_order_external.gd")
+
+class Inner:
+ func fn(p1 := A.c1) -> String:
+ return "p1=%s" % p1
+
+func f(p := A.c1) -> bool:
+ return p is int
+
+enum E2 {V = 2}
+
+enum {EV3 = A.EV1 + 1}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 7ef42247d1..6559cbf75d 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -2489,9 +2489,12 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
if (!ret_void) {
if (return_type->cname != name_cache.type_Variant) {
+ // Usually the return value takes ownership, but in this case the variant is only used
+ // for conversion to another return type. As such, the local variable takes ownership.
r_output << "using godot_variant " << C_LOCAL_VARARG_RET " = ";
} else {
- r_output << "using godot_variant " << C_LOCAL_RET " = ";
+ // Variant's [c_out] takes ownership of the variant value
+ r_output << "godot_variant " << C_LOCAL_RET " = ";
}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 2f26497cc8..0ba86e4316 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -422,7 +422,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
private static boolean isMouseEvent(int eventSource) {
- boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & InputDevice.SOURCE_STYLUS) == InputDevice.SOURCE_STYLUS);
+ boolean mouseSource = ((eventSource & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) || ((eventSource & (InputDevice.SOURCE_TOUCHSCREEN | InputDevice.SOURCE_STYLUS)) == InputDevice.SOURCE_STYLUS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mouseSource = mouseSource || ((eventSource & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE);
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index f26e05518e..d9f76a6fa6 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -150,7 +150,12 @@ RichTextLabel::Item *RichTextLabel::_get_item_at_pos(RichTextLabel::Item *p_item
return it;
}
} break;
- case ITEM_IMAGE:
+ case ITEM_IMAGE: {
+ offset += 1;
+ if (offset > p_position) {
+ return it;
+ }
+ } break;
case ITEM_TABLE: {
offset += 1;
} break;
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index f860bab15e..a55cccdc6b 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -70,17 +70,17 @@ void RenderForwardMobile::ForwardIDStorageMobile::map_forward_id(RendererRD::For
void RenderForwardMobile::ForwardIDStorageMobile::fill_push_constant_instance_indices(GeometryInstanceForwardMobile::PushConstant *p_push_constant, uint32_t &spec_constants, const GeometryInstanceForwardMobile *p_instance) {
// first zero out our indices
- p_push_constant->omni_lights[0] = 0xFFFF;
- p_push_constant->omni_lights[1] = 0xFFFF;
+ p_push_constant->omni_lights[0] = 0xFFFFFFFF;
+ p_push_constant->omni_lights[1] = 0xFFFFFFFF;
- p_push_constant->spot_lights[0] = 0xFFFF;
- p_push_constant->spot_lights[1] = 0xFFFF;
+ p_push_constant->spot_lights[0] = 0xFFFFFFFF;
+ p_push_constant->spot_lights[1] = 0xFFFFFFFF;
- p_push_constant->decals[0] = 0xFFFF;
- p_push_constant->decals[1] = 0xFFFF;
+ p_push_constant->decals[0] = 0xFFFFFFFF;
+ p_push_constant->decals[1] = 0xFFFFFFFF;
- p_push_constant->reflection_probes[0] = 0xFFFF;
- p_push_constant->reflection_probes[1] = 0xFFFF;
+ p_push_constant->reflection_probes[0] = 0xFFFFFFFF;
+ p_push_constant->reflection_probes[1] = 0xFFFFFFFF;
if (p_instance->omni_light_count == 0) {
spec_constants |= 1 << SPEC_CONSTANT_DISABLE_OMNI_LIGHTS;
diff --git a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
index a8b2d77718..631d1968b0 100644
--- a/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/screen_space_reflection.glsl
@@ -66,6 +66,19 @@ void main() {
vec4 normal_roughness = imageLoad(source_normal_roughness, ssC);
vec3 normal = normal_roughness.xyz * 2.0 - 1.0;
+ float roughness = normal_roughness.w;
+
+ // The roughness cutoff of 0.6 is chosen to match the roughness fadeout from GH-69828.
+ if (roughness > 0.6) {
+ // Do not compute SSR for rough materials to improve performance at the cost of
+ // subtle artifacting.
+#ifdef MODE_ROUGH
+ imageStore(blur_radius_image, ssC, vec4(0.0));
+#endif
+ imageStore(ssr_image, ssC, vec4(0.0));
+ return;
+ }
+
normal = normalize(normal);
normal.y = -normal.y; //because this code reads flipped
@@ -81,8 +94,6 @@ void main() {
imageStore(ssr_image, ssC, vec4(0.0));
return;
}
- //ray_dir = normalize(view_dir - normal * dot(normal,view_dir) * 2.0);
- //ray_dir = normalize(vec3(1.0, 1.0, -1.0));
////////////////
@@ -223,7 +234,6 @@ void main() {
// if roughness is enabled, do screen space cone tracing
float blur_radius = 0.0;
- float roughness = normal_roughness.w;
if (roughness > 0.001) {
float cone_angle = min(roughness, 0.999) * M_PI * 0.5;