@@ -674,23 +674,50 @@ void GDScriptParser::parse_program() {
674674 reset_extents (head, current);
675675 }
676676
677- bool has_early_abstract = false ;
677+ bool first_is_abstract = false ;
678678 while (can_have_class_or_extends) {
679679 // Order here doesn't matter, but there should be only one of each at most.
680680 switch (current.type ) {
681681 case GDScriptTokenizer::Token::ABSTRACT: {
682- PUSH_PENDING_ANNOTATIONS_TO_HEAD;
683- if (head->start_line == 1 ) {
684- reset_extents (head, current);
682+ if (head->is_abstract ) {
683+ // The root class is already marked as abstract, so this is
684+ // the beginning of an abstract function or inner class.
685+ can_have_class_or_extends = false ;
686+ break ;
685687 }
688+
689+ const GDScriptTokenizer::Token abstract_token = current;
686690 advance ();
687- if (has_early_abstract) {
688- push_error (R"( Expected "class_name", "extends", or "class" after "abstract".)" );
689- } else {
690- has_early_abstract = true ;
691- }
691+
692+ // A standalone "abstract" is only allowed for script-level stuff.
693+ bool is_standalone = false ;
692694 if (current.type == GDScriptTokenizer::Token::NEWLINE) {
693- end_statement (" class_name abstract" );
695+ is_standalone = true ;
696+ end_statement (" standalone \" abstract\" " );
697+ }
698+
699+ switch (current.type ) {
700+ case GDScriptTokenizer::Token::CLASS_NAME:
701+ case GDScriptTokenizer::Token::EXTENDS:
702+ PUSH_PENDING_ANNOTATIONS_TO_HEAD;
703+ head->is_abstract = true ;
704+ if (head->start_line == 1 ) {
705+ reset_extents (head, abstract_token);
706+ }
707+ break ;
708+ case GDScriptTokenizer::Token::CLASS:
709+ case GDScriptTokenizer::Token::FUNC:
710+ if (is_standalone) {
711+ push_error (R"( Expected "class_name" or "extends" after a standalone "abstract".)" );
712+ } else {
713+ first_is_abstract = true ;
714+ }
715+ // This is the beginning of an abstract function or inner class.
716+ can_have_class_or_extends = false ;
717+ break ;
718+ default :
719+ push_error (R"( Expected "class_name", "extends", "class", or "func" after "abstract".)" );
720+ break ;
694721 }
695722 } break ;
696723 case GDScriptTokenizer::Token::CLASS_NAME:
@@ -701,10 +728,6 @@ void GDScriptParser::parse_program() {
701728 } else {
702729 parse_class_name ();
703730 }
704- if (has_early_abstract) {
705- head->is_abstract = true ;
706- has_early_abstract = false ;
707- }
708731 break ;
709732 case GDScriptTokenizer::Token::EXTENDS:
710733 PUSH_PENDING_ANNOTATIONS_TO_HEAD;
@@ -715,10 +738,6 @@ void GDScriptParser::parse_program() {
715738 parse_extends ();
716739 end_statement (" superclass" );
717740 }
718- if (has_early_abstract) {
719- head->is_abstract = true ;
720- has_early_abstract = false ;
721- }
722741 break ;
723742 case GDScriptTokenizer::Token::TK_EOF:
724743 PUSH_PENDING_ANNOTATIONS_TO_HEAD;
@@ -753,7 +772,7 @@ void GDScriptParser::parse_program() {
753772
754773#undef PUSH_PENDING_ANNOTATIONS_TO_HEAD
755774
756- parse_class_body (has_early_abstract , true );
775+ parse_class_body (first_is_abstract , true );
757776
758777 head->end_line = current.end_line ;
759778 head->end_column = current.end_column ;
@@ -1028,25 +1047,19 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(b
10281047 }
10291048}
10301049
1031- void GDScriptParser::parse_class_body (bool p_is_abstract , bool p_is_multiline) {
1050+ void GDScriptParser::parse_class_body (bool p_first_is_abstract , bool p_is_multiline) {
10321051 bool class_end = false ;
1033- // The header parsing code might have skipped over abstract, so we start by checking the previous token.
1034- bool next_is_abstract = p_is_abstract;
1035- if (next_is_abstract && (current.type != GDScriptTokenizer::Token::CLASS_NAME && current.type != GDScriptTokenizer::Token::CLASS)) {
1036- push_error (R"( Expected "class_name" or "class" after "abstract".)" );
1037- }
1052+ // The header parsing code could consume `abstract` for the first function or inner class.
1053+ bool next_is_abstract = p_first_is_abstract;
10381054 bool next_is_static = false ;
10391055 while (!class_end && !is_at_end ()) {
10401056 GDScriptTokenizer::Token token = current;
10411057 switch (token.type ) {
10421058 case GDScriptTokenizer::Token::ABSTRACT: {
10431059 advance ();
10441060 next_is_abstract = true ;
1045- if (check (GDScriptTokenizer::Token::NEWLINE)) {
1046- advance ();
1047- }
1048- if (!check (GDScriptTokenizer::Token::CLASS_NAME) && !check (GDScriptTokenizer::Token::CLASS)) {
1049- push_error (R"( Expected "class_name" or "class" after "abstract".)" );
1061+ if (!check (GDScriptTokenizer::Token::CLASS) && !check (GDScriptTokenizer::Token::FUNC)) {
1062+ push_error (R"( Expected "class" or "func" after "abstract".)" );
10501063 }
10511064 } break ;
10521065 case GDScriptTokenizer::Token::VAR:
@@ -1062,12 +1075,11 @@ void GDScriptParser::parse_class_body(bool p_is_abstract, bool p_is_multiline) {
10621075 parse_class_member (&GDScriptParser::parse_signal, AnnotationInfo::SIGNAL, " signal" );
10631076 break ;
10641077 case GDScriptTokenizer::Token::FUNC:
1065- parse_class_member (&GDScriptParser::parse_function, AnnotationInfo::FUNCTION, " function" , false , next_is_static);
1078+ parse_class_member (&GDScriptParser::parse_function, AnnotationInfo::FUNCTION, " function" , next_is_abstract , next_is_static);
10661079 break ;
1067- case GDScriptTokenizer::Token::CLASS: {
1080+ case GDScriptTokenizer::Token::CLASS:
10681081 parse_class_member (&GDScriptParser::parse_class, AnnotationInfo::CLASS, " class" , next_is_abstract);
1069- next_is_abstract = false ;
1070- } break ;
1082+ break ;
10711083 case GDScriptTokenizer::Token::ENUM:
10721084 parse_class_member (&GDScriptParser::parse_enum, AnnotationInfo::NONE, " enum" );
10731085 break ;
@@ -1146,6 +1158,9 @@ void GDScriptParser::parse_class_body(bool p_is_abstract, bool p_is_multiline) {
11461158 }
11471159 break ;
11481160 }
1161+ if (token.type != GDScriptTokenizer::Token::ABSTRACT) {
1162+ next_is_abstract = false ;
1163+ }
11491164 if (token.type != GDScriptTokenizer::Token::STATIC) {
11501165 next_is_static = false ;
11511166 }
@@ -1662,18 +1677,23 @@ void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNod
16621677
16631678#ifdef TOOLS_ENABLED
16641679 if (p_type == " function" && p_signature_start != -1 ) {
1665- int signature_end_pos = tokenizer->get_current_position () - 1 ;
1666- String source_code = tokenizer->get_source_code ();
1667- p_function->signature = source_code.substr (p_signature_start, signature_end_pos - p_signature_start);
1680+ const int signature_end_pos = tokenizer->get_current_position () - 1 ;
1681+ const String source_code = tokenizer->get_source_code ();
1682+ p_function->signature = source_code.substr (p_signature_start, signature_end_pos - p_signature_start). strip_edges ( false , true ) ;
16681683 }
16691684#endif // TOOLS_ENABLED
16701685
1671- // TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
1672- consume (GDScriptTokenizer::Token::COLON, vformat (R"( Expected ":" after %s declaration.)" , p_type));
1686+ if (p_function->is_abstract ) {
1687+ end_statement (" abstract function declaration" );
1688+ } else {
1689+ // TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
1690+ consume (GDScriptTokenizer::Token::COLON, vformat (R"( Expected ":" after %s declaration.)" , p_type));
1691+ }
16731692}
16741693
16751694GDScriptParser::FunctionNode *GDScriptParser::parse_function (bool p_is_abstract, bool p_is_static) {
16761695 FunctionNode *function = alloc_node<FunctionNode>();
1696+ function->is_abstract = p_is_abstract;
16771697 function->is_static = p_is_static;
16781698
16791699 make_completion_context (COMPLETION_OVERRIDE_METHOD, function);
@@ -1714,7 +1734,13 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function(bool p_is_abstract,
17141734 function->min_local_doc_line = previous.end_line + 1 ;
17151735#endif // TOOLS_ENABLED
17161736
1717- function->body = parse_suite (" function declaration" , body);
1737+ if (function->is_abstract ) {
1738+ reset_extents (body, current);
1739+ complete_extents (body);
1740+ function->body = body;
1741+ } else {
1742+ function->body = parse_suite (" function declaration" , body);
1743+ }
17181744
17191745 current_function = previous_function;
17201746 complete_extents (function);
@@ -5936,6 +5962,9 @@ void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const
59365962 for (const AnnotationNode *E : p_function->annotations ) {
59375963 print_annotation (E);
59385964 }
5965+ if (p_function->is_abstract ) {
5966+ push_text (" Abstract " );
5967+ }
59395968 if (p_function->is_static ) {
59405969 push_text (" Static " );
59415970 }
0 commit comments