From b50c88b590894246e9922b6f860dda248e8545e1 Mon Sep 17 00:00:00 2001 From: Richard Menzies Date: Wed, 18 Sep 2024 23:05:24 +0100 Subject: [PATCH] Fix dictionary variable matching dictionary --- modules/gdscript/gdscript_analyzer.cpp | 203 +++++++++++++++---------- 1 file changed, 124 insertions(+), 79 deletions(-) diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index ff07353f761..be78e341c64 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2561,100 +2561,145 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc } if (p_match_pattern->pattern_type == GDScriptParser::PatternNode::PT_DICTIONARY) { - GDScriptParser::DictionaryNode *dict_node_match = dynamic_cast(p_match_test); - bool open_ended = false; + if (p_match_test->type == GDScriptParser::Node::Type::IDENTIFIER) { + Pair other_type_found; + bool incorrect_type_present = false; + bool incorrect_key_present = false; + bool has_value = false; - if (p_match_pattern->dictionary.size() == 0) { - break; - } + GDScriptParser::DataType correct_type_key; + GDScriptParser::DataType correct_type_value; - // Create vectors of the types present in the pattern and the matching expression - List> match_vector; - List> pattern_vector; - List pattern_vector_keys; - for (int i = 0; i < p_match_pattern->dictionary.size(); i++) { - if (p_match_pattern->dictionary[i].value_pattern && p_match_pattern->dictionary[i].value_pattern->pattern_type == GDScriptParser::PatternNode::PT_REST) { - open_ended = true; - continue; - } - - if (p_match_pattern->dictionary[i].value_pattern) { - pattern_vector.push_front(Pair(p_match_pattern->dictionary[i].key->datatype, p_match_pattern->dictionary[i].value_pattern->datatype)); + if (p_match_test->get_datatype().container_element_types.size() == 2) { + // Both dict key and value specified + correct_type_key = p_match_test->get_datatype().container_element_types[0]; + correct_type_value = p_match_test->get_datatype().container_element_types[1]; + has_value = true; } else { - pattern_vector_keys.push_front(p_match_pattern->dictionary[i].key->datatype); + break; // dictionary has no type } - } - - // Check sizes - if ((!open_ended && dict_node_match->elements.size() != p_match_pattern->dictionary.size()) || (open_ended && dict_node_match->elements.size() < p_match_pattern->dictionary.size() - 1)) { - parser->push_warning(p_match_pattern, GDScriptWarning::MISMATCHED_TYPE, Variant::get_type_name(Variant::DICTIONARY), p_match_test->get_datatype().to_string(), "size", String::num(p_match_pattern->dictionary.size() - (open_ended ? 1 : 0)), String::num(dict_node_match->elements.size())); - break; - } - - // Create match vector - for (int i = 0; i < dict_node_match->elements.size(); i++) { - match_vector.push_front(Pair(dict_node_match->elements[i].key ? dict_node_match->elements[i].key->datatype : GDScriptParser::DataType(), dict_node_match->elements[i].value ? dict_node_match->elements[i].value->datatype : GDScriptParser::DataType())); - } - - // Match keys with values within pattern - int vec_size = pattern_vector.size(); - List>::Element *pattern_to_match = pattern_vector.front(); - for (int i = 0; i < vec_size; i++) { - List>::Element *j = match_vector.find(pattern_to_match->get()); - - if (j != nullptr) { - j->erase(); - List>::Element *temp = pattern_to_match->next(); - pattern_to_match->erase(); - pattern_to_match = temp; - - if (match_vector.is_empty() || pattern_vector.is_empty()) { - break; + for (int i = 0; i < p_match_pattern->dictionary.size(); i++) { + // Don't count the ".." symbol + if (p_match_pattern->dictionary[i].value_pattern && p_match_pattern->dictionary[i].value_pattern->pattern_type == GDScriptParser::PatternNode::PT_REST) { + continue; + } + + if (p_match_pattern->dictionary[i].value_pattern) { + // Test key and value type, key and value given + if (p_match_pattern->dictionary[i].key->datatype != correct_type_key || (has_value && p_match_pattern->dictionary[i].value_pattern->datatype != correct_type_value)) { + incorrect_type_present = true; + other_type_found = Pair(p_match_pattern->dictionary[i].key->datatype, p_match_pattern->dictionary[i].value_pattern->datatype); + break; + } + } else { + // Only test key type, value is not given + if (p_match_pattern->dictionary[i].key->datatype != correct_type_key) { + incorrect_key_present = true; + other_type_found = Pair(p_match_pattern->dictionary[i].key->datatype, GDScriptParser::DataType()); + } } - } else { - pattern_to_match = pattern_to_match->next(); } - if (pattern_to_match == nullptr) { + + if (incorrect_type_present || incorrect_key_present) { + parser->push_warning(p_match_pattern, GDScriptWarning::MISMATCHED_TYPE, Variant::get_type_name(Variant::Type::DICTIONARY), p_match_test->get_datatype().to_string(), "dict_typed", "[" + other_type_found.first.to_string() + (incorrect_key_present ? "" : ", " + other_type_found.second.to_string()) + "]", ""); + } + break; + } else if (p_match_test->type == GDScriptParser::Node::Type::DICTIONARY) { + GDScriptParser::DictionaryNode *dict_node_match = dynamic_cast(p_match_test); + bool open_ended = false; + + if (p_match_pattern->dictionary.size() == 0) { break; } - } - // Match keys without values within pattern - List::Element *pattern_keys_to_match = pattern_vector_keys.front(); - bool found = false; - vec_size = pattern_vector_keys.size(); - for (int i = 0; i < vec_size; i++) { - found = false; - GDScriptParser::DataType pattern_to_match_dt = pattern_keys_to_match->get(); - for (List>::Element *elem_match = match_vector.front(); elem_match != nullptr; elem_match = elem_match->next()) { - if (elem_match->get().first == pattern_to_match_dt) { - elem_match->erase(); - List::Element *temp = pattern_keys_to_match->next(); - pattern_keys_to_match->erase(); - pattern_keys_to_match = temp; + // Create vectors of the types present in the pattern and the matching expression + List> match_vector; + List> pattern_vector; + List pattern_vector_keys; + for (int i = 0; i < p_match_pattern->dictionary.size(); i++) { + if (p_match_pattern->dictionary[i].value_pattern && p_match_pattern->dictionary[i].value_pattern->pattern_type == GDScriptParser::PatternNode::PT_REST) { + open_ended = true; + continue; + } - found = true; + if (p_match_pattern->dictionary[i].value_pattern) { + pattern_vector.push_front(Pair(p_match_pattern->dictionary[i].key->datatype, p_match_pattern->dictionary[i].value_pattern->datatype)); + } else { + pattern_vector_keys.push_front(p_match_pattern->dictionary[i].key->datatype); + } + } + + // Check sizes + if ((!open_ended && dict_node_match->elements.size() != p_match_pattern->dictionary.size()) || (open_ended && dict_node_match->elements.size() < p_match_pattern->dictionary.size() - 1)) { + parser->push_warning(p_match_pattern, GDScriptWarning::MISMATCHED_TYPE, Variant::get_type_name(Variant::DICTIONARY), p_match_test->get_datatype().to_string(), "size", String::num(p_match_pattern->dictionary.size() - (open_ended ? 1 : 0)), String::num(dict_node_match->elements.size())); + break; + } + + // Create match vector + for (int i = 0; i < dict_node_match->elements.size(); i++) { + match_vector.push_front(Pair(dict_node_match->elements[i].key ? dict_node_match->elements[i].key->datatype : GDScriptParser::DataType(), dict_node_match->elements[i].value ? dict_node_match->elements[i].value->datatype : GDScriptParser::DataType())); + } + + // Match keys with values within pattern + int vec_size = pattern_vector.size(); + List>::Element *pattern_to_match = pattern_vector.front(); + for (int i = 0; i < vec_size; i++) { + List>::Element *j = match_vector.find(pattern_to_match->get()); + + if (j != nullptr) { + j->erase(); + List>::Element *temp = pattern_to_match->next(); + pattern_to_match->erase(); + pattern_to_match = temp; + + if (match_vector.is_empty() || pattern_vector.is_empty()) { + break; + } + } else { + pattern_to_match = pattern_to_match->next(); + } + if (pattern_to_match == nullptr) { break; } } - if (!found) { - pattern_keys_to_match = pattern_keys_to_match->next(); - } - if (pattern_keys_to_match == nullptr || match_vector.is_empty() || pattern_vector_keys.is_empty()) { - break; - } - } - if (pattern_vector.is_empty() && pattern_vector_keys.is_empty() && open_ended) { - break; // All pattern types are within the match vector - } - if (!match_vector.is_empty()) { - // Anything remaining in the match vector is not present in the pattern vector, the match cannot succeed - String missing_values; - for (List>::Element *elem = match_vector.front(); elem != nullptr; elem = elem->next()) { - missing_values += "[" + elem->get().first.to_string() + ", " + elem->get().second.to_string() + "]"; + // Match keys without values within pattern + List::Element *pattern_keys_to_match = pattern_vector_keys.front(); + bool found = false; + vec_size = pattern_vector_keys.size(); + for (int i = 0; i < vec_size; i++) { + found = false; + GDScriptParser::DataType pattern_to_match_dt = pattern_keys_to_match->get(); + for (List>::Element *elem_match = match_vector.front(); elem_match != nullptr; elem_match = elem_match->next()) { + if (elem_match->get().first == pattern_to_match_dt) { + elem_match->erase(); + List::Element *temp = pattern_keys_to_match->next(); + pattern_keys_to_match->erase(); + pattern_keys_to_match = temp; + + found = true; + break; + } + } + if (!found) { + pattern_keys_to_match = pattern_keys_to_match->next(); + } + if (pattern_keys_to_match == nullptr || match_vector.is_empty() || pattern_vector_keys.is_empty()) { + break; + } + } + + if (pattern_vector.is_empty() && pattern_vector_keys.is_empty() && open_ended) { + break; // All pattern types are within the match vector + } + if (!match_vector.is_empty()) { + // Anything remaining in the match vector is not present in the pattern vector, the match cannot succeed + String missing_values; + for (List>::Element *elem = match_vector.front(); elem != nullptr; elem = elem->next()) { + missing_values += "[" + elem->get().first.to_string() + ", " + elem->get().second.to_string() + "]"; + } + parser->push_warning(p_match_pattern, GDScriptWarning::MISMATCHED_TYPE, Variant::get_type_name(Variant::Type::DICTIONARY), p_match_test->get_datatype().to_string(), "dict", missing_values, ""); } - parser->push_warning(p_match_pattern, GDScriptWarning::MISMATCHED_TYPE, Variant::get_type_name(Variant::Type::DICTIONARY), p_match_test->get_datatype().to_string(), "dict", missing_values, ""); } } break;