summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
authorDanil Alexeev <danil@alexeev.xyz>2023-02-01 10:28:20 +0300
committerDanil Alexeev <danil@alexeev.xyz>2023-02-07 19:32:08 +0300
commitc8e3d8b5d5d4e82c4337ae150560615bfc38850c (patch)
tree36acd94da5f8113205c69a3ced171bb93d4bc26a /modules/gdscript
parent929333fe267f488638c76564237faff9d5d572fc (diff)
GDScript: Improve validation and documentation of `@export_flags`
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml16
-rw-r--r--modules/gdscript/gdscript_parser.cpp53
2 files changed, 55 insertions, 14 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index e05b17168d..923b2fe30d 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -304,7 +304,7 @@
<return type="void" />
<param index="0" name="names" type="String" />
<description>
- Export an [int] or [String] property as an enumerated list of options. If the property is an [int], then the index of the value is stored, in the same order the values are provided. You can add specific identifiers for allowed values using a colon. If the property is a [String], then the value is stored.
+ Export an [int] or [String] property as an enumerated list of options. If the property is an [int], then the index of the value is stored, in the same order the values are provided. You can add explicit values using a colon. If the property is a [String], then the value is stored.
See also [constant PROPERTY_HINT_ENUM].
[codeblock]
@export_enum("Warrior", "Magician", "Thief") var character_class: int
@@ -357,6 +357,20 @@
[codeblock]
@export_flags("Fire", "Water", "Earth", "Wind") var spell_elements = 0
[/codeblock]
+ You can add explicit values using a colon:
+ [codeblock]
+ @export_flags("Self:4", "Allies:8", "Foes:16") var spell_targets = 0
+ [/codeblock]
+ You can also combine several flags:
+ [codeblock]
+ @export_flags("Self:4", "Allies:8", "Self and Allies:12", "Foes:16")
+ var spell_targets = 0
+ [/codeblock]
+ [b]Note:[/b] A flag value must be at least [code]1[/code] and at most [code]2 ** 32 - 1[/code].
+ [b]Note:[/b] Unlike [annotation @export_enum], the previous explicit value is not taken into account. In the following example, A is 16, B is 2, C is 4.
+ [codeblock]
+ @export_flags("A:16", "B", "C") var x
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_flags_2d_navigation">
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 0a1a64cb59..c6e4222213 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -3637,15 +3637,6 @@ template <PropertyHint t_hint, Variant::Type t_type>
bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node *p_node) {
ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
- {
- const int max_flags = 32;
-
- if (t_hint == PropertyHint::PROPERTY_HINT_FLAGS && p_annotation->resolved_arguments.size() > max_flags) {
- push_error(vformat(R"(The argument count limit for "@export_flags" is exceeded (%d/%d).)", p_annotation->resolved_arguments.size(), max_flags), p_annotation);
- return false;
- }
- }
-
VariableNode *variable = static_cast<VariableNode *>(p_node);
if (variable->exported) {
push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
@@ -3659,14 +3650,50 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
String hint_string;
for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) {
- if (p_annotation->name != SNAME("@export_placeholder") && String(p_annotation->resolved_arguments[i]).contains(",")) {
- push_error(vformat(R"(Argument %d of annotation "%s" contains a comma. Use separate arguments instead.)", i + 1, p_annotation->name), p_annotation->arguments[i]);
- return false;
+ String arg_string = String(p_annotation->resolved_arguments[i]);
+
+ if (p_annotation->name != SNAME("@export_placeholder")) {
+ if (arg_string.is_empty()) {
+ push_error(vformat(R"(Argument %d of annotation "%s" is empty.)", i + 1, p_annotation->name), p_annotation->arguments[i]);
+ return false;
+ }
+ if (arg_string.contains(",")) {
+ push_error(vformat(R"(Argument %d of annotation "%s" contains a comma. Use separate arguments instead.)", i + 1, p_annotation->name), p_annotation->arguments[i]);
+ return false;
+ }
+ }
+
+ if (p_annotation->name == SNAME("@export_flags")) {
+ const int64_t max_flags = 32;
+ Vector<String> t = arg_string.split(":", true, 1);
+ if (t[0].is_empty()) {
+ push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Expected flag name.)", i + 1), p_annotation->arguments[i]);
+ return false;
+ }
+ if (t.size() == 2) {
+ if (t[1].is_empty()) {
+ push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Expected flag value.)", i + 1), p_annotation->arguments[i]);
+ return false;
+ }
+ if (!t[1].is_valid_int()) {
+ push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": The flag value must be a valid integer.)", i + 1), p_annotation->arguments[i]);
+ return false;
+ }
+ int64_t value = t[1].to_int();
+ if (value < 1 || value >= (1LL << max_flags)) {
+ push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": The flag value must be at least 1 and at most 2 ** %d - 1.)", i + 1, max_flags), p_annotation->arguments[i]);
+ return false;
+ }
+ } else if (i >= max_flags) {
+ push_error(vformat(R"(Invalid argument %d of annotation "@export_flags": Starting from argument %d, the flag value must be specified explicitly.)", i + 1, max_flags + 1), p_annotation->arguments[i]);
+ return false;
+ }
}
+
if (i > 0) {
hint_string += ",";
}
- hint_string += String(p_annotation->resolved_arguments[i]);
+ hint_string += arg_string;
}
variable->export_info.hint_string = hint_string;