Skip to content

Commit d0a0d62

Browse files
authored
Fixed result value for bitwise operators when using enums (#720)
* Fixed result value for bitwise operators when using enums * . * 452
1 parent 7281dc9 commit d0a0d62

File tree

4 files changed

+153
-6
lines changed

4 files changed

+153
-6
lines changed

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,11 @@ public bool TryUnwrapAsConstantExpression(Expression? expression, [NotNullWhen(t
7171
return false;
7272
}
7373

74-
public void ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right)
74+
public bool ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right)
7575
{
7676
if (left.Type == right.Type)
7777
{
78-
return;
78+
return true;
7979
}
8080

8181
if (left.Type == typeof(ulong) || right.Type == typeof(ulong))
@@ -113,6 +113,8 @@ public void ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expressio
113113
right = right.Type != typeof(byte) ? Expression.Convert(right, typeof(byte)) : right;
114114
left = left.Type != typeof(byte) ? Expression.Convert(left, typeof(byte)) : left;
115115
}
116+
117+
return false;
116118
}
117119

118120
public Expression GenerateAdd(Expression left, Expression right)

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

+17-3
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,12 @@ private Expression ParseIn()
386386
private Expression ParseLogicalAndOrOperator()
387387
{
388388
Expression left = ParseComparisonOperator();
389-
while (_textParser.CurrentToken.Id == TokenId.Ampersand || _textParser.CurrentToken.Id == TokenId.Bar)
389+
390+
var leftType = left.Type;
391+
var typesAreSame = true;
392+
var numberOfArguments = 0;
393+
394+
while (_textParser.CurrentToken.Id is TokenId.Ampersand or TokenId.Bar)
390395
{
391396
Token op = _textParser.CurrentToken;
392397
_textParser.NextToken();
@@ -422,17 +427,26 @@ private Expression ParseLogicalAndOrOperator()
422427
}
423428
else
424429
{
425-
_expressionHelper.ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref left, ref right);
430+
typesAreSame &= _expressionHelper.ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref left, ref right);
426431
left = Expression.And(left, right);
427432
}
428433
break;
429434

430435
case TokenId.Bar:
431-
_expressionHelper.ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref left, ref right);
436+
typesAreSame &= _expressionHelper.ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref left, ref right);
432437
left = Expression.Or(left, right);
433438
break;
434439
}
440+
441+
numberOfArguments++;
435442
}
443+
444+
// #695
445+
if (numberOfArguments > 0 && typesAreSame && leftType.GetTypeInfo().IsEnum)
446+
{
447+
return Expression.Convert(left, leftType);
448+
}
449+
436450
return left;
437451
}
438452

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace System.Linq.Dynamic.Core.Parser;
55

66
internal interface IExpressionHelper
77
{
8-
void ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right);
8+
bool ConvertNumericTypeToBiggestCommonTypeForBinaryOperator(ref Expression left, ref Expression right);
99

1010
Expression GenerateAdd(Expression left, Expression right);
1111

test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs

+131
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ public partial class ExpressionParserTests
1717

1818
private readonly ParsingConfig _parsingConfig;
1919

20+
[Flags]
21+
public enum ExampleFlags
22+
{
23+
None = 0,
24+
A = 1,
25+
B = 2,
26+
C = 4,
27+
D = 8,
28+
};
29+
30+
2031
public ExpressionParserTests()
2132
{
2233
_dynamicTypeProviderMock = new Mock<IDynamicLinkCustomTypeProvider>();
@@ -32,6 +43,126 @@ public ExpressionParserTests()
3243
};
3344
}
3445

