Browse Source

GDScript: Add error when exporting node in non [Node]-derived classes

Danil Alexeev 1 year ago
parent
commit
9e2273abc7

+ 26 - 1
modules/gdscript/doc_classes/@GDScript.xml

@@ -285,11 +285,36 @@
 			<description>
 			<description>
 				Mark the following property as exported (editable in the Inspector dock and saved to disk). To control the type of the exported property, use the type hint notation.
 				Mark the following property as exported (editable in the Inspector dock and saved to disk). To control the type of the exported property, use the type hint notation.
 				[codeblock]
 				[codeblock]
+				extends Node
+
+				enum Direction {LEFT, RIGHT, UP, DOWN}
+
+				# Built-in types.
 				@export var string = ""
 				@export var string = ""
 				@export var int_number = 5
 				@export var int_number = 5
 				@export var float_number: float = 5
 				@export var float_number: float = 5
+
+				# Enums.
+				@export var type: Variant.Type
+				@export var format: Image.Format
+				@export var direction: Direction
+
+				# Resources.
 				@export var image: Image
 				@export var image: Image
-				[/codeblock]
+				@export var custom_resource: CustomResource
+
+				# Nodes.
+				@export var node: Node
+				@export var custom_node: CustomNode
+
+				# Typed arrays.
+				@export var int_array: Array[int]
+				@export var direction_array: Array[Direction]
+				@export var image_array: Array[Image]
+				@export var node_array: Array[Node]
+				[/codeblock]
+				[b]Note:[/b] Custom resources and nodes must be registered as global classes using [code]class_name[/code].
+				[b]Note:[/b] Node export is only supported in [Node]-derived classes and has a number of other limitations.
 			</description>
 			</description>
 		</annotation>
 		</annotation>
 		<annotation name="@export_category">
 		<annotation name="@export_category">

+ 10 - 10
modules/gdscript/gdscript_analyzer.cpp

@@ -910,7 +910,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
 			for (GDScriptParser::AnnotationNode *&E : member_node->annotations) {
 			for (GDScriptParser::AnnotationNode *&E : member_node->annotations) {
 				if (E->name == SNAME("@warning_ignore")) {
 				if (E->name == SNAME("@warning_ignore")) {
 					resolve_annotation(E);
 					resolve_annotation(E);
-					E->apply(parser, member.variable);
+					E->apply(parser, member.variable, p_class);
 				}
 				}
 			}
 			}
 			for (GDScriptWarning::Code ignored_warning : member_node->ignored_warnings) {
 			for (GDScriptWarning::Code ignored_warning : member_node->ignored_warnings) {
@@ -933,7 +933,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
 				for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
 				for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
 					if (E->name != SNAME("@warning_ignore")) {
 					if (E->name != SNAME("@warning_ignore")) {
 						resolve_annotation(E);
 						resolve_annotation(E);
-						E->apply(parser, member.variable);
+						E->apply(parser, member.variable, p_class);
 					}
 					}
 				}
 				}
 
 
@@ -985,7 +985,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
 				// Apply annotations.
 				// Apply annotations.
 				for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) {
 				for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) {
 					resolve_annotation(E);
 					resolve_annotation(E);
-					E->apply(parser, member.constant);
+					E->apply(parser, member.constant, p_class);
 				}
 				}
 			} break;
 			} break;
 			case GDScriptParser::ClassNode::Member::SIGNAL: {
 			case GDScriptParser::ClassNode::Member::SIGNAL: {
@@ -1015,7 +1015,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
 				// Apply annotations.
 				// Apply annotations.
 				for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) {
 				for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) {
 					resolve_annotation(E);
 					resolve_annotation(E);
-					E->apply(parser, member.signal);
+					E->apply(parser, member.signal, p_class);
 				}
 				}
 			} break;
 			} break;
 			case GDScriptParser::ClassNode::Member::ENUM: {
 			case GDScriptParser::ClassNode::Member::ENUM: {
@@ -1063,13 +1063,13 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
 				// Apply annotations.
 				// Apply annotations.
 				for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) {
 				for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) {
 					resolve_annotation(E);
 					resolve_annotation(E);
-					E->apply(parser, member.m_enum);
+					E->apply(parser, member.m_enum, p_class);
 				}
 				}
 			} break;
 			} break;
 			case GDScriptParser::ClassNode::Member::FUNCTION:
 			case GDScriptParser::ClassNode::Member::FUNCTION:
 				for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
 				for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
 					resolve_annotation(E);
 					resolve_annotation(E);
