diff --git a/QueryKit.UnitTests/FilterParserTests.cs b/QueryKit.UnitTests/FilterParserTests.cs index a72fadd..f981682 100644 --- a/QueryKit.UnitTests/FilterParserTests.cs +++ b/QueryKit.UnitTests/FilterParserTests.cs @@ -516,6 +516,22 @@ public void can_throw_error_when_missing_double_quotes_not_recognized() .WithMessage("There was a parsing failure, likely due to an invalid comparison or logical operator. You may also be missing double quotes surrounding a string or guid.*"); } + [Theory] + [InlineData("""Age ^#$%^%@ 25""")] + [InlineData("""Title == "temp" %$@#^ Age == 25""")] + [InlineData("""BirthMonth == invalid""")] + public void parsing_exception_message_has_no_dotnet_type_names(string input) + { + var act = () => FilterParser.ParseFilter(input); + var exception = act.Should().Throw().Which; + + exception.Message.Should().NotContain("System."); + exception.Message.Should().NotContain("Sprache."); + exception.Message.Should().NotMatch("*InvalidOperationException*"); + exception.Message.Should().NotMatch("*ParseException*"); + exception.InnerException.Should().NotBeNull(); + } + [Fact] public void can_throw_error_when_property_has_space() { diff --git a/QueryKit/Exceptions/ParsingException.cs b/QueryKit/Exceptions/ParsingException.cs index d7bee9e..c3dbb3e 100644 --- a/QueryKit/Exceptions/ParsingException.cs +++ b/QueryKit/Exceptions/ParsingException.cs @@ -1,11 +1,25 @@ namespace QueryKit.Exceptions; +using Sprache; + public sealed class ParsingException : QueryKitException { public ParsingException(Exception exception) - : base(@$"There was a parsing failure, likely due to an invalid comparison or logical operator. You may also be missing double quotes surrounding a string or guid. + : base(BuildMessage(exception), exception) + { + } -{exception.Message}", exception) + private static string BuildMessage(Exception exception) { + const string baseMessage = "There was a parsing failure, likely due to an invalid comparison or logical operator. You may also be missing double quotes surrounding a string or guid."; + + // Sprache.Position holds only a line and a column, so it is safe to expose to a client. + // The full exception.Message can name internal parser rules or .NET types, so it stays server-side on InnerException. + if (exception is ParseException parseException) + { + return $"{baseMessage} Failed at {parseException.Position}."; + } + + return baseMessage; } }