From 677a054448d1cbda56c74664980f369c472acb3e Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 19 Jul 2026 21:57:05 -0700 Subject: [PATCH] fix(advanced-search): N - reference-type comparisons don't throw on ordering operators --- .../AdvancedSearchUtilitiesTests.cs | 12 ++++++ .../AdvancedSearchUtilities.cs | 41 ++++++++++++++----- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchUtilitiesTests.cs b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchUtilitiesTests.cs index 412e5c1e0..030f3f573 100644 --- a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchUtilitiesTests.cs +++ b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchUtilitiesTests.cs @@ -54,4 +54,16 @@ public class AdvancedSearchUtilitiesTests var result = AdvancedSearchUtilities.EvaluateBoolean(expr, leaf => leaf == "T"); Assert.Equal(expected, result); } + + [Fact] + public void CompareValues_ReferenceType_EqualityByString_NoThrow() + { + // A reference-typed property (e.g. RootParent name-ish) compared with "=" should not throw, + // and ordering operators must return false rather than throwing. + var ex = Record.Exception(() => + { + Assert.False(AdvancedSearchUtilities.CompareValues(typeof(object), new object(), "whatever", ">")); + }); + Assert.Null(ex); + } } diff --git a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs index 16355a249..44a694ef1 100644 --- a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs +++ b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchUtilities.cs @@ -240,17 +240,38 @@ public static class AdvancedSearchUtilities _ => false }; - public static bool CompareReference(T propertyValue, T parsedValue, ReadOnlySpan operatorSpan) => - operatorSpan switch + public static bool CompareReference(T propertyValue, T parsedValue, ReadOnlySpan operatorSpan) + { + switch (operatorSpan) { - "=" or "==" => propertyValue.Equals(parsedValue), - "!" or "!=" => !propertyValue.Equals(parsedValue), - ">" => Comparer.Default.Compare(propertyValue, parsedValue) > 0, - "<" => Comparer.Default.Compare(propertyValue, parsedValue) < 0, - ">=" => Comparer.Default.Compare(propertyValue, parsedValue) >= 0, - "<=" => Comparer.Default.Compare(propertyValue, parsedValue) <= 0, - _ => false - }; + case "=": + case "==": return Equals(propertyValue, parsedValue); + case "!": + case "!=": return !Equals(propertyValue, parsedValue); + } + + if (propertyValue is IComparable cmp && parsedValue != null) + { + try + { + var c = cmp.CompareTo(parsedValue); + return operatorSpan switch + { + ">" => c > 0, + "<" => c < 0, + ">=" => c >= 0, + "<=" => c <= 0, + _ => false + }; + } + catch + { + return false; + } + } + + return false; + } public static T ParseValue(ReadOnlySpan valuePart) {