diff options
Diffstat (limited to 'modules/gdscript/gdscript_parser.cpp')
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 252 |
1 files changed, 147 insertions, 105 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 6ef3ab67ae..d125da5b79 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -1868,6 +1868,10 @@ GDScriptParser::Node *GDScriptParser::_reduce_expression(Node *p_node, bool p_to _set_error("Can't assign to constant", tokenizer->get_token_line() - 1); error_line = op->line; return op; + } else if (op->arguments[0]->type == Node::TYPE_SELF) { + _set_error("Can't assign to self.", op->line); + error_line = op->line; + return op; } if (op->arguments[0]->type == Node::TYPE_OPERATOR) { @@ -2689,6 +2693,7 @@ void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) { op->op = OperatorNode::OP_ASSIGN; op->arguments.push_back(id2); op->arguments.push_back(local_var->assign); + local_var->assign_op = op; branch->body->statements.push_front(op); branch->body->statements.push_front(local_var); @@ -2866,7 +2871,6 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { assigned = _get_default_value_for_type(lv->datatype, var_line); } - lv->assign = assigned; //must be added later, to avoid self-referencing. p_block->variables.insert(n, lv); @@ -3349,7 +3353,12 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { } p_block->statements.push_back(expression); if (!_end_statement()) { - _set_error("Expected end of statement after expression."); + // Attempt to guess a better error message if the user "retypes" a variable + if (tokenizer->get_token() == GDScriptTokenizer::TK_COLON && tokenizer->get_token(1) == GDScriptTokenizer::TK_OP_ASSIGN) { + _set_error("Unexpected ':=', use '=' instead. Expected end of statement after expression."); + } else { + _set_error(String() + "Expected end of statement after expression, got " + tokenizer->get_token_name(tokenizer->get_token()) + " instead"); + } return; } @@ -4040,7 +4049,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) { - tokenizer->advance(); +#define _ADVANCE_AND_CONSUME_NEWLINES \ + do { \ + tokenizer->advance(); \ + } while (tokenizer->get_token() == GDScriptTokenizer::TK_NEWLINE) + + _ADVANCE_AND_CONSUME_NEWLINES; + parenthesis++; String hint_prefix = ""; bool is_arrayed = false; @@ -4070,11 +4085,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } current_export.type = type; current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { // hint expected next! - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; switch (type) { @@ -4082,7 +4097,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") { - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { WARN_DEPRECATED_MSG("Exporting bit flags hint requires string constants."); @@ -4094,7 +4109,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } current_export.hint = PROPERTY_HINT_FLAGS; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; bool first = true; while (true) { @@ -4113,7 +4128,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.hint_string += c.xml_escape(); - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) break; @@ -4122,7 +4137,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("Expected \")\" or \",\" in the named bit flags hint."); return; } - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } break; @@ -4130,7 +4145,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_RENDER") { - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected \")\" in the layers 2D render hint."); return; @@ -4141,7 +4156,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_2D_PHYSICS") { - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected \")\" in the layers 2D physics hint."); return; @@ -4152,7 +4167,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_RENDER") { - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected \")\" in the layers 3D render hint."); return; @@ -4163,7 +4178,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "LAYERS_3D_PHYSICS") { - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected \")\" in the layers 3D physics hint."); return; @@ -4193,7 +4208,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.hint_string += c.xml_escape(); - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) break; @@ -4203,7 +4218,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } break; @@ -4215,7 +4230,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "EASE") { current_export.hint = PROPERTY_HINT_EXP_EASING; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected \")\" in the hint."); return; @@ -4227,7 +4242,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "EXP") { current_export.hint = PROPERTY_HINT_EXP_RANGE; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) break; @@ -4235,7 +4250,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("Expected \")\" or \",\" in the exponential range hint."); return; } - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } else current_export.hint = PROPERTY_HINT_RANGE; @@ -4243,7 +4258,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_SUB) { sign = -1; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || !tokenizer->get_token_constant().is_num()) { @@ -4253,7 +4268,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } current_export.hint_string = rtos(sign * double(tokenizer->get_token_constant())); - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { current_export.hint_string = "0," + current_export.hint_string; @@ -4267,12 +4282,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; sign = 1.0; if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_SUB) { sign = -1; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || !tokenizer->get_token_constant().is_num()) { @@ -4283,7 +4298,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } current_export.hint_string += "," + rtos(sign * double(tokenizer->get_token_constant())); - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) break; @@ -4295,11 +4310,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; sign = 1.0; if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_SUB) { sign = -1; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } if (tokenizer->get_token() != GDScriptTokenizer::TK_CONSTANT || !tokenizer->get_token_constant().is_num()) { @@ -4310,7 +4325,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } current_export.hint_string += "," + rtos(sign * double(tokenizer->get_token_constant())); - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } break; case Variant::STRING: { @@ -4335,7 +4350,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { first = false; current_export.hint_string += c.xml_escape(); - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) break; @@ -4344,7 +4359,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("Expected \")\" or \",\" in the enumeration hint."); return; } - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } break; @@ -4352,13 +4367,13 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "DIR") { - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) current_export.hint = PROPERTY_HINT_DIR; else if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() != GDScriptTokenizer::TK_IDENTIFIER || !(tokenizer->get_token_identifier() == "GLOBAL")) { _set_error("Expected \"GLOBAL\" after comma in the directory hint."); @@ -4369,7 +4384,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } current_export.hint = PROPERTY_HINT_GLOBAL_DIR; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected \")\" in the hint."); @@ -4385,11 +4400,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FILE") { current_export.hint = PROPERTY_HINT_FILE; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "GLOBAL") { @@ -4398,12 +4413,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } current_export.hint = PROPERTY_HINT_GLOBAL_FILE; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_CLOSE) break; else if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; else { _set_error("Expected \")\" or \",\" in the hint."); return; @@ -4419,7 +4434,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } current_export.hint_string = tokenizer->get_token_constant(); - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { @@ -4432,7 +4447,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "MULTILINE") { current_export.hint = PROPERTY_HINT_MULTILINE_TEXT; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() != GDScriptTokenizer::TK_PARENTHESIS_CLOSE) { _set_error("Expected \")\" in the hint."); return; @@ -4459,7 +4474,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { _set_error("Color type hint expects RGB or RGBA as hints."); return; } - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } break; default: { @@ -4510,11 +4525,11 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { bool is_flags = false; if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) { - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; if (tokenizer->get_token() == GDScriptTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier() == "FLAGS") { is_flags = true; - tokenizer->advance(); + _ADVANCE_AND_CONSUME_NEWLINES; } else { current_export = PropertyInfo(); _set_error("Expected \"FLAGS\" after comma."); @@ -4558,6 +4573,9 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { return; } + tokenizer->advance(); + parenthesis--; + if (is_arrayed) { hint_prefix += itos(current_export.type); if (current_export.hint) { @@ -4567,8 +4585,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { current_export.hint = PROPERTY_HINT_TYPE_STRING; current_export.type = Variant::ARRAY; } - - tokenizer->advance(); +#undef _ADVANCE_AND_CONSUME_NEWLINES } if (tokenizer->get_token() != GDScriptTokenizer::TK_PR_VAR && tokenizer->get_token() != GDScriptTokenizer::TK_PR_ONREADY && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTE && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTER && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPET && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_REMOTESYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_MASTERSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_PUPPETSYNC && tokenizer->get_token() != GDScriptTokenizer::TK_PR_SLAVE) { @@ -4728,10 +4745,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { member.line = tokenizer->get_token_line(); member.usages = 0; member.rpc_mode = rpc_mode; -#ifdef TOOLS_ENABLED - Variant::CallError ce; - member.default_value = Variant::construct(member._export.type, NULL, 0, ce); -#endif if (current_class->constant_expressions.has(member.identifier)) { _set_error("A constant named \"" + String(member.identifier) + "\" already exists in this class (at line: " + @@ -4784,6 +4797,32 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } + if (autoexport && member.data_type.has_type) { + if (member.data_type.kind == DataType::BUILTIN) { + member._export.type = member.data_type.builtin_type; + } else if (member.data_type.kind == DataType::NATIVE) { + if (ClassDB::is_parent_class(member.data_type.native_type, "Resource")) { + member._export.type = Variant::OBJECT; + member._export.hint = PROPERTY_HINT_RESOURCE_TYPE; + member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; + member._export.hint_string = member.data_type.native_type; + member._export.class_name = member.data_type.native_type; + } else { + _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line); + return; + } + + } else { + _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line); + return; + } + } + +#ifdef TOOLS_ENABLED + Variant::CallError ce; + member.default_value = Variant::construct(member._export.type, NULL, 0, ce); +#endif + if (tokenizer->get_token() == GDScriptTokenizer::TK_OP_ASSIGN) { #ifdef DEBUG_ENABLED @@ -4917,27 +4956,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { member.initial_assignment = op; } - if (autoexport && member.data_type.has_type) { - if (member.data_type.kind == DataType::BUILTIN) { - member._export.type = member.data_type.builtin_type; - } else if (member.data_type.kind == DataType::NATIVE) { - if (ClassDB::is_parent_class(member.data_type.native_type, "Resource")) { - member._export.type = Variant::OBJECT; - member._export.hint = PROPERTY_HINT_RESOURCE_TYPE; - member._export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE; - member._export.hint_string = member.data_type.native_type; - member._export.class_name = member.data_type.native_type; - } else { - _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line); - return; - } - - } else { - _set_error("Invalid export type. Only built-in and native resource types can be exported.", member.line); - return; - } - } - if (tokenizer->get_token() == GDScriptTokenizer::TK_PR_SETGET) { tokenizer->advance(); @@ -5390,12 +5408,12 @@ void GDScriptParser::_determine_inheritance(ClassNode *p_class, bool p_recursive ident += ("." + subclass); - if (base_script->get_subclasses().has(subclass)) { + if (find_subclass->get_subclasses().has(subclass)) { - find_subclass = base_script->get_subclasses()[subclass]; - } else if (base_script->get_constants().has(subclass)) { + find_subclass = find_subclass->get_subclasses()[subclass]; + } else if (find_subclass->get_constants().has(subclass)) { - Ref<GDScript> new_base_class = base_script->get_constants()[subclass]; + Ref<GDScript> new_base_class = find_subclass->get_constants()[subclass]; if (new_base_class.is_null()) { _set_error("Constant isn't a class: " + ident, p_class->line); return; @@ -6276,6 +6294,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { node_type.has_type = true; node_type.kind = DataType::CLASS; node_type.class_type = current_class; + node_type.is_constant = true; } break; case Node::TYPE_IDENTIFIER: { IdentifierNode *id = static_cast<IdentifierNode *>(p_node); @@ -6952,6 +6971,17 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat if (error_set) return DataType(); + // Special case: check copy constructor. Those are defined implicitly in Variant. + if (par_types.size() == 1) { + if (!par_types[0].has_type || (par_types[0].kind == DataType::BUILTIN && par_types[0].builtin_type == tn->vtype)) { + DataType result; + result.has_type = true; + result.kind = DataType::BUILTIN; + result.builtin_type = tn->vtype; + return result; + } + } + bool match = false; List<MethodInfo> constructors; Variant::get_constructor_list(tn->vtype, &constructors); @@ -7022,12 +7052,10 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat return_type = _type_from_property(mi.return_val, false); -#ifdef DEBUG_ENABLED // Check all arguments beforehand to solve warnings for (int i = 1; i < p_call->arguments.size(); i++) { _reduce_node_type(p_call->arguments[i]); } -#endif // DEBUG_ENABLED // Check arguments @@ -7055,12 +7083,10 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat ERR_FAIL_V(DataType()); } -#ifdef DEBUG_ENABLED // Check all arguments beforehand to solve warnings for (int i = arg_id + 1; i < p_call->arguments.size(); i++) { _reduce_node_type(p_call->arguments[i]); } -#endif // DEBUG_ENABLED IdentifierNode *func_id = static_cast<IdentifierNode *>(p_call->arguments[arg_id]); callee_name = func_id->name; @@ -7624,6 +7650,11 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType void GDScriptParser::_check_class_level_types(ClassNode *p_class) { + // Names of internal object properties that we check to avoid overriding them. + // "__meta__" could also be in here, but since it doesn't really affect object metadata, + // it is okay to override it on script. + StringName script_name = CoreStringNames::get_singleton()->_script; + _mark_line_as_safe(p_class->line); // Constants @@ -7644,8 +7675,9 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { c.expression->set_datatype(expr); DataType tmp; - if (_get_member_type(p_class->base_type, E->key(), tmp)) { - _set_error("The member \"" + String(E->key()) + "\" already exists in a parent class.", c.expression->line); + const StringName &constant_name = E->key(); + if (constant_name == script_name || _get_member_type(p_class->base_type, constant_name, tmp)) { + _set_error("The member \"" + String(constant_name) + "\" already exists in a parent class.", c.expression->line); return; } } @@ -7666,7 +7698,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { ClassNode::Member &v = p_class->variables.write[i]; DataType tmp; - if (_get_member_type(p_class->base_type, v.identifier, tmp)) { + if (v.identifier == script_name || _get_member_type(p_class->base_type, v.identifier, tmp)) { _set_error("The member \"" + String(v.identifier) + "\" already exists in a parent class.", v.line); return; } @@ -7843,12 +7875,12 @@ void GDScriptParser::_check_function_types(FunctionNode *p_function) { def_type.is_constant = false; p_function->argument_types.write[i] = def_type; } else { - p_function->return_type = _resolve_type(p_function->return_type, p_function->line); + p_function->argument_types.write[i] = _resolve_type(p_function->argument_types[i], p_function->line); if (!_is_type_compatible(p_function->argument_types[i], def_type, true)) { String arg_name = p_function->arguments[i]; _set_error("Value type (" + def_type.to_string() + ") doesn't match the type of argument '" + - arg_name + "' (" + p_function->arguments[i] + ").", + arg_name + "' (" + p_function->argument_types[i].to_string() + ").", p_function->line); } } @@ -8034,6 +8066,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { last_var_assign = lv->assign; if (lv->assign) { + lv->assign_op->arguments[0]->set_datatype(lv->datatype); DataType assign_type = _reduce_node_type(lv->assign); #ifdef DEBUG_ENABLED if (assign_type.has_type && assign_type.kind == DataType::BUILTIN && assign_type.builtin_type == Variant::NIL) { @@ -8178,9 +8211,12 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { if (lh_type.has_type && rh_type.may_yield && op->arguments[1]->type == Node::TYPE_OPERATOR) { _add_warning(GDScriptWarning::FUNCTION_MAY_YIELD, op->line, _find_function_name(static_cast<OperatorNode *>(op->arguments[1]))); } -#endif // DEBUG_ENABLED +#endif // DEBUG_ENABLED + bool type_match = lh_type.has_type && rh_type.has_type; if (check_types && !_is_type_compatible(lh_type, rh_type)) { + type_match = false; + // Try supertype test if (_is_type_compatible(rh_type, lh_type)) { _mark_line_as_unsafe(op->line); @@ -8192,23 +8228,27 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { op->line); return; } - // Replace assignment with implicit conversion - BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); - convert->line = op->line; - convert->function = GDScriptFunctions::TYPE_CONVERT; - - ConstantNode *tgt_type = alloc_node<ConstantNode>(); - tgt_type->line = op->line; - tgt_type->value = (int)lh_type.builtin_type; - - OperatorNode *convert_call = alloc_node<OperatorNode>(); - convert_call->line = op->line; - convert_call->op = OperatorNode::OP_CALL; - convert_call->arguments.push_back(convert); - convert_call->arguments.push_back(op->arguments[1]); - convert_call->arguments.push_back(tgt_type); - - op->arguments.write[1] = convert_call; + if (op->op == OperatorNode::OP_ASSIGN) { + // Replace assignment with implicit conversion + BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); + convert->line = op->line; + convert->function = GDScriptFunctions::TYPE_CONVERT; + + ConstantNode *tgt_type = alloc_node<ConstantNode>(); + tgt_type->line = op->line; + tgt_type->value = (int)lh_type.builtin_type; + + OperatorNode *convert_call = alloc_node<OperatorNode>(); + convert_call->line = op->line; + convert_call->op = OperatorNode::OP_CALL; + convert_call->arguments.push_back(convert); + convert_call->arguments.push_back(op->arguments[1]); + convert_call->arguments.push_back(tgt_type); + + op->arguments.write[1] = convert_call; + + type_match = true; // Since we are converting, the type is matching + } #ifdef DEBUG_ENABLED if (lh_type.builtin_type == Variant::INT && rh_type.builtin_type == Variant::REAL) { _add_warning(GDScriptWarning::NARROWING_CONVERSION, op->line); @@ -8221,6 +8261,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { _mark_line_as_unsafe(op->line); } #endif // DEBUG_ENABLED + op->datatype.has_type = type_match; } break; case OperatorNode::OP_CALL: case OperatorNode::OP_PARENT_CALL: { @@ -8246,7 +8287,11 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { _mark_line_as_safe(op->line); _reduce_node_type(op); // Test for safety anyway #ifdef DEBUG_ENABLED - _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line); + if (op->op == OperatorNode::OP_TERNARY_IF) { + _add_warning(GDScriptWarning::STANDALONE_TERNARY, statement->line); + } else { + _add_warning(GDScriptWarning::STANDALONE_EXPRESSION, statement->line); + } #endif // DEBUG_ENABLED } } @@ -8463,11 +8508,8 @@ Error GDScriptParser::_parse(const String &p_base_path) { current_class = main_class; current_function = NULL; current_block = NULL; -#ifdef DEBUG_ENABLED + if (for_completion) check_types = false; -#else - check_types = false; -#endif // Resolve all class-level stuff before getting into function blocks _check_class_level_types(main_class); |