Преглед на файлове

Merge pull request #102218 from HolonProduction/dictionary-recovery

GDScript: Do phrase level recovery when parsing faulty dictionaries
Thaddeus Crews преди 4 месеца
родител
ревизия
bef5d1e4f8
променени са 30 файла, в които са добавени 177 реда и са изтрити 4 реда
  1. 15 4
      modules/gdscript/gdscript_parser.cpp
  2. 12 0
      modules/gdscript/gdscript_parser.h
  3. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.cfg
  4. 6 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.gd
  5. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.cfg
  6. 10 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.gd
  7. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.cfg
  8. 5 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.gd
  9. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.cfg
  10. 9 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.gd
  11. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.cfg
  12. 6 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.gd
  13. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.cfg
  14. 9 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.gd
  15. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.cfg
  16. 6 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.gd
  17. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.cfg
  18. 5 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.gd
  19. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.cfg
  20. 5 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.gd
  21. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.cfg
  22. 9 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.gd
  23. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.cfg
  24. 5 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.gd
  25. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.cfg
  26. 5 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.gd
  27. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.cfg
  28. 9 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.gd
  29. 4 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.cfg
  30. 5 0
      modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.gd

+ 15 - 4
modules/gdscript/gdscript_parser.cpp

@@ -3122,13 +3122,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<LiteralNode *>(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)) {
@@ -3168,6 +3164,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<LiteralNode>();
+				dummy->value = Variant();
+
+				dictionary->elements.push_back({ key, dummy });
+			} else if (key == nullptr && value != nullptr) {
+				LiteralNode *dummy = alloc_recovery_node<LiteralNode>();
+				dummy->value = Variant();
+
+				dictionary->elements.push_back({ dummy, value });
+			}
+
 		} while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end());
 	}
 	pop_multiline();

+ 12 - 0
modules/gdscript/gdscript_parser.h

@@ -1451,6 +1451,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 <typename T>
+	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

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.cfg

@@ -0,0 +1,4 @@
+[output]
+exclude=[
+    {"display": "AUTO_TRANSLATE_MODE_INHERIT"},
+]

+ 6 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_1.gd

@@ -0,0 +1,6 @@
+extends Node
+
+var test = {
+    t = 1,
+    AutoTranslateMode.➡
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_key_2.cfg

@@ -0,0 +1,4 @@
+[output]
+exclude=[
+    {"display": "VALUE"},
+]

+ 10 - 0
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,
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "AUTO_TRANSLATE_MODE_INHERIT"},
+]

+ 5 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_1.gd

@@ -0,0 +1,5 @@
+extends Node
+
+var test = {
+    t = AutoTranslateMode.➡
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_2.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "VALUE"},
+]

+ 9 - 0
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.➡
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_3.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "AUTO_TRANSLATE_MODE_INHERIT"},
+]

+ 6 - 0
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.➡,
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_4.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "VALUE"},
+]

+ 9 - 0
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.➡
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/lua_value_5.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "AUTO_TRANSLATE_MODE_INHERIT"},
+]

+ 6 - 0
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.➡,
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "AUTO_TRANSLATE_MODE_INHERIT"},
+]

+ 5 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_1.gd

@@ -0,0 +1,5 @@
+extends Node
+
+var test = {
+    AutoTranslateMode.➡
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "AUTO_TRANSLATE_MODE_INHERIT"},
+]

+ 5 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_2.gd

@@ -0,0 +1,5 @@
+extends Node
+
+var test = {
+    AutoTranslateMode.➡:
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_3.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "VALUE"},
+]

+ 9 - 0
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"
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "AUTO_TRANSLATE_MODE_INHERIT"},
+]

+ 5 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_key_4.gd

@@ -0,0 +1,5 @@
+extends Node
+
+var test = {
+    AutoTranslateMode.➡: 1
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "AUTO_TRANSLATE_MODE_INHERIT"},
+]

+ 5 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_1.gd

@@ -0,0 +1,5 @@
+extends Node
+
+var test = {
+    1: AutoTranslateMode.➡
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_2.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "VALUE"},
+]

+ 9 - 0
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.➡
+}

+ 4 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.cfg

@@ -0,0 +1,4 @@
+[output]
+include=[
+    {"display": "AUTO_TRANSLATE_MODE_INHERIT"},
+]

+ 5 - 0
modules/gdscript/tests/scripts/completion/enum_values_in_dictionary/py_value_3.gd

@@ -0,0 +1,5 @@
+extends Node
+
+var test = {
+    1 AutoTranslateMode.➡
+}