From aadd3191ee701a96a66c58f324b791cf57c5044d Mon Sep 17 00:00:00 2001 From: Arctis-Fireblight <6182060+Arctis-Fireblight@users.noreply.github.com> Date: Wed, 17 Jun 2026 17:01:43 -0500 Subject: [PATCH] Add support for parsing `Variant::NIL` and handling related cases in `VariantParser` - Implemented parsing logic for `Variant::NIL` and error handling for invalid constants. - Added tests to validate `Variant::NIL`, `null`, and `nil` parsing, including regression cases. - Improved security gating for object parsing with `p_allow_objects`. - Enhanced `str_to_var` to support `Variant::NIL`. --- core/variant/variant_parser.cpp | 36 ++++++++++++++ tests/core/variant/test_variant.h | 79 +++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index f8bc24e2f81..10c4692bda6 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -714,6 +714,42 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, value = -Math::INF; } else if (id == "nan") { value = Math::NaN; + } else if (id == "Variant") { + Error err = get_token(p_stream, token, line, r_err_str); + if (err) { + return err; + } + if (token.type != TK_COLON) { + r_err_str = "Expected '::' after 'Variant'"; + return ERR_PARSE_ERROR; + } + + err = get_token(p_stream, token, line, r_err_str); + if (err) { + return err; + } + if (token.type != TK_COLON) { + r_err_str = "Expected '::' after 'Variant'"; + return ERR_PARSE_ERROR; + } + + err = get_token(p_stream, token, line, r_err_str); + if (err) { + return err; + } + + if (token.type != TK_IDENTIFIER) { + r_err_str = "Expected identifier after 'Variant::'"; + return ERR_PARSE_ERROR; + } + + String constant = token.value; + if (constant == "NIL") { + value = Variant(); + } else { + r_err_str = vformat("Unknown Variant constant '%s'", constant); + return ERR_PARSE_ERROR; + } } else if (id == "Vector2") { Vector args; Error err = _parse_construct(p_stream, args, line, r_err_str); diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h index 7ef8bb9c312..23576c45173 100644 --- a/tests/core/variant/test_variant.h +++ b/tests/core/variant/test_variant.h @@ -34,6 +34,7 @@ #include "core/variant/variant.h" #include "core/variant/variant_parser.h" +#include "core/variant/variant_utility.h" #include "tests/test_macros.h" @@ -2290,4 +2291,82 @@ TEST_CASE("[Variant] Operator NOT") { } } +TEST_CASE("[Variant] VariantParser for Variant::NIL") { + String errs; + int line; + Variant result; + Error err; + + // Test Variant::NIL + { + VariantParser::StreamString ss; + ss.s = "Variant::NIL"; + line = 1; + err = VariantParser::parse(&ss, result, errs, line); + CHECK_MESSAGE(err == OK, "Variant::NIL should parse without error"); + CHECK_MESSAGE(result.get_type() == Variant::NIL, "Variant::NIL should result in a nil Variant"); + } + + // Test null (regression) + { + VariantParser::StreamString ss; + ss.s = "null"; + line = 1; + err = VariantParser::parse(&ss, result, errs, line); + CHECK_MESSAGE(err == OK, "null should parse without error"); + CHECK_MESSAGE(result.get_type() == Variant::NIL, "null should result in a nil Variant"); + } + + // Test nil (regression) + { + VariantParser::StreamString ss; + ss.s = "nil"; + line = 1; + err = VariantParser::parse(&ss, result, errs, line); + CHECK_MESSAGE(err == OK, "nil should parse without error"); + CHECK_MESSAGE(result.get_type() == Variant::NIL, "nil should result in a nil Variant"); + } + + // Test Variant::INVALID (error case) + { + VariantParser::StreamString ss; + ss.s = "Variant::INVALID"; + line = 1; + err = VariantParser::parse(&ss, result, errs, line); + CHECK_MESSAGE(err != OK, "Variant::INVALID should produce a parse error"); + CHECK_MESSAGE(errs.contains("Unknown Variant constant 'INVALID'"), "Error message should mention unknown constant"); + } + + // Test Variant (alone) + { + VariantParser::StreamString ss; + ss.s = "Variant"; + line = 1; + err = VariantParser::parse(&ss, result, errs, line); + CHECK_MESSAGE(err != OK, "Variant alone should produce a parse error"); + CHECK_MESSAGE(errs.contains("Expected '::' after 'Variant'"), "Error message should mention expected '::'"); + } + + // Test p_allow_objects security gating + { + VariantParser::StreamString ss; + ss.s = "Object(RefCounted, {})"; + line = 1; + err = VariantParser::parse(&ss, result, errs, line, nullptr, false); // p_allow_objects = false + CHECK_MESSAGE(err == ERR_UNAUTHORIZED, "Object parsing should be unauthorized when p_allow_objects is false"); + } + + { + VariantParser::StreamString ss; + ss.s = "Variant::NIL"; + line = 1; + err = VariantParser::parse(&ss, result, errs, line, nullptr, false); // p_allow_objects = false + CHECK_MESSAGE(err == OK, "Variant::NIL should be allowed even when p_allow_objects is false"); + } + + // Test with str_to_var + Variant v = VariantUtilityFunctions::str_to_var("Variant::NIL"); + CHECK_MESSAGE(v.get_type() == Variant::NIL, "str_to_var(\"Variant::NIL\") should result in a nil Variant"); +} + } // namespace TestVariant