Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDScript: Fix some export annotation issues #90716

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion editor/editor_help.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1967,7 +1967,7 @@ void EditorHelp::_update_doc() {

class_desc->add_text(argument.name);
class_desc->add_text(": ");
_add_type(argument.type);
_add_type(argument.type, argument.enumeration, argument.is_bitfield);

if (!argument.default_value.is_empty()) {
class_desc->push_color(theme_cache.symbol_color);
Expand Down
3 changes: 2 additions & 1 deletion modules/gdscript/doc_classes/@GDScript.xml
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,11 @@
<param index="1" name="hint_string" type="String" />
<param index="2" name="usage" type="int" enum="PropertyUsageFlags" is_bitfield="true" default="6" />
<description>
Allows you to set a custom hint, hint string, and usage flags for the exported property. Note that there's no validation done in GDScript, it will just pass the hint along to the editor.
Allows you to set a custom hint, hint string, and usage flags for the exported property. Note that there's no validation done in GDScript, it will just pass the parameters to the editor.
[codeblock]
@export_custom(PROPERTY_HINT_NONE, "suffix:m") var suffix: Vector3
[/codeblock]
[b]Note:[/b] Regardless of the [param usage] value, the [constant PROPERTY_USAGE_SCRIPT_VARIABLE] flag is always added, as with any explicitly declared script variable.
</description>
</annotation>
<annotation name="@export_dir">
Expand Down
23 changes: 23 additions & 0 deletions modules/gdscript/gdscript_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,29 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
option.insert_text = option.display.quote(p_quote_style);
r_result.insert(option.display, option);
}
} else if (p_annotation->name == SNAME("@export_custom")) {
switch (p_argument) {
case 0: {
static HashMap<StringName, int64_t> items;
if (unlikely(items.is_empty())) {
CoreConstants::get_enum_values(SNAME("PropertyHint"), &items);
}
for (const KeyValue<StringName, int64_t> &item : items) {
ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
r_result.insert(option.display, option);
}
} break;
case 2: {
static HashMap<StringName, int64_t> items;
if (unlikely(items.is_empty())) {
CoreConstants::get_enum_values(SNAME("PropertyUsageFlags"), &items);
}
for (const KeyValue<StringName, int64_t> &item : items) {
ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);
r_result.insert(option.display, option);
}
} break;
}
} else if (p_annotation->name == SNAME("@warning_ignore")) {
for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {
ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
Expand Down
43 changes: 34 additions & 9 deletions modules/gdscript/gdscript_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
// Export annotations.
register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
register_annotation(MethodInfo("@export_enum", PropertyInfo(Variant::STRING, "names")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::NIL>, varray(), true);
register_annotation(MethodInfo("@export_file", PropertyInfo(Variant::STRING, "filter")), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, varray(""), true);
register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
Expand All @@ -121,6 +120,7 @@ GDScriptParser::GDScriptParser() {
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_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_storage_annotation);
register_annotation(MethodInfo("@export_custom", PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_ENUM, "PropertyHint"), PropertyInfo(Variant::STRING, "hint_string"), PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_BITFIELD, "PropertyUsageFlags")), AnnotationInfo::VARIABLE, &GDScriptParser::export_custom_annotation, varray(PROPERTY_USAGE_DEFAULT));
// Export grouping annotations.
register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
Expand Down Expand Up @@ -4295,7 +4295,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
case GDScriptParser::DataType::BUILTIN:
variable->export_info.type = export_type.builtin_type;
variable->export_info.hint = PROPERTY_HINT_NONE;
variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type);
variable->export_info.hint_string = String();
break;
case GDScriptParser::DataType::NATIVE:
if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
Expand Down Expand Up @@ -4396,12 +4396,6 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
push_error(_get_annotation_error_string(p_annotation->name, expected_types, variable->get_datatype()), p_annotation);
return false;
}
} else if (p_annotation->name == SNAME("@export_storage")) {
use_default_variable_type_check = false; // Can be applied to a variable of any type.

// Save the info because the compiler uses export info for overwriting member info.
variable->export_info = export_type.to_property_info(variable->identifier->name);
variable->export_info.usage |= PROPERTY_USAGE_STORAGE;
}

if (use_default_variable_type_check) {
Expand All @@ -4421,11 +4415,38 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
if (variable->export_info.hint) {
hint_prefix += "/" + itos(variable->export_info.hint);
}
variable->export_info.type = original_export_type_builtin;
variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
variable->export_info.type = original_export_type_builtin;
variable->export_info.usage = PROPERTY_USAGE_DEFAULT;
variable->export_info.class_name = StringName();
}

return true;
}

// For `@export_storage` and `@export_custom`, there is no need to check the variable type, argument values,
// or handle array exports in a special way, so they are implemented as separate methods.

bool GDScriptParser::export_storage_annotation(const AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));

VariableNode *variable = static_cast<VariableNode *>(p_node);
if (variable->is_static) {
push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
return false;
}
if (variable->exported) {
push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
return false;
}

variable->exported = true;

// Save the info because the compiler uses export info for overwriting member info.
variable->export_info = variable->get_datatype().to_property_info(variable->identifier->name);
variable->export_info.usage |= PROPERTY_USAGE_STORAGE;

return true;
}

Expand All @@ -4434,6 +4455,10 @@ bool GDScriptParser::export_custom_annotation(const AnnotationNode *p_annotation
ERR_FAIL_COND_V_MSG(p_annotation->resolved_arguments.size() < 2, false, R"(Annotation "@export_custom" requires 2 arguments.)");

VariableNode *variable = static_cast<VariableNode *>(p_node);
if (variable->is_static) {
push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
return false;
}
if (variable->exported) {
push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
return false;
Expand Down
1 change: 1 addition & 0 deletions modules/gdscript/gdscript_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,7 @@ class GDScriptParser {
bool onready_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
template <PropertyHint t_hint, Variant::Type t_type>
bool export_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool export_storage_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool export_custom_annotation(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
template <PropertyUsageFlags t_usage>
bool export_group_annotations(const AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
GDTEST_OK
var test_1: Dictionary
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_2: TestExportEnumAsDictionary.MyEnum
hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM
hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"TestExportEnumAsDictionary.MyEnum"
var test_3: Dictionary
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_4: TestExportEnumAsDictionary.MyEnum
hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM
hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"TestExportEnumAsDictionary.MyEnum"
var test_5: TestExportEnumAsDictionary.MyEnum
hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM
hint=ENUM hint_string="A:0,B:1,C:2" usage=DEFAULT|SCRIPT_VARIABLE|CLASS_IS_ENUM class_name=&"TestExportEnumAsDictionary.MyEnum"
24 changes: 12 additions & 12 deletions modules/gdscript/tests/scripts/parser/features/annotations.out
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
GDTEST_OK
var test_1: int = null
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_2: int = null
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_3: int = null
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_4: int = null
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_5: int = 0
hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_6: int = 0
hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_7: int = 42
hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_8: int = 0
hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_9: int = 0
hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_10: int = 0
hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_11: int = 0
hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_12: int = 0
hint=NONE hint_string="int" usage=DEFAULT|SCRIPT_VARIABLE
hint=NONE hint_string="" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
Loading
Loading