mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-12-22 03:46:50 +01:00
Check duplicate keys in dictionary literals: enums and const variables
Check identifiers (const variables and unnamed enums) and named enums when parsing dictionary literals whether the keys are not duplicated. In case of duplicate key is encountered, highlight the line with it and print error message: `Duplicate key "foo" found in Dictionary literal` This commit is a logical continuation of the commit dab73c7 which implemented such checks only for literal keys (which fixed #7034). Apart from that, this commit also fixes the issue with the error message itself, which was shown one line below the duplicated key in case it was the last one in the dictionary literal and there was no hanging comma. Also, the format of the error message has been changed so that now the error message also contains the value of the key which is duplicated. Instead of `Duplicate key found in Dictionary literal`, it now prints `Duplicate key "<value>" found in Dictionary literal` Fixes #50971
This commit is contained in:
parent
4496c11437
commit
1a231787b0
@ -1093,14 +1093,15 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
|
||||
}
|
||||
expecting = DICT_EXPECT_COMMA;
|
||||
|
||||
if (key->type == GDScriptParser::Node::TYPE_CONSTANT) {
|
||||
Variant const &keyName = static_cast<const GDScriptParser::ConstantNode *>(key)->value;
|
||||
|
||||
if (keys.has(keyName)) {
|
||||
_set_error("Duplicate key found in Dictionary literal");
|
||||
const Variant *key_value = _try_to_find_constant_value_for_expression(key);
|
||||
if (key_value) {
|
||||
if (keys.has(*key_value)) {
|
||||
_set_error("Duplicate key \"" + String(*key_value) + "\" found in Dictionary literal",
|
||||
key->line,
|
||||
key->column);
|
||||
return nullptr;
|
||||
}
|
||||
keys.insert(keyName);
|
||||
keys.insert(*key_value);
|
||||
}
|
||||
|
||||
DictionaryNode::Pair pair;
|
||||
@ -2146,6 +2147,49 @@ bool GDScriptParser::_reduce_export_var_type(Variant &p_value, int p_line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Variant *GDScriptParser::_try_to_find_constant_value_for_expression(const Node *p_expr) const {
|
||||
if (p_expr->type == Node::TYPE_CONSTANT) {
|
||||
return &(static_cast<const ConstantNode *>(p_expr)->value);
|
||||
} else if (p_expr->type == Node::TYPE_IDENTIFIER) {
|
||||
const StringName &name = static_cast<const IdentifierNode *>(p_expr)->name;
|
||||
const Map<StringName, ClassNode::Constant>::Element *element =
|
||||
current_class->constant_expressions.find(name);
|
||||
if (element) {
|
||||
Node *cn_exp = element->value().expression;
|
||||
if (cn_exp->type == Node::TYPE_CONSTANT) {
|
||||
return &(static_cast<ConstantNode *>(cn_exp)->value);
|
||||
}
|
||||
}
|
||||
} else if (p_expr->type == Node::TYPE_OPERATOR) {
|
||||
// Check if expression `p_expr` is a named enum (e.g. `State.IDLE`).
|
||||
const OperatorNode *op_node = static_cast<const OperatorNode *>(p_expr);
|
||||
if (op_node->op == GDScriptParser::OperatorNode::OP_INDEX_NAMED) {
|
||||
const Vector<Node *> &op_args = op_node->arguments;
|
||||
if (op_args.size() < 2) {
|
||||
return nullptr; // Invalid expression.
|
||||
}
|
||||
|
||||
if (op_args[0]->type != Node::TYPE_IDENTIFIER || op_args[1]->type != Node::TYPE_IDENTIFIER) {
|
||||
return nullptr; // Not an enum expression.
|
||||
}
|
||||
|
||||
const StringName &enum_name = static_cast<const IdentifierNode *>(op_args[0])->name;
|
||||
const StringName &const_name = static_cast<const IdentifierNode *>(op_args[1])->name;
|
||||
Map<StringName, ClassNode::Constant>::Element *element =
|
||||
current_class->constant_expressions.find(enum_name);
|
||||
if (element) {
|
||||
Node *cn_exp = element->value().expression;
|
||||
if (cn_exp->type == Node::TYPE_CONSTANT) {
|
||||
const Dictionary &enum_dict = static_cast<ConstantNode *>(cn_exp)->value;
|
||||
return enum_dict.getptr(const_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool GDScriptParser::_recover_from_completion() {
|
||||
if (!completion_found) {
|
||||
return false; //can't recover if no completion
|
||||
|
@ -600,6 +600,7 @@ private:
|
||||
Node *_reduce_expression(Node *p_node, bool p_to_const = false);
|
||||
Node *_parse_and_reduce_expression(Node *p_parent, bool p_static, bool p_reduce_const = false, bool p_allow_assign = false);
|
||||
bool _reduce_export_var_type(Variant &p_value, int p_line = 0);
|
||||
const Variant *_try_to_find_constant_value_for_expression(const Node *p_expr) const;
|
||||
|
||||
PatternNode *_parse_pattern(bool p_static);
|
||||
void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static);
|
||||
|
Loading…
Reference in New Issue
Block a user