@@ -94,10 +94,11 @@ bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
9494GDScriptParser::GDScriptParser () {
9595 // Register valid annotations.
9696 if (unlikely (valid_annotations.is_empty ())) {
97+ // Script annotations.
9798 register_annotation (MethodInfo (" @tool" ), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
9899 register_annotation (MethodInfo (" @icon" , PropertyInfo (Variant::STRING, " icon_path" )), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
99100 register_annotation (MethodInfo (" @static_unload" ), AnnotationInfo::SCRIPT, &GDScriptParser::static_unload_annotation);
100-
101+ // Onready annotation.
101102 register_annotation (MethodInfo (" @onready" ), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
102103 // Export annotations.
103104 register_annotation (MethodInfo (" @export" ), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
@@ -128,13 +129,18 @@ GDScriptParser::GDScriptParser() {
128129 register_annotation (MethodInfo (" @export_group" , PropertyInfo (Variant::STRING, " name" ), PropertyInfo (Variant::STRING, " prefix" )), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray (" " ));
129130 register_annotation (MethodInfo (" @export_subgroup" , PropertyInfo (Variant::STRING, " name" ), PropertyInfo (Variant::STRING, " prefix" )), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, varray (" " ));
130131 // Warning annotations.
131- register_annotation (MethodInfo (" @warning_ignore" , PropertyInfo (Variant::STRING, " warning" )), AnnotationInfo::CLASS_LEVEL | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, varray (), true );
132+ register_annotation (MethodInfo (" @warning_ignore" , PropertyInfo (Variant::STRING, " warning" )), AnnotationInfo::CLASS_LEVEL | AnnotationInfo::STATEMENT, &GDScriptParser::warning_ignore_annotation, varray (), true );
133+ register_annotation (MethodInfo (" @warning_ignore_start" , PropertyInfo (Variant::STRING, " warning" )), AnnotationInfo::STANDALONE, &GDScriptParser::warning_ignore_region_annotations, varray (), true );
134+ register_annotation (MethodInfo (" @warning_ignore_restore" , PropertyInfo (Variant::STRING, " warning" )), AnnotationInfo::STANDALONE, &GDScriptParser::warning_ignore_region_annotations, varray (), true );
132135 // Networking.
133136 register_annotation (MethodInfo (" @rpc" , PropertyInfo (Variant::STRING, " mode" ), PropertyInfo (Variant::STRING, " sync" ), PropertyInfo (Variant::STRING, " transfer_mode" ), PropertyInfo (Variant::INT, " transfer_channel" )), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray (" authority" , " call_remote" , " unreliable" , 0 ));
134137 }
135138
136139#ifdef DEBUG_ENABLED
137140 is_ignoring_warnings = !(bool )GLOBAL_GET (" debug/gdscript/warnings/enable" );
141+ for (int i = 0 ; i < GDScriptWarning::WARNING_MAX; i++) {
142+ warning_ignore_start_lines[i] = INT_MAX;
143+ }
138144#endif
139145
140146#ifdef TOOLS_ENABLED
@@ -214,6 +220,9 @@ void GDScriptParser::apply_pending_warnings() {
214220 if (warning_ignored_lines[pw.code ].has (pw.source ->start_line )) {
215221 continue ;
216222 }
223+ if (warning_ignore_start_lines[pw.code ] <= pw.source ->start_line ) {
224+ continue ;
225+ }
217226
218227 GDScriptWarning warning;
219228 warning.code = pw.code ;
@@ -625,7 +634,7 @@ void GDScriptParser::parse_program() {
625634 } else if (annotation->applies_to (AnnotationInfo::SCRIPT)) {
626635 PUSH_PENDING_ANNOTATIONS_TO_HEAD;
627636 if (annotation->name == SNAME (" @tool" ) || annotation->name == SNAME (" @icon" )) {
628- // Some annotations need to be resolved in the parser.
637+ // Some annotations need to be resolved and applied in the parser.
629638 annotation->apply (this , head, nullptr ); // `head->outer == nullptr`.
630639 } else {
631640 head->annotations .push_back (annotation);
@@ -640,8 +649,10 @@ void GDScriptParser::parse_program() {
640649 // so we stop looking for script-level stuff.
641650 can_have_class_or_extends = false ;
642651 break ;
652+ } else if (annotation->name == SNAME (" @warning_ignore_start" ) || annotation->name == SNAME (" @warning_ignore_restore" )) {
653+ // Some annotations need to be resolved and applied in the parser.
654+ annotation->apply (this , nullptr , nullptr );
643655 } else {
644- // For potential non-group standalone annotations.
645656 push_error (R"( Unexpected standalone annotation.)" );
646657 }
647658 } else {
@@ -1030,8 +1041,10 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
10301041 }
10311042 if (annotation->name == SNAME (" @export_category" ) || annotation->name == SNAME (" @export_group" ) || annotation->name == SNAME (" @export_subgroup" )) {
10321043 current_class->add_member_group (annotation);
1044+ } else if (annotation->name == SNAME (" @warning_ignore_start" ) || annotation->name == SNAME (" @warning_ignore_restore" )) {
1045+ // Some annotations need to be resolved and applied in the parser.
1046+ annotation->apply (this , nullptr , nullptr );
10331047 } else {
1034- // For potential non-group standalone annotations.
10351048 push_error (R"( Unexpected standalone annotation.)" );
10361049 }
10371050 } else { // `AnnotationInfo::CLASS_LEVEL`.
@@ -1896,9 +1909,21 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
18961909 break ;
18971910 case GDScriptTokenizer::Token::ANNOTATION: {
18981911 advance ();
1899- AnnotationNode *annotation = parse_annotation (AnnotationInfo::STATEMENT);
1912+ AnnotationNode *annotation = parse_annotation (AnnotationInfo::STATEMENT | AnnotationInfo::STANDALONE );
19001913 if (annotation != nullptr ) {
1901- annotation_stack.push_back (annotation);
1914+ if (annotation->applies_to (AnnotationInfo::STANDALONE)) {
1915+ if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
1916+ push_error (R"( Expected newline after a standalone annotation.)" );
1917+ }
1918+ if (annotation->name == SNAME (" @warning_ignore_start" ) || annotation->name == SNAME (" @warning_ignore_restore" )) {
1919+ // Some annotations need to be resolved and applied in the parser.
1920+ annotation->apply (this , nullptr , nullptr );
1921+ } else {
1922+ push_error (R"( Unexpected standalone annotation.)" );
1923+ }
1924+ } else {
1925+ annotation_stack.push_back (annotation);
1926+ }
19021927 }
19031928 break ;
19041929 }
@@ -4096,23 +4121,25 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
40964121 return false ;
40974122 }
40984123
4099- // Some annotations need to be resolved in the parser.
4100- if (p_annotation->name == SNAME (" @icon" )) {
4101- ExpressionNode *argument = p_annotation->arguments [0 ];
4124+ // Some annotations need to be resolved and applied in the parser.
4125+ if (p_annotation->name == SNAME (" @icon" ) || p_annotation->name == SNAME (" @warning_ignore_start" ) || p_annotation->name == SNAME (" @warning_ignore_restore" )) {
4126+ for (int i = 0 ; i < p_annotation->arguments .size (); i++) {
4127+ ExpressionNode *argument = p_annotation->arguments [i];
41024128
4103- if (argument->type != Node::LITERAL) {
4104- push_error (R"( Argument 1 of annotation "@icon " must be a string literal.)" , argument);
4105- return false ;
4106- }
4129+ if (argument->type != Node::LITERAL) {
4130+ push_error (vformat ( R"( Argument %d of annotation "%s " must be a string literal.)" , i + 1 , p_annotation-> name ) , argument);
4131+ return false ;
4132+ }
41074133
4108- Variant value = static_cast <LiteralNode *>(argument)->value ;
4134+ Variant value = static_cast <LiteralNode *>(argument)->value ;
41094135
4110- if (value.get_type () != Variant::STRING) {
4111- push_error (R"( Argument 1 of annotation "@icon " must be a string literal.)" , argument);
4112- return false ;
4113- }
4136+ if (value.get_type () != Variant::STRING) {
4137+ push_error (vformat ( R"( Argument %d of annotation "%s " must be a string literal.)" , i + 1 , p_annotation-> name ) , argument);
4138+ return false ;
4139+ }
41144140
4115- p_annotation->resolved_arguments .push_back (value);
4141+ p_annotation->resolved_arguments .push_back (value);
4142+ }
41164143 }
41174144
41184145 // For other annotations, see `GDScriptAnalyzer::resolve_annotation()`.
@@ -4162,6 +4189,17 @@ bool GDScriptParser::icon_annotation(AnnotationNode *p_annotation, Node *p_targe
41624189 return true ;
41634190}
41644191
4192+ bool GDScriptParser::static_unload_annotation (AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
4193+ ERR_FAIL_COND_V_MSG (p_target->type != Node::CLASS, false , vformat (R"( "%s" annotation can only be applied to classes.)" , p_annotation->name ));
4194+ ClassNode *class_node = static_cast <ClassNode *>(p_target);
4195+ if (class_node->annotated_static_unload ) {
4196+ push_error (vformat (R"( "%s" annotation can only be used once per script.)" , p_annotation->name ), p_annotation);
4197+ return false ;
4198+ }
4199+ class_node->annotated_static_unload = true ;
4200+ return true ;
4201+ }
4202+
41654203bool GDScriptParser::onready_annotation (AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
41664204 ERR_FAIL_COND_V_MSG (p_target->type != Node::VARIABLE, false , R"( "@onready" annotation can only be applied to class variables.)" );
41674205
@@ -4756,11 +4794,8 @@ bool GDScriptParser::export_group_annotations(AnnotationNode *p_annotation, Node
47564794 return true ;
47574795}
47584796
4759- bool GDScriptParser::warning_annotations (AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
4760- #ifndef DEBUG_ENABLED
4761- // Only available in debug builds.
4762- return true ;
4763- #else // DEBUG_ENABLED
4797+ bool GDScriptParser::warning_ignore_annotation (AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
4798+ #ifdef DEBUG_ENABLED
47644799 if (is_ignoring_warnings) {
47654800 return true ; // We already ignore all warnings, let's optimize it.
47664801 }
@@ -4805,8 +4840,14 @@ bool GDScriptParser::warning_annotations(AnnotationNode *p_annotation, Node *p_t
48054840 } break ;
48064841
48074842 case Node::FUNCTION: {
4808- // `@warning_ignore` on function has a controversial feature that is used in tests.
4809- // It's better not to remove it for now, while there is no way to mass-ignore warnings.
4843+ FunctionNode *function = static_cast <FunctionNode *>(p_target);
4844+ end_line = function->start_line ;
4845+ for (int i = 0 ; i < function->parameters .size (); i++) {
4846+ end_line = MAX (end_line, function->parameters [i]->end_line );
4847+ if (function->parameters [i]->initializer != nullptr ) {
4848+ end_line = MAX (end_line, function->parameters [i]->initializer ->end_line );
4849+ }
4850+ }
48104851 } break ;
48114852
48124853 case Node::MATCH_BRANCH: {
@@ -4828,6 +4869,48 @@ bool GDScriptParser::warning_annotations(AnnotationNode *p_annotation, Node *p_t
48284869 }
48294870 }
48304871 return !has_error;
4872+ #else // !DEBUG_ENABLED
4873+ // Only available in debug builds.
4874+ return true ;
4875+ #endif // DEBUG_ENABLED
4876+ }
4877+
4878+ bool GDScriptParser::warning_ignore_region_annotations (AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
4879+ #ifdef DEBUG_ENABLED
4880+ bool has_error = false ;
4881+ const bool is_start = p_annotation->name == SNAME (" @warning_ignore_start" );
4882+ for (const Variant &warning_name : p_annotation->resolved_arguments ) {
4883+ GDScriptWarning::Code warning_code = GDScriptWarning::get_code_from_name (String (warning_name).to_upper ());
4884+ if (warning_code == GDScriptWarning::WARNING_MAX) {
4885+ push_error (vformat (R"( Invalid warning name: "%s".)" , warning_name), p_annotation);
4886+ has_error = true ;
4887+ continue ;
4888+ }
4889+ if (is_start) {
4890+ if (warning_ignore_start_lines[warning_code] != INT_MAX) {
4891+ push_error (vformat (R"( Warning "%s" is already being ignored by "@warning_ignore_start" at line %d.)" , String (warning_name).to_upper (), warning_ignore_start_lines[warning_code]), p_annotation);
4892+ has_error = true ;
4893+ continue ;
4894+ }
4895+ warning_ignore_start_lines[warning_code] = p_annotation->start_line ;
4896+ } else {
4897+ if (warning_ignore_start_lines[warning_code] == INT_MAX) {
4898+ push_error (vformat (R"( Warning "%s" is not being ignored by "@warning_ignore_start".)" , String (warning_name).to_upper ()), p_annotation);
4899+ has_error = true ;
4900+ continue ;
4901+ }
4902+ const int start_line = warning_ignore_start_lines[warning_code];
4903+ const int end_line = MAX (start_line, p_annotation->start_line ); // Prevent infinite loop.
4904+ for (int i = start_line; i <= end_line; i++) {
4905+ warning_ignored_lines[warning_code].insert (i);
4906+ }
4907+ warning_ignore_start_lines[warning_code] = INT_MAX;
4908+ }
4909+ }
4910+ return !has_error;
4911+ #else // !DEBUG_ENABLED
4912+ // Only available in debug builds.
4913+ return true ;
48314914#endif // DEBUG_ENABLED
48324915}
48334916
@@ -4892,17 +4975,6 @@ bool GDScriptParser::rpc_annotation(AnnotationNode *p_annotation, Node *p_target
48924975 return true ;
48934976}
48944977
4895- bool GDScriptParser::static_unload_annotation (AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
4896- ERR_FAIL_COND_V_MSG (p_target->type != Node::CLASS, false , vformat (R"( "%s" annotation can only be applied to classes.)" , p_annotation->name ));
4897- ClassNode *class_node = static_cast <ClassNode *>(p_target);
4898- if (class_node->annotated_static_unload ) {
4899- push_error (vformat (R"( "%s" annotation can only be used once per script.)" , p_annotation->name ), p_annotation);
4900- return false ;
4901- }
4902- class_node->annotated_static_unload = true ;
4903- return true ;
4904- }
4905-
49064978GDScriptParser::DataType GDScriptParser::SuiteNode::Local::get_datatype () const {
49074979 switch (type) {
49084980 case CONSTANT:
0 commit comments