summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_parser.cpp')
-rw-r--r--modules/gdscript/gdscript_parser.cpp50
1 files changed, 43 insertions, 7 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index f3a4f2eaa6..03cf334bed 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -93,6 +93,7 @@ bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
GDScriptParser::GDScriptParser() {
// Register valid annotations.
if (unlikely(valid_annotations.is_empty())) {
+ register_annotation(MethodInfo("@uid", PropertyInfo(Variant::STRING, "uid")), AnnotationInfo::SCRIPT, &GDScriptParser::uid_annotation);
register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
register_annotation(MethodInfo("@icon", PropertyInfo(Variant::STRING, "icon_path")), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
register_annotation(MethodInfo("@static_unload"), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation);
@@ -520,6 +521,8 @@ void GDScriptParser::parse_program() {
// `@icon` needs to be applied in the parser. See GH-72444.
if (annotation->name == SNAME("@icon")) {
annotation->apply(this, head, nullptr);
+ } else if (annotation->name == SNAME("@uid")) {
+ annotation->apply(this, head, nullptr);
} else {
head->annotations.push_back(annotation);
}
@@ -629,7 +632,7 @@ GDScriptParser::ClassNode *GDScriptParser::find_class(const String &p_qualified_
// 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];
+ const 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);
@@ -1120,7 +1123,12 @@ void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
case VariableNode::PROP_INLINE: {
FunctionNode *function = alloc_node<FunctionNode>();
- consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)");
+ if (match(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) {
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after "get(".)*");
+ consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after "get()".)*");
+ } else {
+ consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" or "(" after "get".)");
+ }
IdentifierNode *identifier = alloc_node<IdentifierNode>();
complete_extents(identifier);
@@ -1268,8 +1276,7 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum(bool p_is_static) {
EnumNode *enum_node = alloc_node<EnumNode>();
bool named = false;
- if (check(GDScriptTokenizer::Token::IDENTIFIER)) {
- advance();
+ if (match(GDScriptTokenizer::Token::IDENTIFIER)) {
enum_node->identifier = parse_identifier();
named = true;
}
@@ -3830,18 +3837,18 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
}
// `@icon`'s argument needs to be resolved in the parser. See GH-72444.
- if (p_annotation->name == SNAME("@icon")) {
+ if (p_annotation->name == SNAME("@icon") || p_annotation->name == SNAME("@uid")) {
ExpressionNode *argument = p_annotation->arguments[0];
if (argument->type != Node::LITERAL) {
- push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument);
+ push_error(vformat(R"(Argument 1 of annotation "%s" must be a string literal.)", p_annotation->name), argument);
return false;
}
Variant value = static_cast<LiteralNode *>(argument)->value;
if (value.get_type() != Variant::STRING) {
- push_error(R"(Argument 1 of annotation "@icon" must be a string literal.)", argument);
+ push_error(vformat(R"(Argument 1 of annotation "%s" must be a string literal.)", p_annotation->name), argument);
return false;
}
@@ -3853,6 +3860,35 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
return true;
}
+bool GDScriptParser::uid_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+ ERR_FAIL_COND_V_MSG(p_target->type != Node::CLASS, false, R"("@uid" annotation can only be applied to classes.)");
+ ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false);
+
+#ifdef DEBUG_ENABLED
+ if (this->_has_uid) {
+ push_error(R"("@uid" annotation can only be used once.)", p_annotation);
+ return false;
+ }
+#endif // DEBUG_ENABLED
+
+ // Assign line range early to allow replacing invalid UIDs.
+ ClassNode *class_node = static_cast<ClassNode *>(p_target);
+ class_node->uid_lines = Vector2i(p_annotation->start_line - 1, p_annotation->end_line - 1); // Lines start from 1, so need to subtract.
+
+ const String &uid_string = p_annotation->resolved_arguments[0];
+#ifdef DEBUG_ENABLED
+ if (ResourceUID::get_singleton()->text_to_id(uid_string) == ResourceUID::INVALID_ID) {
+ push_error(R"(The annotated UID is invalid.)", p_annotation);
+ return false;
+ }
+#endif // DEBUG_ENABLED
+
+ class_node->uid_string = uid_string;
+
+ this->_has_uid = true;
+ return true;
+}
+
bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
#ifdef DEBUG_ENABLED
if (this->_is_tool) {