-					E->apply(parser, member.function);
+					E->apply(parser, member.function, p_class);
 				}
 				}
 				resolve_function_signature(member.function, p_source);
 				resolve_function_signature(member.function, p_source);
 				break;
 				break;
@@ -1279,7 +1279,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
 			// Apply annotations.
 			// Apply annotations.
 			for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
 			for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
 				resolve_annotation(E);
 				resolve_annotation(E);
-				E->apply(parser, member.function);
+				E->apply(parser, member.function, p_class);
 			}
 			}
 			resolve_function_body(member.function);
 			resolve_function_body(member.function);
 		} else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) {
 		} else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) {
@@ -1301,7 +1301,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
 		} else if (member.type == GDScriptParser::ClassNode::Member::GROUP) {
 		} else if (member.type == GDScriptParser::ClassNode::Member::GROUP) {
 			// Apply annotation (`@export_{category,group,subgroup}`).
 			// Apply annotation (`@export_{category,group,subgroup}`).
 			resolve_annotation(member.annotation);
 			resolve_annotation(member.annotation);
-			member.annotation->apply(parser, nullptr);
+			member.annotation->apply(parser, nullptr, p_class);
 		}
 		}
 	}
 	}
 
 
@@ -1837,7 +1837,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
 		// Apply annotations.
 		// Apply annotations.
 		for (GDScriptParser::AnnotationNode *&E : stmt->annotations) {
 		for (GDScriptParser::AnnotationNode *&E : stmt->annotations) {
 			resolve_annotation(E);
 			resolve_annotation(E);
-			E->apply(parser, stmt);
+			E->apply(parser, stmt, nullptr);
 		}
 		}
 
 
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
@@ -5544,7 +5544,7 @@ Error GDScriptAnalyzer::analyze() {
 	// Apply annotations.
 	// Apply annotations.
 	for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) {
 	for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) {
 		resolve_annotation(E);
 		resolve_annotation(E);
-		E->apply(parser, parser->head);
+		E->apply(parser, parser->head, nullptr);
 	}
 	}
 
 
 	resolve_interface();
 	resolve_interface();

+ 35 - 29
modules/gdscript/gdscript_parser.cpp

@@ -519,7 +519,7 @@ void GDScriptParser::parse_program() {
 				if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
 				if (annotation->applies_to(AnnotationInfo::SCRIPT)) {
 					// `@icon` needs to be applied in the parser. See GH-72444.
 					// `@icon` needs to be applied in the parser. See GH-72444.
 					if (annotation->name == SNAME("@icon")) {
 					if (annotation->name == SNAME("@icon")) {
-						annotation->apply(this, head);
+						annotation->apply(this, head, nullptr);
 					} else {
 					} else {
 						head->annotations.push_back(annotation);
 						head->annotations.push_back(annotation);
 					}
 					}
@@ -3795,12 +3795,12 @@ const GDScriptParser::SuiteNode::Local &GDScriptParser::SuiteNode::get_local(con
 	return empty;
 	return empty;
 }
 }
 
 
-bool GDScriptParser::AnnotationNode::apply(GDScriptParser *p_this, Node *p_target) {
+bool GDScriptParser::AnnotationNode::apply(GDScriptParser *p_this, Node *p_target, ClassNode *p_class) {
 	if (is_applied) {
 	if (is_applied) {
 		return true;
 		return true;
 	}
 	}
 	is_applied = true;
 	is_applied = true;
-	return (p_this->*(p_this->valid_annotations[name].apply))(this, p_target);
+	return (p_this->*(p_this->valid_annotations[name].apply))(this, p_target, p_class);
 }
 }
 
 
 bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const {
 bool GDScriptParser::AnnotationNode::applies_to(uint32_t p_target_kinds) const {
@@ -3846,7 +3846,7 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
 	return true;
 	return true;
 }
 }
 
 
-bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p_node) {
+bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 	if (this->_is_tool) {
 	if (this->_is_tool) {
 		push_error(R"("@tool" annotation can only be used once.)", p_annotation);
 		push_error(R"("@tool" annotation can only be used once.)", p_annotation);
@@ -3857,15 +3857,15 @@ bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p
 	return true;
 	return true;
 }
 }
 
 
-bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p_node) {
-	ERR_FAIL_COND_V_MSG(p_node->type != Node::CLASS, false, R"("@icon" annotation can only be applied to classes.)");
+bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+	ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, R"("@icon" annotation can only be applied to classes.)");
 	ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false);
 	ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false);
 
 
