From 5bcc3d476cd06c9e9edd00e63a221d77d9ff7c0b Mon Sep 17 00:00:00 2001 From: Kirill Diduk Date: Mon, 13 Jun 2022 14:25:08 +0200 Subject: [PATCH] Fix parsing inner class declaration when "pass" keyword is on the same line Implement a special case for allowing "pass" keyword in one-liner class declaration, to be consistent with Python style. ``` class TestClass: pass ``` This commit fixes #56703 --- modules/gdscript/gdscript_parser.cpp | 78 +++++++++++++++++++++------- modules/gdscript/gdscript_parser.h | 3 ++ 2 files changed, 62 insertions(+), 19 deletions(-) diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 735bc3f5898..9ce1d538f07 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -82,8 +82,51 @@ void GDScriptParser::_set_end_statement_error(String p_name) { } bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { + if (!_parse_colon()) { + return false; + } + + if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { + // Be more Python-like. + IndentLevel current_level = indent_level.back()->get(); + indent_level.push_back(current_level); + return true; + } + + return _parse_indent_block_newlines(p_block); +} + +bool GDScriptParser::_enter_inner_class_indent_block() { + if (!_parse_colon()) { + return false; + } + + if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { + // Check Python-like one-liner class declaration "class Foo: pass". + // Note: only "pass" is allowed on the same line after the colon. + if (tokenizer->get_token() != GDScriptTokenizer::TK_CF_PASS) { + return false; + } + + GDScriptTokenizer::Token token = tokenizer->get_token(1); + if (token != GDScriptTokenizer::TK_NEWLINE && token != GDScriptTokenizer::TK_EOF) { + int line = tokenizer->get_token_line(); + int col = tokenizer->get_token_column(); + String message = "Invalid syntax: unexpected \""; + message += GDScriptTokenizer::get_token_name(token); + message += "\"."; + _set_error(message, line, col); + return false; + } + return true; + } + + return _parse_indent_block_newlines(); +} + +bool GDScriptParser::_parse_colon() { if (tokenizer->get_token() != GDScriptTokenizer::TK_COLON) { - // report location at the previous token (on the previous line) + // Report location at the previous token (on the previous line). int error_line = tokenizer->get_token_line(-1); int error_column = tokenizer->get_token_column(-1); _set_error("':' expected at end of line.", error_line, error_column); @@ -95,19 +138,12 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { return false; } - if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { - // be more python-like - IndentLevel current_level = indent_level.back()->get(); - indent_level.push_back(current_level); - return true; - //_set_error("newline expected after ':'."); - //return false; - } + return true; +} +bool GDScriptParser::_parse_indent_block_newlines(BlockNode *p_block) { while (true) { - if (tokenizer->get_token() != GDScriptTokenizer::TK_NEWLINE) { - return false; //wtf - } else if (tokenizer->get_token(1) == GDScriptTokenizer::TK_EOF) { + if (tokenizer->get_token(1) == GDScriptTokenizer::TK_EOF) { return false; } else if (tokenizer->get_token(1) != GDScriptTokenizer::TK_NEWLINE) { int indent = tokenizer->get_token_line_indent(); @@ -126,14 +162,13 @@ bool GDScriptParser::_enter_indent_block(BlockNode *p_block) { indent_level.push_back(new_indent); tokenizer->advance(); return true; - } else if (p_block) { NewLineNode *nl = alloc_node(); nl->line = tokenizer->get_token_line(); p_block->statements.push_back(nl); } - tokenizer->advance(); // go to next newline + tokenizer->advance(); // Go to the next newline. } } @@ -3840,13 +3875,18 @@ void GDScriptParser::_parse_class(ClassNode *p_class) { } } - if (!_enter_indent_block()) { - _set_error("Indented block expected."); + if (!_enter_inner_class_indent_block()) { + if (!error_set) { + _set_error("Indented block or \"pass\" expected."); + } return; } - current_class = newclass; - _parse_class(newclass); - current_class = p_class; + + if (tokenizer->get_token() != GDScriptTokenizer::TK_CF_PASS) { + current_class = newclass; + _parse_class(newclass); + current_class = p_class; + } } break; /* this is for functions.... diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index f3daa4e72d7..725d3df7c8a 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -599,6 +599,9 @@ private: bool _parse_arguments(Node *p_parent, Vector &p_args, bool p_static, bool p_can_codecomplete = false, bool p_parsing_constant = false); bool _enter_indent_block(BlockNode *p_block = nullptr); + bool _enter_inner_class_indent_block(); + bool _parse_colon(); + bool _parse_indent_block_newlines(BlockNode *p_block = nullptr); bool _parse_newline(); Node *_parse_expression(Node *p_parent, bool p_static, bool p_allow_assign = false, bool p_parsing_constant = false); Node *_reduce_expression(Node *p_node, bool p_to_const = false);