Add grouping annotations for class properties in GDScript
This commit is contained in:
parent
b4644e2835
commit
f85bafaa11
8 changed files with 123 additions and 15 deletions
|
@ -860,6 +860,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
|
||||||
case GDScriptParser::ClassNode::Member::CLASS:
|
case GDScriptParser::ClassNode::Member::CLASS:
|
||||||
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
|
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
|
||||||
break;
|
break;
|
||||||
|
case GDScriptParser::ClassNode::Member::GROUP:
|
||||||
|
// No-op, but needed to silence warnings.
|
||||||
|
break;
|
||||||
case GDScriptParser::ClassNode::Member::UNDEFINED:
|
case GDScriptParser::ClassNode::Member::UNDEFINED:
|
||||||
ERR_PRINT("Trying to resolve undefined member.");
|
ERR_PRINT("Trying to resolve undefined member.");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -2452,6 +2452,25 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case GDScriptParser::ClassNode::Member::GROUP: {
|
||||||
|
const GDScriptParser::AnnotationNode *annotation = member.annotation;
|
||||||
|
StringName name = annotation->export_info.name;
|
||||||
|
|
||||||
|
// This is not a normal member, but we need this to keep indices in order.
|
||||||
|
GDScript::MemberInfo minfo;
|
||||||
|
minfo.index = p_script->member_indices.size();
|
||||||
|
|
||||||
|
PropertyInfo prop_info;
|
||||||
|
prop_info.name = name;
|
||||||
|
prop_info.usage = annotation->export_info.usage;
|
||||||
|
prop_info.hint_string = annotation->export_info.hint_string;
|
||||||
|
|
||||||
|
p_script->member_info[name] = prop_info;
|
||||||
|
p_script->member_indices[name] = minfo;
|
||||||
|
p_script->members.insert(name);
|
||||||
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break; // Nothing to do here.
|
break; // Nothing to do here.
|
||||||
}
|
}
|
||||||
|
|
|
@ -950,6 +950,8 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class,
|
||||||
}
|
}
|
||||||
option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
|
option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);
|
||||||
break;
|
break;
|
||||||
|
case GDScriptParser::ClassNode::Member::GROUP:
|
||||||
|
break; // No-op, but silences warnings.
|
||||||
case GDScriptParser::ClassNode::Member::UNDEFINED:
|
case GDScriptParser::ClassNode::Member::UNDEFINED:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2082,6 +2084,8 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
|
||||||
r_type.type.kind = GDScriptParser::DataType::CLASS;
|
r_type.type.kind = GDScriptParser::DataType::CLASS;
|
||||||
r_type.type.class_type = member.m_class;
|
r_type.type.class_type = member.m_class;
|
||||||
return true;
|
return true;
|
||||||
|
case GDScriptParser::ClassNode::Member::GROUP:
|
||||||
|
return false; // No-op, but silences warnings.
|
||||||
case GDScriptParser::ClassNode::Member::UNDEFINED:
|
case GDScriptParser::ClassNode::Member::UNDEFINED:
|
||||||
return false; // Unreachable.
|
return false; // Unreachable.
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,6 +131,11 @@ GDScriptParser::GDScriptParser() {
|
||||||
register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
|
register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
|
||||||
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
|
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
|
||||||
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
|
register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
|
||||||
|
// Export grouping annotations.
|
||||||
|
register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
|
||||||
|
register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, 1);
|
||||||
|
register_annotation(MethodInfo("@export_subgroup", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_SUBGROUP>, 1);
|
||||||
|
// Warning annotations.
|
||||||
register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, 0, true);
|
register_annotation(MethodInfo("@warning_ignore", PropertyInfo(Variant::STRING, "warning")), AnnotationInfo::CLASS | AnnotationInfo::VARIABLE | AnnotationInfo::SIGNAL | AnnotationInfo::CONSTANT | AnnotationInfo::FUNCTION | AnnotationInfo::STATEMENT, &GDScriptParser::warning_annotations, 0, true);
|
||||||
// Networking.
|
// Networking.
|
||||||
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::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, 4, true);
|
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::network_annotations<Multiplayer::RPC_MODE_AUTHORITY>, 4, true);
|
||||||
|
@ -519,9 +524,13 @@ void GDScriptParser::parse_program() {
|
||||||
head = alloc_node<ClassNode>();
|
head = alloc_node<ClassNode>();
|
||||||
current_class = head;
|
current_class = head;
|
||||||
|
|
||||||
|
// If we happen to parse an annotation before extends or class_name keywords, track it.
|
||||||
|
// @tool is allowed, but others should fail.
|
||||||
|
AnnotationNode *premature_annotation = nullptr;
|
||||||
|
|
||||||
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
|
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
|
||||||
// Check for @tool annotation.
|
// Check for @tool, script-level, or standalone annotation.
|
||||||
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
|
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
|
||||||
if (annotation != nullptr) {
|
if (annotation != nullptr) {
|
||||||
if (annotation->name == SNAME("@tool")) {
|
if (annotation->name == SNAME("@tool")) {
|
||||||
// TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
|
// TODO: don't allow @tool anywhere else. (Should all script annotations be the first thing?).
|
||||||
|
@ -531,7 +540,14 @@ void GDScriptParser::parse_program() {
|
||||||
}
|
}
|
||||||
// @tool annotation has no specific target.
|
// @tool annotation has no specific target.
|
||||||
annotation->apply(this, nullptr);
|
annotation->apply(this, nullptr);
|
||||||
|
} else if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
|
||||||
|
premature_annotation = annotation;
|
||||||
|
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
|
||||||
|
push_error(R"(Expected newline after a standalone annotation.)");
|
||||||
|
}
|
||||||
|
annotation->apply(this, head);
|
||||||
} else {
|
} else {
|
||||||
|
premature_annotation = annotation;
|
||||||
annotation_stack.push_back(annotation);
|
annotation_stack.push_back(annotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,8 +557,8 @@ void GDScriptParser::parse_program() {
|
||||||
// Order here doesn't matter, but there should be only one of each at most.
|
// Order here doesn't matter, but there should be only one of each at most.
|
||||||
switch (current.type) {
|
switch (current.type) {
|
||||||
case GDScriptTokenizer::Token::CLASS_NAME:
|
case GDScriptTokenizer::Token::CLASS_NAME:
|
||||||
if (!annotation_stack.is_empty()) {
|
if (premature_annotation != nullptr) {
|
||||||
push_error(R"("class_name" should be used before annotations.)");
|
push_error(R"("class_name" should be used before annotations (except @tool).)");
|
||||||
}
|
}
|
||||||
advance();
|
advance();
|
||||||
if (head->identifier != nullptr) {
|
if (head->identifier != nullptr) {
|
||||||
|
@ -552,8 +568,8 @@ void GDScriptParser::parse_program() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GDScriptTokenizer::Token::EXTENDS:
|
case GDScriptTokenizer::Token::EXTENDS:
|
||||||
if (!annotation_stack.is_empty()) {
|
if (premature_annotation != nullptr) {
|
||||||
push_error(R"("extends" should be used before annotations.)");
|
push_error(R"("extends" should be used before annotations (except @tool).)");
|
||||||
}
|
}
|
||||||
advance();
|
advance();
|
||||||
if (head->extends_used) {
|
if (head->extends_used) {
|
||||||
|
@ -574,12 +590,12 @@ void GDScriptParser::parse_program() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
|
if (match(GDScriptTokenizer::Token::ANNOTATION)) {
|
||||||
// Check for @icon annotation.
|
// Check for a script-level, or standalone annotation.
|
||||||
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::CLASS_LEVEL);
|
AnnotationNode *annotation = parse_annotation(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
|
||||||
if (annotation != nullptr) {
|
if (annotation != nullptr) {
|
||||||
if (annotation->name == SNAME("@icon")) {
|
if (annotation->applies_to(AnnotationInfo::SCRIPT | AnnotationInfo::STANDALONE)) {
|
||||||
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
|
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
|
||||||
push_error(R"(Expected newline after "@icon" annotation.)");
|
push_error(R"(Expected newline after a standalone annotation.)");
|
||||||
}
|
}
|
||||||
annotation->apply(this, head);
|
annotation->apply(this, head);
|
||||||
} else {
|
} else {
|
||||||
|
@ -807,9 +823,18 @@ void GDScriptParser::parse_class_body(bool p_is_multiline) {
|
||||||
break;
|
break;
|
||||||
case GDScriptTokenizer::Token::ANNOTATION: {
|
case GDScriptTokenizer::Token::ANNOTATION: {
|
||||||
advance();
|
advance();
|
||||||
AnnotationNode *annotation = parse_annotation(AnnotationInfo::CLASS_LEVEL);
|
|
||||||
|
// Check for class-level annotations.
|
||||||
|
AnnotationNode *annotation = parse_annotation(AnnotationInfo::STANDALONE | AnnotationInfo::CLASS_LEVEL);
|
||||||
if (annotation != nullptr) {
|
if (annotation != nullptr) {
|
||||||
annotation_stack.push_back(annotation);
|
if (annotation->applies_to(AnnotationInfo::STANDALONE)) {
|
||||||
|
if (previous.type != GDScriptTokenizer::Token::NEWLINE) {
|
||||||
|
push_error(R"(Expected newline after a standalone annotation.)");
|
||||||
|
}
|
||||||
|
annotation->apply(this, head);
|
||||||
|
} else {
|
||||||
|
annotation_stack.push_back(annotation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3662,6 +3687,36 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <PropertyUsageFlags t_usage>
|
||||||
|
bool GDScriptParser::export_group_annotations(const AnnotationNode *p_annotation, Node *p_node) {
|
||||||
|
AnnotationNode *annotation = const_cast<AnnotationNode *>(p_annotation);
|
||||||
|
|
||||||
|
annotation->export_info.name = annotation->resolved_arguments[0];
|
||||||
|
|
||||||
|
switch (t_usage) {
|
||||||
|
case PROPERTY_USAGE_CATEGORY: {
|
||||||
|
annotation->export_info.usage = t_usage;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROPERTY_USAGE_GROUP: {
|
||||||
|
annotation->export_info.usage = t_usage;
|
||||||
|
if (annotation->resolved_arguments.size() == 2) {
|
||||||
|
annotation->export_info.hint_string = annotation->resolved_arguments[1];
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROPERTY_USAGE_SUBGROUP: {
|
||||||
|
annotation->export_info.usage = t_usage;
|
||||||
|
if (annotation->resolved_arguments.size() == 2) {
|
||||||
|
annotation->export_info.hint_string = annotation->resolved_arguments[1];
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_class->add_member_group(annotation);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_node) {
|
bool GDScriptParser::warning_annotations(const AnnotationNode *p_annotation, Node *p_node) {
|
||||||
#ifdef DEBUG_ENABLED
|
#ifdef DEBUG_ENABLED
|
||||||
bool has_error = false;
|
bool has_error = false;
|
||||||
|
@ -4145,6 +4200,8 @@ void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) {
|
||||||
break;
|
break;
|
||||||
case ClassNode::Member::ENUM_VALUE:
|
case ClassNode::Member::ENUM_VALUE:
|
||||||
break; // Nothing. Will be printed by enum.
|
break; // Nothing. Will be printed by enum.
|
||||||
|
case ClassNode::Member::GROUP:
|
||||||
|
break; // Nothing. Groups are only used by inspector.
|
||||||
case ClassNode::Member::UNDEFINED:
|
case ClassNode::Member::UNDEFINED:
|
||||||
push_line("<unknown member>");
|
push_line("<unknown member>");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -325,6 +325,7 @@ public:
|
||||||
Vector<Variant> resolved_arguments;
|
Vector<Variant> resolved_arguments;
|
||||||
|
|
||||||
AnnotationInfo *info = nullptr;
|
AnnotationInfo *info = nullptr;
|
||||||
|
PropertyInfo export_info;
|
||||||
|
|
||||||
bool apply(GDScriptParser *p_this, Node *p_target) const;
|
bool apply(GDScriptParser *p_this, Node *p_target) const;
|
||||||
bool applies_to(uint32_t p_target_kinds) const;
|
bool applies_to(uint32_t p_target_kinds) const;
|
||||||
|
@ -500,6 +501,7 @@ public:
|
||||||
VARIABLE,
|
VARIABLE,
|
||||||
ENUM,
|
ENUM,
|
||||||
ENUM_VALUE, // For unnamed enums.
|
ENUM_VALUE, // For unnamed enums.
|
||||||
|
GROUP, // For member grouping.
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type = UNDEFINED;
|
Type type = UNDEFINED;
|
||||||
|
@ -511,6 +513,7 @@ public:
|
||||||
SignalNode *signal;
|
SignalNode *signal;
|
||||||
VariableNode *variable;
|
VariableNode *variable;
|
||||||
EnumNode *m_enum;
|
EnumNode *m_enum;
|
||||||
|
AnnotationNode *annotation;
|
||||||
};
|
};
|
||||||
EnumNode::Value enum_value;
|
EnumNode::Value enum_value;
|
||||||
|
|
||||||
|
@ -532,6 +535,8 @@ public:
|
||||||
return "enum";
|
return "enum";
|
||||||
case ENUM_VALUE:
|
case ENUM_VALUE:
|
||||||
return "enum value";
|
return "enum value";
|
||||||
|
case GROUP:
|
||||||
|
return "group";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -552,6 +557,8 @@ public:
|
||||||
return m_enum->start_line;
|
return m_enum->start_line;
|
||||||
case SIGNAL:
|
case SIGNAL:
|
||||||
return signal->start_line;
|
return signal->start_line;
|
||||||
|
case GROUP:
|
||||||
|
return annotation->start_line;
|
||||||
case UNDEFINED:
|
case UNDEFINED:
|
||||||
ERR_FAIL_V_MSG(-1, "Reaching undefined member type.");
|
ERR_FAIL_V_MSG(-1, "Reaching undefined member type.");
|
||||||
}
|
}
|
||||||
|
@ -586,6 +593,9 @@ public:
|
||||||
// TODO: Add parameter info.
|
// TODO: Add parameter info.
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
case GROUP: {
|
||||||
|
return DataType();
|
||||||
|
}
|
||||||
case UNDEFINED:
|
case UNDEFINED:
|
||||||
return DataType();
|
return DataType();
|
||||||
}
|
}
|
||||||
|
@ -622,6 +632,10 @@ public:
|
||||||
type = ENUM_VALUE;
|
type = ENUM_VALUE;
|
||||||
enum_value = p_enum_value;
|
enum_value = p_enum_value;
|
||||||
}
|
}
|
||||||
|
Member(AnnotationNode *p_annotation) {
|
||||||
|
type = GROUP;
|
||||||
|
annotation = p_annotation;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
IdentifierNode *identifier = nullptr;
|
IdentifierNode *identifier = nullptr;
|
||||||
|
@ -668,6 +682,10 @@ public:
|
||||||
members_indices[p_enum_value.identifier->name] = members.size();
|
members_indices[p_enum_value.identifier->name] = members.size();
|
||||||
members.push_back(Member(p_enum_value));
|
members.push_back(Member(p_enum_value));
|
||||||
}
|
}
|
||||||
|
void add_member_group(AnnotationNode *p_annotation_node) {
|
||||||
|
members_indices[p_annotation_node->export_info.name] = members.size();
|
||||||
|
members.push_back(Member(p_annotation_node));
|
||||||
|
}
|
||||||
|
|
||||||
ClassNode() {
|
ClassNode() {
|
||||||
type = CLASS;
|
type = CLASS;
|
||||||
|
@ -1238,6 +1256,7 @@ private:
|
||||||
SIGNAL = 1 << 4,
|
SIGNAL = 1 << 4,
|
||||||
FUNCTION = 1 << 5,
|
FUNCTION = 1 << 5,
|
||||||
STATEMENT = 1 << 6,
|
STATEMENT = 1 << 6,
|
||||||
|
STANDALONE = 1 << 7,
|
||||||
CLASS_LEVEL = CLASS | VARIABLE | FUNCTION,
|
CLASS_LEVEL = CLASS | VARIABLE | FUNCTION,
|
||||||
};
|
};
|
||||||
uint32_t target_kind = 0; // Flags.
|
uint32_t target_kind = 0; // Flags.
|
||||||
|
@ -1348,6 +1367,8 @@ private:
|
||||||
bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target);
|
bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target);
|
||||||
template <PropertyHint t_hint, Variant::Type t_type>
|
template <PropertyHint t_hint, Variant::Type t_type>
|
||||||
bool export_annotations(const AnnotationNode *p_annotation, Node *p_target);
|
bool export_annotations(const AnnotationNode *p_annotation, Node *p_target);
|
||||||
|
template <PropertyUsageFlags t_usage>
|
||||||
|
bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target);
|
||||||
bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target);
|
bool warning_annotations(const AnnotationNode *p_annotation, Node *p_target);
|
||||||
template <Multiplayer::RPCMode t_mode>
|
template <Multiplayer::RPCMode t_mode>
|
||||||
bool network_annotations(const AnnotationNode *p_annotation, Node *p_target);
|
bool network_annotations(const AnnotationNode *p_annotation, Node *p_target);
|
||||||
|
|
|
@ -307,6 +307,8 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
|
||||||
parse_class_symbol(m.m_class, symbol);
|
parse_class_symbol(m.m_class, symbol);
|
||||||
r_symbol.children.push_back(symbol);
|
r_symbol.children.push_back(symbol);
|
||||||
} break;
|
} break;
|
||||||
|
case ClassNode::Member::GROUP:
|
||||||
|
break; // No-op, but silences warnings.
|
||||||
case ClassNode::Member::UNDEFINED:
|
case ClassNode::Member::UNDEFINED:
|
||||||
break; // Unreachable.
|
break; // Unreachable.
|
||||||
}
|
}
|
||||||
|
@ -815,6 +817,8 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
|
||||||
methods.append(dump_function_api(m.function));
|
methods.append(dump_function_api(m.function));
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
case ClassNode::Member::GROUP:
|
||||||
|
break; // No-op, but silences warnings.
|
||||||
case ClassNode::Member::UNDEFINED:
|
case ClassNode::Member::UNDEFINED:
|
||||||
break; // Unreachable.
|
break; // Unreachable.
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Error here. `class_name` should be used *before* annotations, not after.
|
# Error here. `class_name` should be used *before* annotations, not after (except @tool).
|
||||||
@icon("res://path/to/optional/icon.svg")
|
@icon("res://path/to/optional/icon.svg")
|
||||||
class_name HelloWorld
|
class_name HelloWorld
|
||||||
|
|
||||||
func test():
|
func test():
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
GDTEST_PARSER_ERROR
|
GDTEST_PARSER_ERROR
|
||||||
"class_name" should be used before annotations.
|
"class_name" should be used before annotations (except @tool).
|
||||||
|
|
Loading…
Reference in a new issue