diff options
author | George Marques <george@gmarqu.es> | 2018-06-05 13:50:21 -0300 |
---|---|---|
committer | George Marques <george@gmarqu.es> | 2018-07-20 21:55:18 -0300 |
commit | 03746da73f275030832f83373388cb0f248616eb (patch) | |
tree | 6fb76e12980dfa2524df4e15927adc3eff6a2a08 /modules/gdscript | |
parent | a2305cd8b2fccf576ca62634a061b16edd4d6c55 (diff) |
Add editor highlight for type-safe lines
The line number is hightlighted to indicate that the line contains only
type-safe code.
Diffstat (limited to 'modules/gdscript')
-rw-r--r-- | modules/gdscript/gdscript.h | 2 | ||||
-rw-r--r-- | modules/gdscript/gdscript_editor.cpp | 4 | ||||
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 274 | ||||
-rw-r--r-- | modules/gdscript/gdscript_parser.h | 15 |
4 files changed, 192 insertions, 103 deletions
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 98196f0b0e..d5fe7a000b 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -397,7 +397,7 @@ public: virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const; virtual bool is_using_templates(); virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script); - virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const; + virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL, Set<int> *r_safe_lines = NULL) const; virtual Script *create_script() const; virtual bool has_named_classes() const; virtual bool supports_builtin_mode() const; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index a09c7a45cc..1848cbf6be 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -115,11 +115,11 @@ void GDScriptLanguage::make_template(const String &p_class_name, const String &p p_script->set_source_code(src); } -bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions) const { +bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, Set<int> *r_safe_lines) const { GDScriptParser parser; - Error err = parser.parse(p_script, p_path.get_base_dir(), true, p_path); + Error err = parser.parse(p_script, p_path.get_base_dir(), true, p_path, false, r_safe_lines); if (err) { r_line_error = parser.get_error_line(); r_col_error = parser.get_error_column(); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 24974f9155..f4426cd0a1 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2464,6 +2464,11 @@ void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) { id->name = "#match_value"; id->line = p_match_statement->line; id->datatype = _reduce_node_type(p_match_statement->val_to_match); + if (id->datatype.has_type) { + _mark_line_as_safe(id->line); + } else { + _mark_line_as_unsafe(id->line); + } if (error_set) { return; @@ -2480,6 +2485,7 @@ void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) { for (int j = 0; j < branch->patterns.size(); j++) { PatternNode *pattern = branch->patterns[j]; + _mark_line_as_safe(pattern->line); Map<StringName, Node *> bindings; Node *resulting_node = NULL; @@ -2633,6 +2639,7 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) { _set_error("Expected ';' or <NewLine>."); return; } + _mark_line_as_safe(tokenizer->get_token_line()); tokenizer->advance(); if (tokenizer->get_token() == GDScriptTokenizer::TK_SEMICOLON) { // Ignore semicolon after 'pass' @@ -3328,6 +3335,7 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } break; case GDScriptTokenizer::TK_PR_EXTENDS: { + _mark_line_as_safe(tokenizer->get_token_line()); _parse_extends(p_class); if (error_set) return; @@ -5693,6 +5701,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { return DataType(); } } + } else { + _mark_line_as_unsafe(cn->line); } node_type = cn->cast_type; @@ -5804,6 +5814,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { DataType argument_a_type = _reduce_node_type(op->arguments[0]); DataType argument_b_type = _reduce_node_type(op->arguments[1]); if (!argument_a_type.has_type || !argument_b_type.has_type) { + _mark_line_as_unsafe(op->line); break; } @@ -5902,6 +5913,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } else { node_type = _reduce_identifier_type(&base_type, member_id->name, op->line); } + } else { + _mark_line_as_unsafe(op->line); } if (error_set) { return DataType(); @@ -5928,6 +5941,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { DataType index_type = _reduce_node_type(op->arguments[1]); if (!base_type.has_type) { + _mark_line_as_unsafe(op->line); break; } @@ -6013,6 +6027,8 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) { } } break; } + } else { + _mark_line_as_unsafe(op->line); } } else if (!for_completion && (index_type.kind != DataType::BUILTIN || index_type.builtin_type != Variant::STRING)) { _set_error("Only strings can be used as index in the base type '" + base_type.to_string() + "'.", op->line); @@ -6386,6 +6402,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat } if (!base_type.has_type || (base_type.kind == DataType::BUILTIN && base_type.builtin_type == Variant::NIL)) { + _mark_line_as_unsafe(p_call->line); return DataType(); } @@ -6395,7 +6412,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat if (check_types) { if (!tmp.has_method(callee_name)) { - _set_error("Method '" + String(callee_name) + "()' is not declared in base '" + base_type.to_string() + "'.", p_call->line); + _set_error("Method '" + callee_name + "' is not declared on base '" + base_type.to_string() + "'.", p_call->line); return DataType(); } @@ -6446,12 +6463,6 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat default_args_count, is_static, is_vararg); } - String base_name; - if (p_call->arguments[0]->type == Node::TYPE_SELF) { - base_name = current_class->name == StringName() ? "self" : current_class->name; - } else { - base_name = original_type.to_string(); - } if (!valid) { if (p_call->arguments[0]->type == Node::TYPE_SELF) { _set_error("Method '" + callee_name + "' is not declared in the current class.", p_call->line); @@ -6496,12 +6507,19 @@ GDScriptParser::DataType GDScriptParser::_reduce_function_call_type(const Operat continue; } - if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) { - _set_error("At '" + callee_name + "()' call, argument " + itos(i - arg_diff + 1) + ". Assigned type (" + - par_type.to_string() + ") doesn't match the function argument's type (" + - arg_types[i - arg_diff].to_string() + ").", - p_call->line); - return DataType(); + if (!par_type.has_type) { + _mark_line_as_unsafe(p_call->line); + } else if (!_is_type_compatible(arg_types[i - arg_diff], par_type, true)) { + // Supertypes are acceptable for dynamic compliance + if (!_is_type_compatible(par_type, arg_types[i - arg_diff])) { + _set_error("At '" + callee_name + "()' call, argument " + itos(i - arg_diff + 1) + ". Assigned type (" + + par_type.to_string() + ") doesn't match the function argument's type (" + + arg_types[i - arg_diff].to_string() + ").", + p_call->line); + return DataType(); + } else { + _mark_line_as_unsafe(p_call->line); + } } } @@ -6858,23 +6876,23 @@ GDScriptParser::DataType GDScriptParser::_reduce_identifier_type(const DataType return member_type; } - if (!check_types) { - return DataType(); - } - if (!p_base_type) { - _set_error("Identifier '" + String(p_identifier) + "' is not declared in the current scope.", p_line); - } else { - _set_error("Identifier '" + String(p_identifier) + "' is not declared in base '" + p_base_type->to_string() + "'.", p_line); + // This means looking in the current class, which type is always known + _set_error("Identifier '" + p_identifier.operator String() + "' is not declared in the current scope.", p_line); } + + _mark_line_as_unsafe(p_line); return DataType(); } void GDScriptParser::_check_class_level_types(ClassNode *p_class) { + _mark_line_as_safe(p_class->line); + // Constants for (Map<StringName, ClassNode::Constant>::Element *E = p_class->constant_expressions.front(); E; E = E->next()) { ClassNode::Constant &c = E->get(); + _mark_line_as_safe(c.expression->line); DataType cont = _resolve_type(c.type, c.expression->line); DataType expr = _resolve_type(c.expression->get_datatype(), c.expression->line); @@ -6910,40 +6928,44 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { return; } - if (!v.data_type.has_type) continue; - + _mark_line_as_safe(v.line); v.data_type = _resolve_type(v.data_type, v.line); if (v.expression) { DataType expr_type = _reduce_node_type(v.expression); if (!_is_type_compatible(v.data_type, expr_type)) { - // Try with implicit conversion - if (v.data_type.kind != DataType::BUILTIN || !_is_type_compatible(v.data_type, expr_type, true)) { - _set_error("Assigned expression type (" + expr_type.to_string() + ") doesn't match the variable's type (" + - v.data_type.to_string() + ").", - v.line); - return; - } + // Try supertype test + if (_is_type_compatible(expr_type, v.data_type)) { + _mark_line_as_unsafe(v.line); + } else { + // Try with implicit conversion + if (v.data_type.kind != DataType::BUILTIN || !_is_type_compatible(v.data_type, expr_type, true)) { + _set_error("Assigned expression type (" + expr_type.to_string() + ") doesn't match the variable's type (" + + v.data_type.to_string() + ").", + v.line); + return; + } - // Replace assigment with implict conversion - BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); - convert->line = v.line; - convert->function = GDScriptFunctions::TYPE_CONVERT; + // Replace assigment with implict conversion + BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); + convert->line = v.line; + convert->function = GDScriptFunctions::TYPE_CONVERT; - ConstantNode *tgt_type = alloc_node<ConstantNode>(); - tgt_type->line = v.line; - tgt_type->value = (int)v.data_type.builtin_type; + ConstantNode *tgt_type = alloc_node<ConstantNode>(); + tgt_type->line = v.line; + tgt_type->value = (int)v.data_type.builtin_type; - OperatorNode *convert_call = alloc_node<OperatorNode>(); - convert_call->line = v.line; - convert_call->op = OperatorNode::OP_CALL; - convert_call->arguments.push_back(convert); - convert_call->arguments.push_back(v.expression); - convert_call->arguments.push_back(tgt_type); + OperatorNode *convert_call = alloc_node<OperatorNode>(); + convert_call->line = v.line; + convert_call->op = OperatorNode::OP_CALL; + convert_call->arguments.push_back(convert); + convert_call->arguments.push_back(v.expression); + convert_call->arguments.push_back(tgt_type); - v.expression = convert_call; - v.initial_assignment->arguments[1] = convert_call; + v.expression = convert_call; + v.initial_assignment->arguments[1] = convert_call; + } } } else if (v.data_type.has_type && v.data_type.kind == DataType::BUILTIN) { // Create default value based on the type @@ -6972,7 +6994,7 @@ void GDScriptParser::_check_class_level_types(ClassNode *p_class) { } // Check export hint - if (v._export.type != Variant::NIL) { + if (v.data_type.has_type && v._export.type != Variant::NIL) { DataType export_type = _type_from_property(v._export); if (!_is_type_compatible(v.data_type, export_type, true)) { _set_error("Export hint type (" + export_type.to_string() + ") doesn't match the variable's type (" + @@ -7149,6 +7171,7 @@ void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { for (int i = 0; i < p_class->static_functions.size(); i++) { current_function = p_class->static_functions[i]; current_block = current_function->body; + _mark_line_as_safe(current_function->line); _check_block_types(current_block); current_block = NULL; current_function = NULL; @@ -7158,6 +7181,7 @@ void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { for (int i = 0; i < p_class->functions.size(); i++) { current_function = p_class->functions[i]; current_block = current_function->body; + _mark_line_as_safe(current_function->line); _check_block_types(current_block); current_block = NULL; current_function = NULL; @@ -7175,6 +7199,8 @@ void GDScriptParser::_check_class_blocks_types(ClassNode *p_class) { void GDScriptParser::_check_block_types(BlockNode *p_block) { + Node *last_var_assign = NULL; + // Check each statement for (List<Node *>::Element *E = p_block->statements.front(); E; E = E->next()) { Node *statement = E->get(); @@ -7187,37 +7213,47 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { case Node::TYPE_LOCAL_VAR: { LocalVarNode *lv = static_cast<LocalVarNode *>(statement); lv->datatype = _resolve_type(lv->datatype, lv->line); + _mark_line_as_safe(lv->line); if (lv->assign) { DataType assign_type = _reduce_node_type(lv->assign); if (!_is_type_compatible(lv->datatype, assign_type)) { - // Try implict conversion - if (lv->datatype.kind != DataType::BUILTIN || !_is_type_compatible(lv->datatype, assign_type, true)) { - _set_error("Assigned value type (" + assign_type.to_string() + ") doesn't match the variable's type (" + - lv->datatype.to_string() + ").", - lv->line); - return; - } - // Replace assigment with implict conversion - BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); - convert->line = lv->line; - convert->function = GDScriptFunctions::TYPE_CONVERT; + // Try supertype test + if (_is_type_compatible(assign_type, lv->datatype)) { + _mark_line_as_unsafe(lv->line); + } else { + // Try implict conversion + if (lv->datatype.kind != DataType::BUILTIN || !_is_type_compatible(lv->datatype, assign_type, true)) { + _set_error("Assigned value type (" + assign_type.to_string() + ") doesn't match the variable's type (" + + lv->datatype.to_string() + ").", + lv->line); + return; + } + // Replace assigment with implict conversion + BuiltInFunctionNode *convert = alloc_node<BuiltInFunctionNode>(); + convert->line = lv->line; + convert->function = GDScriptFunctions::TYPE_CONVERT; - ConstantNode *tgt_type = alloc_node<ConstantNode>(); - tgt_type->line = lv->line; - tgt_type->value = (int)lv->datatype.builtin_type; + ConstantNode *tgt_type = alloc_node<ConstantNode>(); + tgt_type->line = lv->line; + tgt_type->value = (int)lv->datatype.builtin_type; - OperatorNode *convert_call = alloc_node<OperatorNode>(); - convert_call->line = lv->line; - convert_call->op = OperatorNode::OP_CALL; - convert_call->arguments.push_back(convert); - convert_call->arguments.push_back(lv->assign); - convert_call->arguments.push_back(tgt_type); + OperatorNode *convert_call = alloc_node<OperatorNode>(); + convert_call->line = lv->line; + convert_call->op = OperatorNode::OP_CALL; + convert_call->arguments.push_back(convert); + convert_call->arguments.push_back(lv->assign); + convert_call->arguments.push_back(tgt_type); - lv->assign = convert_call; - lv->assign_op->arguments[1] = convert_call; + lv->assign = convert_call; + lv->assign_op->arguments[1] = convert_call; + } + } + if (lv->datatype.has_type && !assign_type.has_type) { + _mark_line_as_unsafe(lv->line); } } + last_var_assign = lv->assign; // TODO: Make a warning /* @@ -7247,6 +7283,13 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { return; } + if (op->arguments[1] == last_var_assign) { + // Assignment was already checked + break; + } + + _mark_line_as_safe(op->line); + DataType lh_type = _reduce_node_type(op->arguments[0]); if (error_set) { @@ -7254,10 +7297,10 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { } if (!lh_type.has_type) { - break; - } - - if (lh_type.is_constant) { + if (op->arguments[0]->type == Node::TYPE_OPERATOR) { + _mark_line_as_unsafe(op->line); + } + } else if (lh_type.is_constant) { _set_error("Cannot assign a new value to a constant.", op->line); return; } @@ -7267,6 +7310,7 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { // Validate operation DataType arg_type = _reduce_node_type(op->arguments[1]); if (!arg_type.has_type) { + _mark_line_as_unsafe(op->line); break; } @@ -7285,38 +7329,49 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { } if (!_is_type_compatible(lh_type, rh_type)) { - // Try implict conversion - if (lh_type.kind != DataType::BUILTIN || !_is_type_compatible(lh_type, rh_type, true)) { - _set_error("Assigned value type (" + rh_type.to_string() + ") doesn't match the variable's type (" + - lh_type.to_string() + ").", - op->line); - return; + // Try supertype test + if (_is_type_compatible(rh_type, lh_type)) { + _mark_line_as_unsafe(op->line); + } else { + // Try implict conversion + if (lh_type.kind != DataType::BUILTIN || !_is_type_compatible(lh_type, rh_type, true)) { + _set_error("Assigned value type (" + rh_type.to_string() + ") doesn't match the variable's type (" + + lh_type.to_string() + ").", + op->line); + return; + } + // Replace assigment with implict 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[1] = convert_call; } - // Replace assigment with implict 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[1] = convert_call; + } + if (!rh_type.has_type && (op->op != OperatorNode::OP_ASSIGN || lh_type.has_type || op->arguments[0]->type == Node::TYPE_OPERATOR)) { + _mark_line_as_unsafe(op->line); } } break; case OperatorNode::OP_CALL: case OperatorNode::OP_PARENT_CALL: { + _mark_line_as_safe(op->line); _reduce_function_call_type(op); if (error_set) return; } break; default: { + _mark_line_as_safe(op->line); + _reduce_node_type(op); // Test for safety anyway // TODO: Make this a warning /*_set_error("Standalone expression, nothing is done in this line.", statement->line); return; */ @@ -7325,11 +7380,20 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { } break; case Node::TYPE_CONTROL_FLOW: { ControlFlowNode *cf = static_cast<ControlFlowNode *>(statement); + _mark_line_as_safe(cf->line); switch (cf->cf_type) { case ControlFlowNode::CF_RETURN: { DataType function_type = current_function->get_datatype(); + DataType ret_type; + if (cf->arguments.size() > 0) { + ret_type = _reduce_node_type(cf->arguments[0]); + if (error_set) { + return; + } + } + if (!function_type.has_type) break; if (function_type.kind == DataType::BUILTIN && function_type.builtin_type == Variant::NIL) { @@ -7345,10 +7409,6 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { return; } - DataType ret_type = _reduce_node_type(cf->arguments[0]); - if (error_set) { - return; - } if (!_is_type_compatible(function_type, ret_type)) { _set_error("Returned value type (" + ret_type.to_string() + ") doesn't match the function return type (" + function_type.to_string() + ").", @@ -7361,6 +7421,14 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { MatchNode *match_node = cf->match; _transform_match_statment(match_node); } break; + default: { + if (cf->body_else) { + _mark_line_as_safe(cf->body_else->line); + } + for (int i = 0; i < cf->arguments.size(); i++) { + _reduce_node_type(cf->arguments[i]); + } + } break; } } break; case Node::TYPE_CONSTANT: { @@ -7371,6 +7439,8 @@ void GDScriptParser::_check_block_types(BlockNode *p_block) { } } // falthrough default: { + _mark_line_as_safe(statement->line); + _reduce_node_type(statement); // Test for safety anyway // TODO: Make this a warning /* _set_error("Standalone expression, nothing is done in this line.", statement->line); return; */ @@ -7479,7 +7549,7 @@ Error GDScriptParser::parse_bytecode(const Vector<uint8_t> &p_bytecode, const St return ret; } -Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion) { +Error GDScriptParser::parse(const String &p_code, const String &p_base_path, bool p_just_validate, const String &p_self_path, bool p_for_completion, Set<int> *r_safe_lines) { clear(); @@ -7489,6 +7559,9 @@ Error GDScriptParser::parse(const String &p_code, const String &p_base_path, boo validating = p_just_validate; for_completion = p_for_completion; +#ifdef DEBUG_ENABLED + safe_lines = r_safe_lines; +#endif // DEBUG_ENABLED tokenizer = tt; Error ret = _parse(p_base_path); memdelete(tt); @@ -7543,6 +7616,9 @@ void GDScriptParser::clear() { current_export.type = Variant::NIL; check_types = true; error = ""; +#ifdef DEBUG_ENABLED + safe_lines = NULL; +#endif // DEBUG_ENABLED } GDScriptParser::CompletionType GDScriptParser::get_completion_type() { diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index e9be25efc7..2650f619f8 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -512,6 +512,9 @@ private: int error_line; int error_column; bool check_types; +#ifdef DEBUG_ENABLED + Set<int> *safe_lines; +#endif // DEBUG_ENABLED int pending_newline; @@ -583,6 +586,16 @@ private: void _check_class_blocks_types(ClassNode *p_class); void _check_function_types(FunctionNode *p_function); void _check_block_types(BlockNode *p_block); + _FORCE_INLINE_ void _mark_line_as_safe(int p_line) const { +#ifdef DEBUG_ENABLED + if (safe_lines) safe_lines->insert(p_line); +#endif // DEBUG_ENABLED + } + _FORCE_INLINE_ void _mark_line_as_unsafe(int p_line) const { +#ifdef DEBUG_ENABLED + if (safe_lines) safe_lines->erase(p_line); +#endif // DEBUG_ENABLED + } Error _parse(const String &p_base_path); @@ -590,7 +603,7 @@ public: String get_error() const; int get_error_line() const; int get_error_column() const; - Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false); + Error parse(const String &p_code, const String &p_base_path = "", bool p_just_validate = false, const String &p_self_path = "", bool p_for_completion = false, Set<int> *r_safe_lines = NULL); Error parse_bytecode(const Vector<uint8_t> &p_bytecode, const String &p_base_path = "", const String &p_self_path = ""); bool is_tool_script() const; |