Skip to content

Commit a76d856

Browse files
authored
Fix calling methods on a constant string (#804)
1 parent 6d0acaa commit a76d856

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

+13-5
Original file line numberDiff line numberDiff line change
@@ -1798,8 +1798,9 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string?
17981798
Expression[]? args = null;
17991799

18001800
var isStaticAccess = expression == null;
1801+
var isConstantString = expression is ConstantExpression { Value: string };
18011802

1802-
if (!isStaticAccess && TypeHelper.TryFindGenericType(typeof(IEnumerable<>), type, out var enumerableType))
1803+
if (!isStaticAccess && !isConstantString && TypeHelper.TryFindGenericType(typeof(IEnumerable<>), type, out var enumerableType))
18031804
{
18041805
var elementType = enumerableType.GetTypeInfo().GetGenericTypeArguments()[0];
18051806
if (TryParseEnumerable(expression!, elementType, id, errorPos, type, out args, out var enumerableExpression))
@@ -2042,25 +2043,32 @@ private Expression ParseAsEnumOrNestedClass(string id)
20422043

20432044
private bool TryParseEnumerable(Expression instance, Type elementType, string methodName, int errorPos, Type? type, out Expression[]? args, [NotNullWhen(true)] out Expression? expression)
20442045
{
2046+
// Keep the current _parent.
20452047
var oldParent = _parent;
20462048

2047-
ParameterExpression? outerIt = _it;
2048-
ParameterExpression innerIt = ParameterExpressionHelper.CreateParameterExpression(elementType, string.Empty, _parsingConfig.RenameEmptyParameterExpressionNames);
2049-
2049+
// Set the _parent to the current _it.
20502050
_parent = _it;
20512051

2052+
// Set the outerIt to the current _it.
2053+
var outerIt = _it;
2054+
2055+
// Create a new innerIt based on the elementType.
2056+
var innerIt = ParameterExpressionHelper.CreateParameterExpression(elementType, string.Empty, _parsingConfig.RenameEmptyParameterExpressionNames);
2057+
20522058
if (new[] { "Contains", "ContainsKey", "Skip", "Take" }.Contains(methodName))
20532059
{
2054-
// for any method that acts on the parent element type, we need to specify the outerIt as scope.
2060+
// For any method that acts on the parent element type, we need to specify the outerIt as scope.
20552061
_it = outerIt;
20562062
}
20572063
else
20582064
{
2065+
// Else we need to specify the innerIt as scope.
20592066
_it = innerIt;
20602067
}
20612068

20622069
args = ParseArgumentList();
20632070

2071+
// Revert the _it and _parent to the old values.
20642072
_it = outerIt;
20652073
_parent = oldParent;
20662074

test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs

+25
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,31 @@ public void DynamicExpressionParser_ParseLambda_StringEquals_WithCombinedConditi
16511651
Check.That(result).IsEqualTo(true);
16521652
}
16531653

1654+
// #803
1655+
[Fact]
1656+
public void DynamicExpressionParser_ParseLambda_StringEquals_WithConstantString()
1657+
{
1658+
// Arrange
1659+
var parameters = new[]
1660+
{
1661+
Expression.Parameter(typeof(MyClass), "myClass")
1662+
};
1663+
1664+
var invokerArguments = new List<object>
1665+
{
1666+
new MyClass { Name = "Foo" }
1667+
};
1668+
1669+
// Act
1670+
var expression = "Name == \"test\" || \"foo\".Equals(it.Name, StringComparison.OrdinalIgnoreCase)";
1671+
var lambdaExpression = DynamicExpressionParser.ParseLambda(parameters, null, expression);
1672+
var del = lambdaExpression.Compile();
1673+
var result = del.DynamicInvoke(invokerArguments.ToArray());
1674+
1675+
// Assert
1676+
result.Should().Be(true);
1677+
}
1678+
16541679
[Fact]
16551680
public void DynamicExpressionParser_ParseLambda_NullPropagation_InstanceMethod_0_Arguments()
16561681
{

0 commit comments

Comments
 (0)