summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp11
-rw-r--r--modules/gdscript/gdscript_compiler.cpp23
-rw-r--r--modules/gdscript/gdscript_editor.cpp28
-rw-r--r--modules/gdscript/gdscript_parser.cpp4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd13
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out5
6 files changed, 62 insertions, 22 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index c35af9ca5b..ac031baa8c 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1035,7 +1035,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
return_type.is_meta_type = false;
p_function->set_datatype(return_type);
if (p_function->return_type) {
- push_error("Constructor cannot have an explicit return type.", p_function->return_type);
+ GDScriptParser::DataType declared_return = resolve_datatype(p_function->return_type);
+ if (declared_return.kind != GDScriptParser::DataType::BUILTIN || declared_return.builtin_type != Variant::NIL) {
+ push_error("Constructor cannot have an explicit return type.", p_function->return_type);
+ }
}
} else {
GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type);
@@ -1198,7 +1201,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
variable_type.kind = GDScriptParser::DataType::BUILTIN;
variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else?
p_for->variable->set_datatype(variable_type);
- } else {
+ } else if (p_for->list) {
resolve_node(p_for->list);
if (p_for->list->datatype.has_container_element_type()) {
variable_type = p_for->list->datatype.get_container_element_type();
@@ -1213,7 +1216,9 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
variable_type.kind = GDScriptParser::DataType::VARIANT;
}
}
- p_for->variable->set_datatype(variable_type);
+ if (p_for->variable) {
+ p_for->variable->set_datatype(variable_type);
+ }
resolve_suite(p_for->loop);
p_for->set_datatype(p_for->loop->get_datatype());
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 2c02291795..9eee0b57f3 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1019,25 +1019,32 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
} else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) {
// Assignment to member property.
- GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
+ GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value);
if (r_error) {
return GDScriptCodeGenerator::Address();
}
- GDScriptCodeGenerator::Address assign_temp = assigned;
+
+ GDScriptCodeGenerator::Address to_assign = assigned_value;
+ bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE;
StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ if (has_operation) {
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary();
GDScriptCodeGenerator::Address member = codegen.add_temporary();
gen->write_get_member(member, name);
- gen->write_binary_operator(assigned, assignment->variant_op, member, assigned);
- gen->pop_temporary();
+ gen->write_binary_operator(op_result, assignment->variant_op, member, assigned_value);
+ gen->pop_temporary(); // Pop member temp.
+ to_assign = op_result;
}
- gen->write_set_member(assigned, name);
+ gen->write_set_member(to_assign, name);
- if (assign_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop the assigned expression or the temp result if it has operation.
+ }
+ if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop the assigned expression if not done before.
}
} else {
// Regular assignment.
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 83805f626a..e49bf518a2 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2222,8 +2222,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (obj) {
List<String> options;
obj->get_argument_options(p_method, p_argidx, &options);
- for (const String &F : options) {
- ScriptCodeCompletionOption option(F, ScriptCodeCompletionOption::KIND_FUNCTION);
+ for (String &opt : options) {
+ if (opt.is_quoted()) {
+ opt = opt.unquote().quote(quote_style); // Handle user preference.
+ }
+ ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_FUNCTION);
r_result.insert(option.display, option);
}
}
@@ -2643,23 +2646,26 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
} break;
case GDScriptParser::COMPLETION_GET_NODE: {
+ // Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically.
if (p_owner) {
List<String> opts;
p_owner->get_argument_options("get_node", 0, &opts);
for (const String &E : opts) {
+ r_forced = true;
String opt = E.strip_edges();
if (opt.is_quoted()) {
- r_forced = true;
- String idopt = opt.unquote();
- if (idopt.replace("/", "_").is_valid_identifier()) {
- ScriptCodeCompletionOption option(idopt, ScriptCodeCompletionOption::KIND_NODE_PATH);
- options.insert(option.display, option);
- } else {
- ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH);
- options.insert(option.display, option);
- }
+ // Remove quotes so that we can handle user preferred quote style,
+ // or handle NodePaths which are valid identifiers and don't need quotes.
+ opt = opt.unquote();
}
+ // The path needs quotes if it's not a valid identifier (with an exception
+ // for "/" as path separator, which also doesn't require quotes).
+ if (!opt.replace("/", "_").is_valid_identifier()) {
+ opt = opt.quote(quote_style); // Handle user preference.
+ }
+ ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH);
+ options.insert(option.display, option);
}
// Get autoloads.
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 63817e970a..ad75e8174c 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -1620,6 +1620,10 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
n_for->list = parse_expression(false);
+ if (!n_for->list) {
+ push_error(R"(Expected a list or range after "in".)");
+ }
+
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "for" condition.)");
// Save break/continue state.
diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd
new file mode 100644
index 0000000000..f6526aefb4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd
@@ -0,0 +1,13 @@
+extends Node
+
+func test():
+ process_priority = 10
+ var change = 20
+
+ print(process_priority)
+ print(change)
+
+ process_priority += change
+
+ print(process_priority)
+ print(change)
diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out
new file mode 100644
index 0000000000..c9e6b34c77
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+10
+20
+30
+20