From a76d85602476f29a75689e40e92c2d29e524cd6f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 26 Apr 2024 09:45:28 +0200 Subject: [PATCH] Fix calling methods on a constant string (#804) --- .../Parser/ExpressionParser.cs | 18 +++++++++---- .../DynamicExpressionParserTests.cs | 25 +++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index 8187cb87..f52718d7 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -1798,8 +1798,9 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string? Expression[]? args = null; var isStaticAccess = expression == null; + var isConstantString = expression is ConstantExpression { Value: string }; - if (!isStaticAccess && TypeHelper.TryFindGenericType(typeof(IEnumerable<>), type, out var enumerableType)) + if (!isStaticAccess && !isConstantString && TypeHelper.TryFindGenericType(typeof(IEnumerable<>), type, out var enumerableType)) { var elementType = enumerableType.GetTypeInfo().GetGenericTypeArguments()[0]; if (TryParseEnumerable(expression!, elementType, id, errorPos, type, out args, out var enumerableExpression)) @@ -2042,25 +2043,32 @@ private Expression ParseAsEnumOrNestedClass(string id) private bool TryParseEnumerable(Expression instance, Type elementType, string methodName, int errorPos, Type? type, out Expression[]? args, [NotNullWhen(true)] out Expression? expression) { + // Keep the current _parent. var oldParent = _parent; - ParameterExpression? outerIt = _it; - ParameterExpression innerIt = ParameterExpressionHelper.CreateParameterExpression(elementType, string.Empty, _parsingConfig.RenameEmptyParameterExpressionNames); - + // Set the _parent to the current _it. _parent = _it; + // Set the outerIt to the current _it. + var outerIt = _it; + + // Create a new innerIt based on the elementType. + var innerIt = ParameterExpressionHelper.CreateParameterExpression(elementType, string.Empty, _parsingConfig.RenameEmptyParameterExpressionNames); + if (new[] { "Contains", "ContainsKey", "Skip", "Take" }.Contains(methodName)) { - // for any method that acts on the parent element type, we need to specify the outerIt as scope. + // For any method that acts on the parent element type, we need to specify the outerIt as scope. _it = outerIt; } else { + // Else we need to specify the innerIt as scope. _it = innerIt; } args = ParseArgumentList(); + // Revert the _it and _parent to the old values. _it = outerIt; _parent = oldParent; diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs index 7185c405..a609aab7 100644 --- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs @@ -1651,6 +1651,31 @@ public void DynamicExpressionParser_ParseLambda_StringEquals_WithCombinedConditi Check.That(result).IsEqualTo(true); } + // #803 + [Fact] + public void DynamicExpressionParser_ParseLambda_StringEquals_WithConstantString() + { + // Arrange + var parameters = new[] + { + Expression.Parameter(typeof(MyClass), "myClass") + }; + + var invokerArguments = new List + { + new MyClass { Name = "Foo" } + }; + + // Act + var expression = "Name == \"test\" || \"foo\".Equals(it.Name, StringComparison.OrdinalIgnoreCase)"; + var lambdaExpression = DynamicExpressionParser.ParseLambda(parameters, null, expression); + var del = lambdaExpression.Compile(); + var result = del.DynamicInvoke(invokerArguments.ToArray()); + + // Assert + result.Should().Be(true); + } + [Fact] public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_0_Arguments() {