-	ClassNode *p_class = static_cast<ClassNode *>(p_node);
+	ClassNode *class_node = static_cast<ClassNode *>(p_target);
 	String path = p_annotation->resolved_arguments[0];
 	String path = p_annotation->resolved_arguments[0];
 
 
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
-	if (!p_class->icon_path.is_empty()) {
+	if (!class_node->icon_path.is_empty()) {
 		push_error(R"("@icon" annotation can only be used once.)", p_annotation);
 		push_error(R"("@icon" annotation can only be used once.)", p_annotation);
 		return false;
 		return false;
 	}
 	}
@@ -3875,27 +3875,27 @@ bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p
 	}
 	}
 #endif // DEBUG_ENABLED
 #endif // DEBUG_ENABLED
 
 
-	p_class->icon_path = path;
+	class_node->icon_path = path;
 
 
 	if (path.is_empty() || path.is_absolute_path()) {
 	if (path.is_empty() || path.is_absolute_path()) {
-		p_class->simplified_icon_path = path.simplify_path();
+		class_node->simplified_icon_path = path.simplify_path();
 	} else if (path.is_relative_path()) {
 	} else if (path.is_relative_path()) {
-		p_class->simplified_icon_path = script_path.get_base_dir().path_join(path).simplify_path();
+		class_node->simplified_icon_path = script_path.get_base_dir().path_join(path).simplify_path();
 	} else {
 	} else {
-		p_class->simplified_icon_path = path;
+		class_node->simplified_icon_path = path;
 	}
 	}
 
 
 	return true;
 	return true;
 }
 }
 
 
-bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node *p_node) {
-	ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, R"("@onready" annotation can only be applied to class variables.)");
+bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+	ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, R"("@onready" annotation can only be applied to class variables.)");
 
 
 	if (current_class && !ClassDB::is_parent_class(current_class->get_datatype().native_type, SNAME("Node"))) {
 	if (current_class && !ClassDB::is_parent_class(current_class->get_datatype().native_type, SNAME("Node"))) {
 		push_error(R"("@onready" can only be used in classes that inherit "Node".)", p_annotation);
 		push_error(R"("@onready" can only be used in classes that inherit "Node".)", p_annotation);
 	}
 	}
 
 
-	VariableNode *variable = static_cast<VariableNode *>(p_node);
+	VariableNode *variable = static_cast<VariableNode *>(p_target);
 	if (variable->is_static) {
 	if (variable->is_static) {
 		push_error(R"("@onready" annotation cannot be applied to a static variable.)", p_annotation);
 		push_error(R"("@onready" annotation cannot be applied to a static variable.)", p_annotation);
 		return false;
 		return false;
@@ -3910,10 +3910,11 @@ bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node
 }
 }
 
 
 template <PropertyHint t_hint, Variant::Type t_type>
 template <PropertyHint t_hint, Variant::Type t_type>
-bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_node) {
-	ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
+bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+	ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
+	ERR_FAIL_NULL_V(p_class, false);
 
 
-	VariableNode *variable = static_cast<VariableNode *>(p_node);
+	VariableNode *variable = static_cast<VariableNode *>(p_target);
 	if (variable->is_static) {
 	if (variable->is_static) {
 		push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
 		push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
 		return false;
 		return false;
@@ -4128,7 +4129,12 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
 			} break;
 			} break;
 			default:
 			default:
 				push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
 				push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable);
-				break;
+				return false;
+		}
+
+		if (variable->export_info.hint == PROPERTY_HINT_NODE_TYPE && !ClassDB::is_parent_class(p_class->base_type.native_type, SNAME("Node"))) {
+			push_error(vformat(R"(Node export is only supported in Node-derived classes, but the current class inherits "%s".)", p_class->base_type.to_string()), variable);
+			return false;
 		}
 		}
 
 
 		if (is_array) {
 		if (is_array) {
@@ -4173,7 +4179,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
 }
 }
 
 
 template <PropertyUsageFlags t_usage>
 template <PropertyUsageFlags t_usage>
-bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation, Node *p_node) {
+bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
 	AnnotationNode *annotation = const_cast<AnnotationNode *>(p_annotation);
 	AnnotationNode *annotation = const_cast<AnnotationNode *>(p_annotation);
 
 
 	if (annotation->resolved_arguments.is_empty()) {
 	if (annotation->resolved_arguments.is_empty()) {
@@ -4205,7 +4211,7 @@ bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation
 	return true;
 	return true;
 }
 }
 
 
-bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_node) {
+bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 	bool has_error = false;
 	bool has_error = false;
 	for (const Variant &warning_name : p_annotation->resolved_arguments) {
 	for (const Variant &warning_name : p_annotation->resolved_arguments) {
@@ -4214,7 +4220,7 @@ bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Nod
 			push_error(vformat(R"(Invalid warning name: "%s".)", warning_name), p_annotation);
 			push_error(vformat(R"(Invalid warning name: "%s".)", warning_name), p_annotation);
 			has_error = true;
 			has_error = true;
 		} else {
 		} else {
-			p_node->ignored_warnings.push_back(warning);
+			p_target->ignored_warnings.push_back(warning);
 		}
 		}
 	}
 	}
 
 
@@ -4226,10 +4232,10 @@ bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Nod
 #endif // DEBUG_ENABLED
 #endif // DEBUG_ENABLED
 }
 }
 
 
-bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_node) {
-	ERR_FAIL_COND_V_MSG(p_node->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to functions.)", p_annotation->name));
+bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+	ERR_FAIL_COND_V_MSG(p_target->type != Node::FUNCTION, false, vformat(R"("%s" annotation can only be applied to functions.)", p_annotation->name));
 
 
-	FunctionNode *function = static_cast<FunctionNode *>(p_node);
+	FunctionNode *function = static_cast<FunctionNode *>(p_target);
 	if (function->rpc_config.get_type() != Variant::NIL) {
 	if (function->rpc_config.get_type() != Variant::NIL) {
 		push_error(R"(RPC annotations can only be used once per function.)", p_annotation);
 		push_error(R"(RPC annotations can only be used once per function.)", p_annotation);
 		return false;
 		return false;
@@ -4287,14 +4293,14 @@ bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_
 	return true;
 	return true;
 }
 }
 
 
-bool GDScriptParser::static_unload_annotation(const AnnotationNode *p_annotation, Node *p_target) {
+bool GDScriptParser::static_unload_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
 	ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, vformat(R"("%s" annotation can only be applied to classes.)", p_annotation->name));
 	ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, vformat(R"("%s" annotation can only be applied to classes.)", p_annotation->name));
-	ClassNode *p_class = static_cast<ClassNode *>(p_target);
-	if (p_class->annotated_static_unload) {
+	ClassNode *class_node = static_cast<ClassNode *>(p_target);
+	if (class_node->annotated_static_unload) {
 		push_error(vformat(R"("%s" annotation can only be used once per script.)", p_annotation->name), p_annotation);
 		push_error(vformat(R"("%s" annotation can only be used once per script.)", p_annotation->name), p_annotation);
 		return false;
 		return false;
 	}
 	}
-	p_class->annotated_static_unload = true;
+	class_node->annotated_static_unload = true;
 	return true;
 	return true;
 }
 }
 
 

+ 10 - 10
modules/gdscript/gdscript_parser.h

@@ -363,7 +363,7 @@ public:
 		bool is_resolved = false;
 		bool is_resolved = false;
 		bool is_applied = false;
 		bool is_applied = false;
 
 
