| 1727 | } |
| 1728 | |
| 1729 | GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_valid_targets) { |
| 1730 | AnnotationNode *annotation = alloc_node<AnnotationNode>(); |
| 1731 | |
| 1732 | annotation->name = previous.literal; |
| 1733 | |
| 1734 | make_completion_context(COMPLETION_ANNOTATION, annotation); |
| 1735 | |
| 1736 | bool valid = true; |
| 1737 | |
| 1738 | if (!valid_annotations.has(annotation->name)) { |
| 1739 | if (annotation->name == "@deprecated") { |
| 1740 | push_error(R"("@deprecated" annotation does not exist. Use "## @deprecated: Reason here." instead.)"); |
| 1741 | } else if (annotation->name == "@experimental") { |
| 1742 | push_error(R"("@experimental" annotation does not exist. Use "## @experimental: Reason here." instead.)"); |
| 1743 | } else if (annotation->name == "@tutorial") { |
| 1744 | push_error(R"("@tutorial" annotation does not exist. Use "## @tutorial(Title): https://example.com" instead.)"); |
| 1745 | } else { |
| 1746 | push_error(vformat(R"(Unrecognized annotation: "%s".)", annotation->name)); |
| 1747 | } |
| 1748 | valid = false; |
| 1749 | } |
| 1750 | |
| 1751 | if (valid) { |
| 1752 | annotation->info = &valid_annotations[annotation->name]; |
| 1753 | |
| 1754 | if (!annotation->applies_to(p_valid_targets)) { |
| 1755 | if (annotation->applies_to(AnnotationInfo::SCRIPT)) { |
| 1756 | push_error(vformat(R"(Annotation "%s" must be at the top of the script, before "extends" and "class_name".)", annotation->name)); |
| 1757 | } else { |
| 1758 | push_error(vformat(R"(Annotation "%s" is not allowed in this level.)", annotation->name)); |
| 1759 | } |
| 1760 | valid = false; |
| 1761 | } |
| 1762 | } |
| 1763 | |
| 1764 | if (check(GDScriptTokenizer::Token::PARENTHESIS_OPEN)) { |
| 1765 | push_multiline(true); |
| 1766 | advance(); |
| 1767 | // Arguments. |
| 1768 | push_completion_call(annotation); |
| 1769 | int argument_index = 0; |
| 1770 | do { |
| 1771 | make_completion_context(COMPLETION_ANNOTATION_ARGUMENTS, annotation, argument_index); |
| 1772 | set_last_completion_call_arg(argument_index); |
| 1773 | if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) { |
| 1774 | // Allow for trailing comma. |
| 1775 | break; |
| 1776 | } |
| 1777 | |
| 1778 | ExpressionNode *argument = parse_expression(false); |
| 1779 | |
| 1780 | if (argument == nullptr) { |
| 1781 | push_error("Expected expression as the annotation argument."); |
| 1782 | valid = false; |
| 1783 | } else { |
| 1784 | annotation->arguments.push_back(argument); |
| 1785 | |
| 1786 | if (argument->type == Node::LITERAL) { |
nothing calls this directly
no test coverage detected