diff options
Diffstat (limited to 'modules/gdscript/gdscript_parser.cpp')
-rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 97 |
1 files changed, 64 insertions, 33 deletions
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index c402b63f7b..6a4a4e66f2 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -712,14 +712,14 @@ void GDScriptParser::parse_extends() { if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected superclass name after "extends".)")) { return; } - current_class->extends.push_back(previous.literal); + current_class->extends.push_back(parse_identifier()); while (match(GDScriptTokenizer::Token::PERIOD)) { make_completion_context(COMPLETION_INHERIT_TYPE, current_class, chain_index++); if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected superclass name after ".".)")) { return; } - current_class->extends.push_back(previous.literal); + current_class->extends.push_back(parse_identifier()); } } @@ -761,7 +761,11 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)() #ifdef TOOLS_ENABLED // Consume doc comments. class_doc_line = MIN(class_doc_line, doc_comment_line - 1); - if (has_comment(doc_comment_line)) { + + // Check whether current line has a doc comment + if (has_comment(previous.start_line, true)) { + member->doc_description = get_doc_comment(previous.start_line, true); + } else if (has_comment(doc_comment_line, true)) { if constexpr (std::is_same_v<T, ClassNode>) { get_class_doc_comment(doc_comment_line, member->doc_brief_description, member->doc_description, member->doc_tutorials, true); } else { @@ -1343,7 +1347,7 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod default_used = true; } else { if (default_used) { - push_error("Cannot have a mandatory parameters after optional parameters."); + push_error("Cannot have mandatory parameters after optional parameters."); continue; } } @@ -1439,27 +1443,32 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali valid = false; } - if (match(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) { + if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) { + push_multiline(true); + advance(); // Arguments. push_completion_call(annotation); make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, 0, true); - if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) { - push_multiline(true); - int argument_index = 0; - do { - make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index, true); - set_last_completion_call_arg(argument_index++); - ExpressionNode *argument = parse_expression(false); - if (argument == nullptr) { - valid = false; - continue; - } - annotation->arguments.push_back(argument); - } while (match(GDScriptTokenizer::Token::COMMA)); - pop_multiline(); + int argument_index = 0; + do { + if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) { + // Allow for trailing comma. + break; + } - consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after annotation arguments.)*"); - } + make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index, true); + set_last_completion_call_arg(argument_index++); + ExpressionNode *argument = parse_expression(false); + if (argument == nullptr) { + push_error("Expected expression as the annotation argument."); + valid = false; + continue; + } + annotation->arguments.push_back(argument); + } while (match(GDScriptTokenizer::Token::COMMA) && !is_at_end()); + + pop_multiline(); + consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after annotation arguments.)*"); pop_completion_call(); } complete_extents(annotation); @@ -1475,7 +1484,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali void GDScriptParser::clear_unused_annotations() { for (const AnnotationNode *annotation : annotation_stack) { - push_error(vformat(R"(Annotation "%s" does not precedes a valid target, so it will have no effect.)", annotation->name), annotation); + push_error(vformat(R"(Annotation "%s" does not precede a valid target, so it will have no effect.)", annotation->name), annotation); } annotation_stack.clear(); @@ -1521,7 +1530,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, int error_count = 0; do { - if (!multiline && previous.type == GDScriptTokenizer::Token::SEMICOLON && check(GDScriptTokenizer::Token::NEWLINE)) { + if (is_at_end() || (!multiline && previous.type == GDScriptTokenizer::Token::SEMICOLON && check(GDScriptTokenizer::Token::NEWLINE))) { break; } Node *statement = parse_statement(); @@ -1817,7 +1826,7 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() { n_for->list = parse_expression(false); if (!n_for->list) { - push_error(R"(Expected a list or range after "in".)"); + push_error(R"(Expected iterable after "in".)"); } consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "for" condition.)"); @@ -2820,6 +2829,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode * attribute->base = p_previous_operand; + if (current.is_node_name()) { + current.type = GDScriptTokenizer::Token::IDENTIFIER; + } if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) { complete_extents(attribute); return attribute; @@ -3288,8 +3300,15 @@ static bool _in_codeblock(String p_line, bool p_already_in, int *r_block_begins } } -bool GDScriptParser::has_comment(int p_line) { - return tokenizer.get_comments().has(p_line); +bool GDScriptParser::has_comment(int p_line, bool p_must_be_doc) { + bool has_comment = tokenizer.get_comments().has(p_line); + // If there are no comments or if we don't care whether the comment + // is a docstring, we have our result. + if (!p_must_be_doc || !has_comment) { + return has_comment; + } + + return tokenizer.get_comments()[p_line].comment.begins_with("##"); } String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) { @@ -3678,6 +3697,12 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation) } bool GDScriptParser::tool_annotation(const AnnotationNode *p_annotation, Node *p_node) { +#ifdef DEBUG_ENABLED + if (this->_is_tool) { + push_error(R"("@tool" annotation can only be used once.)", p_annotation); + return false; + } +#endif // DEBUG_ENABLED this->_is_tool = true; return true; } @@ -3686,6 +3711,16 @@ bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p ERR_FAIL_COND_V_MSG(p_node->type != Node::CLASS, false, R"("@icon" annotation can only be applied to classes.)"); ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false); ClassNode *p_class = static_cast<ClassNode *>(p_node); +#ifdef DEBUG_ENABLED + if (!p_class->icon_path.is_empty()) { + push_error(R"("@icon" annotation can only be used once.)", p_annotation); + return false; + } + if (String(p_annotation->resolved_arguments[0]).is_empty()) { + push_error(R"("@icon" annotation argument must contain the path to the icon.)", p_annotation->arguments[0]); + return false; + } +#endif // DEBUG_ENABLED p_class->icon_path = p_annotation->resolved_arguments[0]; return true; } @@ -3856,7 +3891,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node variable->export_info.hint = PROPERTY_HINT_NODE_TYPE; variable->export_info.hint_string = export_type.to_string(); } else { - push_error(R"(Export type can only be built-in, a resource, a node or an enum.)", variable); + push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable); return false; } @@ -3901,8 +3936,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node variable->export_info.hint_string = enum_hint_string; } break; default: - // TODO: Allow custom user resources. - push_error(R"(Export type can only be built-in, a resource, or an enum.)", variable); + push_error(R"(Export type can only be built-in, a resource, a node, or an enum.)", variable); break; } @@ -4121,9 +4155,6 @@ String GDScriptParser::DataType::to_string() const { } return native_type.operator String(); case CLASS: - if (is_meta_type) { - return GDScript::get_class_static(); - } if (class_type->identifier != nullptr) { return class_type->identifier->name.operator String(); } @@ -4479,7 +4510,7 @@ void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) { } else { first = false; } - push_text(p_class->extends[i]); + push_text(p_class->extends[i]->name); } } |