-		bool apply(GDScriptParser *p_this, Node *p_target);
+		bool apply(GDScriptParser *p_this, Node *p_target, ClassNode *p_class);
 		bool applies_to(uint32_t p_target_kinds) const;
 		bool applies_to(uint32_t p_target_kinds) const;
 
 
 		AnnotationNode() {
 		AnnotationNode() {
@@ -1340,7 +1340,7 @@ private:
 	bool in_lambda = false;
 	bool in_lambda = false;
 	bool lambda_ended = false; // Marker for when a lambda ends, to apply an end of statement if needed.
 	bool lambda_ended = false; // Marker for when a lambda ends, to apply an end of statement if needed.
 
 
-	typedef bool (GDScriptParser::*AnnotationAction)(const AnnotationNode *p_annotation, Node *p_target);
+	typedef bool (GDScriptParser::*AnnotationAction)(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
 	struct AnnotationInfo {
 	struct AnnotationInfo {
 		enum TargetKind {
 		enum TargetKind {
 			NONE = 0,
 			NONE = 0,
@@ -1461,16 +1461,16 @@ private:
 	bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
 	bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, const Vector<Variant> &p_default_arguments = Vector<Variant>(), bool p_is_vararg = false);
 	bool validate_annotation_arguments(AnnotationNode *p_annotation);
 	bool validate_annotation_arguments(AnnotationNode *p_annotation);
 	void clear_unused_annotations();
 	void clear_unused_annotations();
-	bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target);
-	bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target);
-	bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target);
+	bool tool_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
+	bool icon_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
+	bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
 	template <PropertyHint t_hint, Variant::Type t_type>
 	template <PropertyHint t_hint, Variant::Type t_type>
-	bool export_annotations(const AnnotationNode *p_annotation, Node *p_target);
+	bool export_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
 	template <PropertyUsageFlags t_usage>
 	template <PropertyUsageFlags t_usage>
-	bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target);
-	bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target);
-	bool rpc_annotation(const AnnotationNode *p_annotation, Node *p_target);
-	bool static_unload_annotation(const AnnotationNode *p_annotation, Node *p_target);
+	bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
+	bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
+	bool rpc_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
+	bool static_unload_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
 	// Statements.
 	// Statements.
 	Node *parse_statement();
 	Node *parse_statement();
 	VariableNode *parse_variable(bool p_is_static);
 	VariableNode *parse_variable(bool p_is_static);

+ 8 - 0
modules/gdscript/tests/scripts/analyzer/errors/export_node_in_non_node_derived_class_1.gd

@@ -0,0 +1,8 @@
+# GH-82809
+
+extends Resource
+
+@export var node: Node
+
+func test():
+	pass

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/errors/export_node_in_non_node_derived_class_1.out

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Node export is only supported in Node-derived classes, but the current class inherits "Resource".

+ 9 - 0
modules/gdscript/tests/scripts/analyzer/errors/export_node_in_non_node_derived_class_2.gd

@@ -0,0 +1,9 @@
+# GH-82809
+
+extends Node
+
+class Inner:
+	@export var node: Node
+
+func test():
+	pass

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/errors/export_node_in_non_node_derived_class_2.out

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Node export is only supported in Node-derived classes, but the current class inherits "RefCounted".

+ 8 - 0
modules/gdscript/tests/scripts/analyzer/errors/export_node_in_non_node_derived_class_3.gd

@@ -0,0 +1,8 @@
+# GH-82809
+
+extends Resource
+
+@export var node_array: Array[Node]
+
+func test():
+	pass

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/errors/export_node_in_non_node_derived_class_3.out

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Node export is only supported in Node-derived classes, but the current class inherits "Resource".

+ 6 - 1
modules/gdscript/tests/scripts/parser/features/export_variable.gd

@@ -1,3 +1,5 @@
+extends Node
+
 @export var example = 99
 @export var example = 99
 @export_range(0, 100) var example_range = 100
 @export_range(0, 100) var example_range = 100
 @export_range(0, 100, 1) var example_range_step = 101
 @export_range(0, 100, 1) var example_range_step = 101
@@ -6,7 +8,8 @@
 @export var color: Color
 @export var color: Color
 @export_color_no_alpha var color_no_alpha: Color
 @export_color_no_alpha var color_no_alpha: Color
 @export_node_path("Sprite2D", "Sprite3D", "Control", "Node") var nodepath := ^"hello"
 @export_node_path("Sprite2D", "Sprite3D", "Control", "Node") var nodepath := ^"hello"
-
+@export var node: Node
+@export var node_array: Array[Node]
 
 
 func test():
 func test():
 	print(example)
 	print(example)
@@ -16,3 +19,5 @@ func test():
 	print(color)
 	print(color)
 	print(color_no_alpha)
 	print(color_no_alpha)
 	print(nodepath)
 	print(nodepath)
+	print(node)
+	print(var_to_str(node_array))

+ 2 - 0
modules/gdscript/tests/scripts/parser/features/export_variable.out

@@ -6,3 +6,5 @@ GDTEST_OK
 (0, 0, 0, 1)
 (0, 0, 0, 1)
 (0, 0, 0, 1)
 (0, 0, 0, 1)
 hello
 hello
+<null>
+Array[Node]([])