| 948 | |
| 949 | template <typename T> |
| 950 | void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)(bool), AnnotationInfo::TargetKind p_target, const String &p_member_kind, bool p_is_static) { |
| 951 | advance(); |
| 952 | |
| 953 | // Consume annotations. |
| 954 | List<AnnotationNode *> annotations; |
| 955 | while (!annotation_stack.is_empty()) { |
| 956 | AnnotationNode *last_annotation = annotation_stack.back()->get(); |
| 957 | if (last_annotation->applies_to(p_target)) { |
| 958 | annotations.push_front(last_annotation); |
| 959 | annotation_stack.pop_back(); |
| 960 | } else { |
| 961 | push_error(vformat(R"(Annotation "%s" cannot be applied to a %s.)", last_annotation->name, p_member_kind)); |
| 962 | clear_unused_annotations(); |
| 963 | } |
| 964 | } |
| 965 | |
| 966 | T *member = (this->*p_parse_function)(p_is_static); |
| 967 | if (member == nullptr) { |
| 968 | return; |
| 969 | } |
| 970 | |
| 971 | #ifdef TOOLS_ENABLED |
| 972 | int doc_comment_line = member->start_line - 1; |
| 973 | #endif // TOOLS_ENABLED |
| 974 | |
| 975 | for (AnnotationNode *&annotation : annotations) { |
| 976 | member->annotations.push_back(annotation); |
| 977 | #ifdef TOOLS_ENABLED |
| 978 | if (annotation->start_line <= doc_comment_line) { |
| 979 | doc_comment_line = annotation->start_line - 1; |
| 980 | } |
| 981 | #endif // TOOLS_ENABLED |
| 982 | } |
| 983 | |
| 984 | #ifdef TOOLS_ENABLED |
| 985 | if constexpr (std::is_same_v<T, ClassNode>) { |
| 986 | if (has_comment(member->start_line, true)) { |
| 987 | // Inline doc comment. |
| 988 | member->doc_data = parse_class_doc_comment(member->start_line, true); |
| 989 | } else if (has_comment(doc_comment_line, true) && tokenizer->get_comments()[doc_comment_line].new_line) { |
| 990 | // Normal doc comment. Don't check `min_member_doc_line` because a class ends parsing after its members. |
| 991 | // This may not work correctly for cases like `var a; class B`, but it doesn't matter in practice. |
| 992 | member->doc_data = parse_class_doc_comment(doc_comment_line); |
| 993 | } |
| 994 | } else { |
| 995 | if (has_comment(member->start_line, true)) { |
| 996 | // Inline doc comment. |
| 997 | member->doc_data = parse_doc_comment(member->start_line, true); |
| 998 | } else if (doc_comment_line >= min_member_doc_line && has_comment(doc_comment_line, true) && tokenizer->get_comments()[doc_comment_line].new_line) { |
| 999 | // Normal doc comment. |
| 1000 | member->doc_data = parse_doc_comment(doc_comment_line); |
| 1001 | } |
| 1002 | } |
| 1003 | |
| 1004 | min_member_doc_line = member->end_line + 1; // Prevent multiple members from using the same doc comment. |
| 1005 | #endif // TOOLS_ENABLED |
| 1006 | |
| 1007 | if (member->identifier != nullptr) { |
nothing calls this directly
no test coverage detected