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
02c02403e0
commit
52507443d3
2 changed files with 51 additions and 6 deletions
|
@ -1127,14 +1127,15 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
|
||||||
}
|
}
|
||||||
expecting = DICT_EXPECT_COMMA;
|
expecting = DICT_EXPECT_COMMA;
|
||||||
|
|
||||||
if (key->type == GDScriptParser::Node::TYPE_CONSTANT) {
|
const Variant *key_value = _try_to_find_constant_value_for_expression(key);
|
||||||
Variant const &keyName = static_cast<const GDScriptParser::ConstantNode *>(key)->value;
|
if (key_value) {
|
||||||
|
if (keys.has(*key_value)) {
|
||||||
if (keys.has(keyName)) {
|
_set_error("Duplicate key \"" + String(*key_value) + "\" found in Dictionary literal",
|
||||||
_set_error("Duplicate key found in Dictionary literal");
|
key->line,
|
||||||
|
key->column);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
keys.insert(keyName);
|
keys.insert(*key_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
DictionaryNode::Pair pair;
|
DictionaryNode::Pair pair;
|
||||||
|
@ -2180,6 +2181,49 @@ bool GDScriptParser::_reduce_export_var_type(Variant &p_value, int p_line) {
|
||||||
return false;
|
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() {
|
bool GDScriptParser::_recover_from_completion() {
|
||||||
if (!completion_found) {
|
if (!completion_found) {
|
||||||
return false; //can't recover if no completion
|
return false; //can't recover if no completion
|
||||||
|
|
|
@ -607,6 +607,7 @@ private:
|
||||||
Node *_reduce_expression(Node *p_node, bool p_to_const = false);
|
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);
|
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);
|
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);
|
PatternNode *_parse_pattern(bool p_static);
|
||||||
void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static);
|
void _parse_pattern_block(BlockNode *p_block, Vector<PatternBranchNode *> &p_branches, bool p_static);
|
||||||
|
|
Loading…
Reference in a new issue