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.cpp251
1 files changed, 155 insertions, 96 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index d706f4e9a3..d0948e4831 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -101,7 +101,6 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
// Export annotations.
register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
- register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true);
register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
@@ -121,6 +120,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
+ register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_storage_annotation);
register_annotation(MethodInfo("@export_custom", PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_ENUM, "PropertyHint"), PropertyInfo(Variant::STRING, "hint_string"), PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_BITFIELD, "PropertyUsageFlags")), AnnotationInfo::VARIABLE, &GDScriptParser::export_custom_annotation, varray(PROPERTY_USAGE_DEFAULT));
// Export grouping annotations.
register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
@@ -147,23 +147,17 @@ GDScriptParser::GDScriptParser() {
}
GDScriptParser::~GDScriptParser() {
- clear();
-}
-
-void GDScriptParser::clear() {
while (list != nullptr) {
Node *element = list;
list = list->next;
memdelete(element);
}
+}
- head = nullptr;
- list = nullptr;
- _is_tool = false;
- for_completion = false;
- errors.clear();
- multiline_stack.clear();
- nodes_in_progress.clear();
+void GDScriptParser::clear() {
+ GDScriptParser tmp;
+ tmp = *this;
+ *this = GDScriptParser();
}
void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
@@ -172,7 +166,7 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
panic_mode = true;
// TODO: Improve positional information.
if (p_origin == nullptr) {
- errors.push_back({ p_message, current.start_line, current.start_column });
+ errors.push_back({ p_message, previous.start_line, previous.start_column });
} else {
errors.push_back({ p_message, p_origin->start_line, p_origin->leftmost_column });
}
@@ -709,6 +703,25 @@ void GDScriptParser::parse_program() {
clear_unused_annotations();
}
+Ref<GDScriptParserRef> GDScriptParser::get_depended_parser_for(const String &p_path) {
+ Ref<GDScriptParserRef> ref;
+ if (depended_parsers.has(p_path)) {
+ ref = depended_parsers[p_path];
+ } else {
+ Error err = OK;
+ ref = GDScriptCache::get_parser(p_path, GDScriptParserRef::EMPTY, err, script_path);
+ if (ref.is_valid()) {
+ depended_parsers[p_path] = ref;
+ }
+ }
+
+ return ref;
+}
+
+const HashMap<String, Ref<GDScriptParserRef>> &GDScriptParser::get_depended_parsers() {
+ return depended_parsers;
+}
+
GDScriptParser::ClassNode *GDScriptParser::find_class(const String &p_qualified_name) const {
String first = p_qualified_name.get_slice("::", 0);
@@ -1864,6 +1877,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
case Node::CALL:
// Fine.
break;
+ case Node::PRELOAD:
+ // `preload` is a function-like keyword.
+ push_warning(expression, GDScriptWarning::RETURN_VALUE_DISCARDED, "preload");
+ break;
case Node::LAMBDA:
// Standalone lambdas can't be used, so make this an error.
push_error("Standalone lambdas cannot be accessed. Consider assigning it to a variable.", expression);
@@ -2270,28 +2287,31 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
break;
case GDScriptTokenizer::Token::BRACKET_OPEN: {
// Array.
+ push_multiline(true);
advance();
pattern->pattern_type = PatternNode::PT_ARRAY;
-
- if (!check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
- do {
- PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
- if (sub_pattern == nullptr) {
- continue;
- }
- if (pattern->rest_used) {
- push_error(R"(The ".." pattern must be the last element in the pattern array.)");
- } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
- pattern->rest_used = true;
- }
- pattern->array.push_back(sub_pattern);
- } while (match(GDScriptTokenizer::Token::COMMA));
- }
+ do {
+ if (is_at_end() || check(GDScriptTokenizer::Token::BRACKET_CLOSE)) {
+ break;
+ }
+ PatternNode *sub_pattern = parse_match_pattern(p_root_pattern != nullptr ? p_root_pattern : pattern);
+ if (sub_pattern == nullptr) {
+ continue;
+ }
+ if (pattern->rest_used) {
+ push_error(R"(The ".." pattern must be the last element in the pattern array.)");
+ } else if (sub_pattern->pattern_type == PatternNode::PT_REST) {
+ pattern->rest_used = true;
+ }
+ pattern->array.push_back(sub_pattern);
+ } while (match(GDScriptTokenizer::Token::COMMA));
consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected "]" to close the array pattern.)");
+ pop_multiline();
break;
}
case GDScriptTokenizer::Token::BRACE_OPEN: {
// Dictionary.
+ push_multiline(true);
advance();
pattern->pattern_type = PatternNode::PT_DICTIONARY;
do {
@@ -2334,6 +2354,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
}
} while (match(GDScriptTokenizer::Token::COMMA));
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected "}" to close the dictionary pattern.)");
+ pop_multiline();
break;
}
default: {
@@ -2769,10 +2790,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
return parse_expression(false); // Return the following expression.
}
-#ifdef DEBUG_ENABLED
- VariableNode *source_variable = nullptr;
-#endif
-
switch (p_previous_operand->type) {
case Node::IDENTIFIER: {
#ifdef DEBUG_ENABLED
@@ -2781,8 +2798,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
IdentifierNode *id = static_cast<IdentifierNode *>(p_previous_operand);
switch (id->source) {
case IdentifierNode::LOCAL_VARIABLE:
-
- source_variable = id->variable_source;
id->variable_source->usages--;
break;
case IdentifierNode::LOCAL_CONSTANT:
@@ -2813,16 +2828,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
update_extents(assignment);
make_completion_context(COMPLETION_ASSIGN, assignment);
-#ifdef DEBUG_ENABLED
- bool has_operator = true;
-#endif
switch (previous.type) {
case GDScriptTokenizer::Token::EQUAL:
assignment->operation = AssignmentNode::OP_NONE;
assignment->variant_op = Variant::OP_MAX;
-#ifdef DEBUG_ENABLED
- has_operator = false;
-#endif
break;
case GDScriptTokenizer::Token::PLUS_EQUAL:
assignment->operation = AssignmentNode::OP_ADDITION;
@@ -2878,16 +2887,6 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
}
complete_extents(assignment);
-#ifdef DEBUG_ENABLED
- if (source_variable != nullptr) {
- if (has_operator && source_variable->assignments == 0) {
- push_warning(assignment, GDScriptWarning::UNASSIGNED_VARIABLE_OP_ASSIGN, source_variable->identifier->name);
- }
-
- source_variable->assignments += 1;
- }
-#endif
-
return assignment;
}
@@ -3204,6 +3203,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ // We want code completion after a DOLLAR even if the current code is invalid.
+ make_completion_context(COMPLETION_GET_NODE, nullptr, -1, true);
+
if (!current.is_node_name() && !check(GDScriptTokenizer::Token::LITERAL) && !check(GDScriptTokenizer::Token::SLASH) && !check(GDScriptTokenizer::Token::PERCENT)) {
push_error(vformat(R"(Expected node path as string or identifier after "%s".)", previous.get_name()));
return nullptr;
@@ -3259,7 +3261,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
path_state = PATH_STATE_SLASH;
}
- make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++);
+ make_completion_context(COMPLETION_GET_NODE, get_node, context_argument++, true);
if (match(GDScriptTokenizer::Token::LITERAL)) {
if (previous.literal.get_type() != Variant::STRING) {
@@ -4089,6 +4091,7 @@ bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, 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);
+ return false;
}
VariableNode *variable = static_cast<VariableNode *>(p_target);
@@ -4133,6 +4136,9 @@ static String _get_annotation_error_string(const StringName &p_annotation_name,
case Variant::COLOR:
types.push_back("PackedColorArray");
break;
+ case Variant::VECTOR4:
+ types.push_back("PackedVector4Array");
+ break;
default:
break;
}
@@ -4154,6 +4160,64 @@ static String _get_annotation_error_string(const StringName &p_annotation_name,
return vformat(R"("%s" annotation requires a variable of type %s, but type "%s" was given instead.)", p_annotation_name, string, p_provided_type.to_string());
}
+static StringName _find_narrowest_native_or_global_class(const GDScriptParser::DataType &p_type) {
+ switch (p_type.kind) {
+ case GDScriptParser::DataType::NATIVE: {
+ if (p_type.is_meta_type) {
+ return Object::get_class_static(); // `GDScriptNativeClass` is not an exposed class.
+ }
+ return p_type.native_type;
+ } break;
+ case GDScriptParser::DataType::SCRIPT: {
+ Ref<Script> script;
+ if (p_type.script_type.is_valid()) {
+ script = p_type.script_type;
+ } else {
+ script = ResourceLoader::load(p_type.script_path, SNAME("Script"));
+ }
+
+ if (p_type.is_meta_type) {
+ return script.is_valid() ? script->get_class() : Script::get_class_static();
+ }
+ if (script.is_null()) {
+ return p_type.native_type;
+ }
+ if (script->get_global_name() != StringName()) {
+ return script->get_global_name();
+ }
+
+ Ref<Script> base_script = script->get_base_script();
+ if (base_script.is_null()) {
+ return script->get_instance_base_type();
+ }
+
+ GDScriptParser::DataType base_type;
+ base_type.kind = GDScriptParser::DataType::SCRIPT;
+ base_type.builtin_type = Variant::OBJECT;
+ base_type.native_type = base_script->get_instance_base_type();
+ base_type.script_type = base_script;
+ base_type.script_path = base_script->get_path();
+
+ return _find_narrowest_native_or_global_class(base_type);
+ } break;
+ case GDScriptParser::DataType::CLASS: {
+ if (p_type.is_meta_type) {
+ return GDScript::get_class_static();
+ }
+ if (p_type.class_type == nullptr) {
+ return p_type.native_type;
+ }
+ if (p_type.class_type->get_global_name() != StringName()) {
+ return p_type.class_type->get_global_name();
+ }
+ return _find_narrowest_native_or_global_class(p_type.class_type->base_type);
+ } break;
+ default: {
+ ERR_FAIL_V(StringName());
+ } break;
+ }
+}
+
template <PropertyHint t_hint, Variant::Type t_type>
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));
@@ -4293,56 +4357,24 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
case GDScriptParser::DataType::BUILTIN:
variable->export_info.type = export_type.builtin_type;
variable->export_info.hint = PROPERTY_HINT_NONE;
- variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type);
+ variable->export_info.hint_string = String();
break;
case GDScriptParser::DataType::NATIVE:
+ case GDScriptParser::DataType::SCRIPT:
+ case GDScriptParser::DataType::CLASS: {
+ const StringName class_name = _find_narrowest_native_or_global_class(export_type);
if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
- variable->export_info.hint_string = export_type.native_type;
- } else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
- variable->export_info.type = Variant::OBJECT;
- variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
- variable->export_info.hint_string = export_type.native_type;
- } else {
- push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", p_annotation);
- return false;
- }
- break;
- case GDScriptParser::DataType::CLASS:
- if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
- variable->export_info.type = Variant::OBJECT;
- variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
- variable->export_info.hint_string = export_type.to_string();
+ variable->export_info.hint_string = class_name;
} else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
- variable->export_info.hint_string = export_type.to_string();
+ variable->export_info.hint_string = class_name;
} else {
push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", p_annotation);
return false;
}
-
- break;
- case GDScriptParser::DataType::SCRIPT: {
- StringName class_name;
- StringName native_base;
- if (export_type.script_type.is_valid()) {
- class_name = export_type.script_type->get_language()->get_global_class_name(export_type.script_type->get_path());
- native_base = export_type.script_type->get_instance_base_type();
- }
- if (class_name == StringName()) {
- Ref<Script> script = ResourceLoader::load(export_type.script_path, SNAME("Script"));
- if (script.is_valid()) {
- class_name = script->get_language()->get_global_class_name(export_type.script_path);
- native_base = script->get_instance_base_type();
- }
- }
- if (class_name != StringName() && native_base != StringName() && ClassDB::is_parent_class(native_base, SNAME("Resource"))) {
- variable->export_info.type = Variant::OBJECT;
- variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
- variable->export_info.hint_string = class_name;
- }
} break;
case GDScriptParser::DataType::ENUM: {
if (export_type.is_meta_type) {
@@ -4394,12 +4426,6 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
push_error(_get_annotation_error_string(p_annotation->name, expected_types, variable->get_datatype()), p_annotation);
return false;
}
- } else if (p_annotation->name == SNAME("@export_storage")) {
- use_default_variable_type_check = false; // Can be applied to a variable of any type.
-
- // Save the info because the compiler uses export info for overwriting member info.
- variable->export_info = export_type.to_property_info(variable->identifier->name);
- variable->export_info.usage |= PROPERTY_USAGE_STORAGE;
}
if (use_default_variable_type_check) {
@@ -4419,19 +4445,50 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
if (variable->export_info.hint) {
hint_prefix += "/" + itos(variable->export_info.hint);
}
+ variable->export_info.type = original_export_type_builtin;
variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
- variable->export_info.type = original_export_type_builtin;
+ variable->export_info.usage = PROPERTY_USAGE_DEFAULT;
+ variable->export_info.class_name = StringName();
}
return true;
}
+// For `@export_storage` and `@export_custom`, there is no need to check the variable type, argument values,
+// or handle array exports in a special way, so they are implemented as separate methods.
+
+bool GDScriptParser::export_storage_annotation(const AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
+ ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
+
+ VariableNode *variable = static_cast<VariableNode *>(p_node);
+ if (variable->is_static) {
+ push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
+ return false;
+ }
+ if (variable->exported) {
+ push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
+ return false;
+ }
+
+ variable->exported = true;
+
+ // Save the info because the compiler uses export info for overwriting member info.
+ variable->export_info = variable->get_datatype().to_property_info(variable->identifier->name);
+ variable->export_info.usage |= PROPERTY_USAGE_STORAGE;
+
+ return true;
+}
+
bool GDScriptParser::export_custom_annotation(const AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
ERR_FAIL_COND_V_MSG(p_annotation->resolved_arguments.size() < 2, false, R"(Annotation "@export_custom" requires 2 arguments.)");
VariableNode *variable = static_cast<VariableNode *>(p_node);
+ if (variable->is_static) {
+ push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
+ return false;
+ }
if (variable->exported) {
push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
return false;
@@ -4831,6 +4888,8 @@ static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_t
return Variant::VECTOR3;
case Variant::PACKED_COLOR_ARRAY:
return Variant::COLOR;
+ case Variant::PACKED_VECTOR4_ARRAY:
+ return Variant::VECTOR4;
default:
return Variant::NIL;
}