46+
[Fact]
47+
public void Parse_BitwiseOperatorOr_On_2EnumFlags()
48+
{
49+
// Arrange
50+
var expression = "@0 | @1";
51+
#if NET452
52+
var expected = "Convert((Convert(A) | Convert(B)))";
53+
#else
54+
var expected = "Convert((Convert(A, Int32) | Convert(B, Int32)), ExampleFlags)";
55+
#endif
56+
ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(typeof(int), "x") };
57+
var sut = new ExpressionParser(parameters, expression, new object[] { ExampleFlags.A, ExampleFlags.B }, null);
58+
59+
// Act
60+
var parsedExpression = sut.Parse(null).ToString();
61+
62+
// Assert
63+
parsedExpression.Should().Be(expected);
64+
65+
// Arrange
66+
var query = new[] { 0 }.AsQueryable();
67+
68+
// Act
69+
var result = query.Select(expression, ExampleFlags.A, ExampleFlags.B).First();
70+
71+
// Assert
72+
Assert.IsType<ExampleFlags>(result);
73+
Assert.Equal(ExampleFlags.A | ExampleFlags.B, result);
74+
}
75+
76+
[Fact]
77+
public void Parse_BitwiseOperatorAnd_On_2EnumFlags()
78+
{
79+
// Arrange
80+
var expression = "@0 & @1";
81+
#if NET452
82+
var expected = "Convert((Convert(A) & Convert(B)))";
83+
#else
84+
var expected = "Convert((Convert(A, Int32) & Convert(B, Int32)), ExampleFlags)";
85+
#endif
86+
ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(typeof(int), "x") };
87+
var sut = new ExpressionParser(parameters, expression, new object[] { ExampleFlags.A, ExampleFlags.B }, null);
88+
89+
// Act
90+
var parsedExpression = sut.Parse(null).ToString();
91+
92+
// Assert
93+
parsedExpression.Should().Be(expected);
94+
95+
// Arrange
96+
var query = new[] { 0 }.AsQueryable();
97+
98+
// Act
99+
var result = query.Select(expression, ExampleFlags.A, ExampleFlags.B).First();
100+
101+
// Assert
102+
Assert.IsType<ExampleFlags>(result);
103+
Assert.Equal(ExampleFlags.A & ExampleFlags.B, result);
104+
}
105+
106+
[Fact]
107+
public void Parse_BitwiseOperatorOr_On_3EnumFlags()
108+
{
109+
// Arrange
110+
var expression = "@0 | @1 | @2";
111+
#if NET452
112+
var expected = "Convert(((Convert(A) | Convert(B)) | Convert(C)))";
113+
#else
114+
var expected = "Convert(((Convert(A, Int32) | Convert(B, Int32)) | Convert(C, Int32)), ExampleFlags)";
115+
#endif
116+
ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(typeof(int), "x") };
117+
var sut = new ExpressionParser(parameters, expression, new object[] { ExampleFlags.A, ExampleFlags.B, ExampleFlags.C }, null);
118+
119+
// Act
120+
var parsedExpression = sut.Parse(null).ToString();
121+
122+
// Assert
123+
parsedExpression.Should().Be(expected);
124+
125+
// Arrange
126+
var query = new[] { 0 }.AsQueryable();
127+
128+
// Act
129+
var result = query.Select(expression, ExampleFlags.A, ExampleFlags.B, ExampleFlags.C).First();
130+
131+
// Assert
132+
Assert.IsType<ExampleFlags>(result);
133+
Assert.Equal(ExampleFlags.A | ExampleFlags.B | ExampleFlags.C, result);
134+
}
135+
136+
[Fact]
137+
public void Parse_BitwiseOperatorAnd_On_3EnumFlags()
138+
{
139+
// Arrange
140+
var expression = "@0 & @1 & @2";
141+
#if NET452
142+
var expected = "Convert(((Convert(A) & Convert(B)) & Convert(C)))";
143+
#else
144+
var expected = "Convert(((Convert(A, Int32) & Convert(B, Int32)) & Convert(C, Int32)), ExampleFlags)";
145+
#endif
146+
ParameterExpression[] parameters = { ParameterExpressionHelper.CreateParameterExpression(typeof(int), "x") };
147+
var sut = new ExpressionParser(parameters, expression, new object[] { ExampleFlags.A, ExampleFlags.B, ExampleFlags.C }, null);
148+
149+
// Act
150+
var parsedExpression = sut.Parse(null).ToString();
151+
152+
// Assert
153+
parsedExpression.Should().Be(expected);
154+
155+
// Arrange
156+
var query = new[] { 0 }.AsQueryable();
157+
158+
// Act
159+
var result = query.Select(expression, ExampleFlags.A, ExampleFlags.B, ExampleFlags.C).First();
160+
161+
// Assert
162+
Assert.IsType<ExampleFlags>(result);
163+
Assert.Equal(ExampleFlags.A & ExampleFlags.B & ExampleFlags.C, result);
164+
}
165+
35166
[Fact]
36167
public void Parse_ParseBinaryInteger()
37168
{

0 commit comments

Comments
 (0)