From ca1e444bca01545ffed97e8786de5b30a9ace01e Mon Sep 17 00:00:00 2001 From: HolonProduction Date: Fri, 31 Jan 2025 00:04:01 +0100 Subject: [PATCH] GDScript: Do phrase level recovery when parsing faulty dictionaries --- modules/gdscript/gdscript_parser.cpp | 19 +++++++++++++++---- modules/gdscript/gdscript_parser.h | 12 ++++++++++++ .../enum_values_in_dictionary/lua_key_1.cfg | 4 ++++ .../enum_values_in_dictionary/lua_key_1.gd | 6 ++++++ .../enum_values_in_dictionary/lua_key_2.cfg | 4 ++++ .../enum_values_in_dictionary/lua_key_2.gd | 10 ++++++++++ .../enum_values_in_dictionary/lua_value_1.cfg | 4 ++++ .../enum_values_in_dictionary/lua_value_1.gd | 5 +++++ .../enum_values_in_dictionary/lua_value_2.cfg | 4 ++++ .../enum_values_in_dictionary/lua_value_2.gd | 9 +++++++++ .../enum_values_in_dictionary/lua_value_3.cfg | 4 ++++ .../enum_values_in_dictionary/lua_value_3.gd | 6 ++++++ .../enum_values_in_dictionary/lua_value_4.cfg | 4 ++++ .../enum_values_in_dictionary/lua_value_4.gd | 9 +++++++++ .../enum_values_in_dictionary/lua_value_5.cfg | 4 ++++ .../enum_values_in_dictionary/lua_value_5.gd | 6 ++++++ .../enum_values_in_dictionary/py_key_1.cfg | 4 ++++ .../enum_values_in_dictionary/py_key_1.gd | 5 +++++ .../enum_values_in_dictionary/py_key_2.cfg | 4 ++++ .../enum_values_in_dictionary/py_key_2.gd | 5 +++++ .../enum_values_in_dictionary/py_key_3.cfg | 4 ++++ .../enum_values_in_dictionary/py_key_3.gd | 9 +++++++++ .../enum_values_in_dictionary/py_key_4.cfg | 4 ++++ .../enum_values_in_dictionary/py_key_4.gd | 5 +++++ .../enum_values_in_dictionary/py_value_1.cfg | 4 ++++ .../enum_values_in_dictionary/py_value_1.gd | 5 +++++ .../enum_values_in_dictionary/py_value_2.cfg | 4 ++++ .../enum_values_in_dictionary/py_value_2.gd | 9 +++++++++ .../enum_values_in_dictionary/py_value_3.cfg | 4 ++++ .../enum_values_in_dictionary/py_value_3.gd | 5 +++++ 30 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.gd create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.cfg create mode 100644 modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.gd diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 077ab6f0466..4b95f5a8361 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3123,13 +3123,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode case DictionaryNode::LUA_TABLE: if (key != nullptr && key->type != Node::IDENTIFIER && key->type != Node::LITERAL) { push_error(R"(Expected identifier or string as Lua-style dictionary key (e.g "{ key = value }").)"); - advance(); - break; } if (key != nullptr && key->type == Node::LITERAL && static_cast(key)->value.get_type() != Variant::STRING) { push_error(R"(Expected identifier or string as Lua-style dictionary key (e.g "{ key = value }").)"); - advance(); - break; } if (!match(GDScriptTokenizer::Token::EQUAL)) { if (match(GDScriptTokenizer::Token::COLON)) { @@ -3169,6 +3165,21 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode if (key != nullptr && value != nullptr) { dictionary->elements.push_back({ key, value }); } + + // Do phrase level recovery by inserting an imaginary expression for missing keys or values. + // This ensures the successfully parsed expression is part of the AST and can be analyzed. + if (key != nullptr && value == nullptr) { + LiteralNode *dummy = alloc_recovery_node(); + dummy->value = Variant(); + + dictionary->elements.push_back({ key, dummy }); + } else if (key == nullptr && value != nullptr) { + LiteralNode *dummy = alloc_recovery_node(); + dummy->value = Variant(); + + dictionary->elements.push_back({ dummy, value }); + } + } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end()); } pop_multiline(); diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index 5da2bc80207..8afc6b5f05a 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1453,6 +1453,18 @@ private: return node; } + + // Allocates a node for patching up the parse tree when an error occurred. + // Such nodes don't track their extents as they don't relate to actual tokens. + template + T *alloc_recovery_node() { + T *node = memnew(T); + node->next = list; + list = node; + + return node; + } + void clear(); void push_error(const String &p_message, const Node *p_origin = nullptr); #ifdef DEBUG_ENABLED diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.cfg new file mode 100644 index 00000000000..8920916a898 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.cfg @@ -0,0 +1,4 @@ +[output] +exclude=[ + {"display": "AUTO_TRANSLATE_MODE_INHERIT"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.gd new file mode 100644 index 00000000000..7795c0636d4 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.gd @@ -0,0 +1,6 @@ +extends Node + +var test = { + t = 1, + AutoTranslateMode.➡ +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.cfg new file mode 100644 index 00000000000..689a99b70c6 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.cfg @@ -0,0 +1,4 @@ +[output] +exclude=[ + {"display": "VALUE"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.gd new file mode 100644 index 00000000000..9ba440a10db --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.gd @@ -0,0 +1,10 @@ +extends Node + +enum TestEnum { + VALUE, +} + +var test = { + t = 1, + TestEnum.➡ = 1, +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.cfg new file mode 100644 index 00000000000..e464da23f81 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "AUTO_TRANSLATE_MODE_INHERIT"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.gd new file mode 100644 index 00000000000..d5f087b3bd6 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.gd @@ -0,0 +1,5 @@ +extends Node + +var test = { + t = AutoTranslateMode.➡ +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.cfg new file mode 100644 index 00000000000..5e69ae24f97 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "VALUE"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.gd new file mode 100644 index 00000000000..9eb72aed30b --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.gd @@ -0,0 +1,9 @@ +extends Node + +enum TestEnum { + VALUE, +} + +var test = { + = TestEnum.➡ +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.cfg new file mode 100644 index 00000000000..e464da23f81 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "AUTO_TRANSLATE_MODE_INHERIT"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.gd new file mode 100644 index 00000000000..394aa28b631 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.gd @@ -0,0 +1,6 @@ +extends Node + +var test = { + t = 1, + e AutoTranslateMode.➡, +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.cfg new file mode 100644 index 00000000000..5e69ae24f97 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "VALUE"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.gd new file mode 100644 index 00000000000..17fc79b52d1 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.gd @@ -0,0 +1,9 @@ +extends Node + +enum TestEnum { + VALUE, +} + +var test = { + 1 = TestEnum.➡ +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.cfg new file mode 100644 index 00000000000..e464da23f81 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "AUTO_TRANSLATE_MODE_INHERIT"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.gd new file mode 100644 index 00000000000..aa6c52ae807 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.gd @@ -0,0 +1,6 @@ +extends Node + +var test = { + t = 1, + 1 AutoTranslateMode.➡, +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.cfg new file mode 100644 index 00000000000..e464da23f81 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "AUTO_TRANSLATE_MODE_INHERIT"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.gd new file mode 100644 index 00000000000..ad3009532b5 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.gd @@ -0,0 +1,5 @@ +extends Node + +var test = { + AutoTranslateMode.➡ +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.cfg new file mode 100644 index 00000000000..e464da23f81 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "AUTO_TRANSLATE_MODE_INHERIT"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.gd new file mode 100644 index 00000000000..d5a50261cc5 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.gd @@ -0,0 +1,5 @@ +extends Node + +var test = { + AutoTranslateMode.➡: +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.cfg new file mode 100644 index 00000000000..5e69ae24f97 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "VALUE"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.gd new file mode 100644 index 00000000000..a2b6fa0106e --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.gd @@ -0,0 +1,9 @@ +extends Node + +enum TestEnum { + VALUE, +} + +var test = { + TestEnum.➡ "test" +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.cfg new file mode 100644 index 00000000000..e464da23f81 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "AUTO_TRANSLATE_MODE_INHERIT"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.gd new file mode 100644 index 00000000000..ac7b3f8614c --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.gd @@ -0,0 +1,5 @@ +extends Node + +var test = { + AutoTranslateMode.➡: 1 +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.cfg new file mode 100644 index 00000000000..e464da23f81 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "AUTO_TRANSLATE_MODE_INHERIT"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.gd new file mode 100644 index 00000000000..5ea4cab76fc --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.gd @@ -0,0 +1,5 @@ +extends Node + +var test = { + 1: AutoTranslateMode.➡ +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.cfg new file mode 100644 index 00000000000..5e69ae24f97 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "VALUE"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.gd new file mode 100644 index 00000000000..fd14b2152dd --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.gd @@ -0,0 +1,9 @@ +extends Node + +enum TestEnum { + VALUE, +} + +var test = { + : TestEnum.➡ +} diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.cfg b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.cfg new file mode 100644 index 00000000000..e464da23f81 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.cfg @@ -0,0 +1,4 @@ +[output] +include=[ + {"display": "AUTO_TRANSLATE_MODE_INHERIT"}, +] diff --git a/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.gd b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.gd new file mode 100644 index 00000000000..befd4cc0cce --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.gd @@ -0,0 +1,5 @@ +extends Node + +var test = { + 1 AutoTranslateMode.➡ +}