@@ -365,78 +365,26 @@ private Expression ParseIn()
365365
366366 _textParser . NextToken ( ) ;
367367
368+ var expressions = new Dictionary < Expression , int > ( ) ;
369+
368370 if ( _textParser . CurrentToken . Id == TokenId . OpenParen ) // literals (or other inline list)
369371 {
370- var values = new List < Expression > ( ) ;
371- var comparisons = new List < Expression > ( ) ;
372- Expression ? containsLeft = null ;
373- string ? containsLeftText = null ;
374- var canUseContains = true ;
375-
376372 while ( _textParser . CurrentToken . Id != TokenId . CloseParen )
377373 {
378374 _textParser . NextToken ( ) ;
379375
380376 // we need to parse unary expressions because otherwise 'in' clause will fail in use cases like 'in (-1, -1)' or 'in (!true)'
381377 Expression right = ParseUnary ( ) ;
382378
383- // if the identifier is an Enum (or nullable Enum), try to convert the right-side also to an Enum.
384- if ( TypeHelper . GetNonNullableType ( left . Type ) . GetTypeInfo ( ) . IsEnum )
385- {
386- if ( right is ConstantExpression constantExprRight )
387- {
388- right = ParseEnumToConstantExpression ( token . Pos , left . Type , constantExprRight ) ;
389- }
390- else if ( _expressionHelper . TryUnwrapAsConstantExpression ( right , out var unwrappedConstantExprRight ) )
391- {
392- right = ParseEnumToConstantExpression ( token . Pos , left . Type , unwrappedConstantExprRight ) ;
393- }
394- }
395-
396- // else, check for direct type match
397- else if ( left . Type != right . Type )
398- {
399- CheckAndPromoteOperands ( typeof ( IEqualitySignatures ) , TokenId . DoubleEqual , "==" , ref left , ref right , token . Pos ) ;
400- }
401-
402- var equalsExpression = _expressionHelper . GenerateEqual ( left , right ) ;
403- comparisons . Add ( equalsExpression ) ;
404-
405- if ( canUseContains && equalsExpression is BinaryExpression binaryExpression && binaryExpression . NodeType == ExpressionType . Equal )
406- {
407- containsLeft ??= binaryExpression . Left ;
408- containsLeftText ??= binaryExpression . Left . ToString ( ) ;
409-
410- if ( containsLeft . Type != binaryExpression . Left . Type || ! string . Equals ( containsLeftText , binaryExpression . Left . ToString ( ) , StringComparison . Ordinal ) || binaryExpression . Right . Type != containsLeft . Type )
411- {
412- canUseContains = false ;
413- }
414- else
415- {
416- values . Add ( binaryExpression . Right ) ;
417- }
418- }
419- else
420- {
421- canUseContains = false ;
422- }
379+ expressions . Add ( right , token . Pos ) ;
423380
424381 if ( _textParser . CurrentToken . Id == TokenId . End )
425382 {
426383 throw ParseError ( token . Pos , Res . CloseParenOrCommaExpected ) ;
427384 }
428385 }
429386
430- if ( canUseContains && containsLeft != null )
431- {
432- var typeArgs = new [ ] { containsLeft . Type } ;
433- var args = new Expression [ ] { Expression . NewArrayInit ( containsLeft . Type , values ) , containsLeft } ;
434- accumulate = Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Contains ) , typeArgs , args ) ;
435- }
436- else
437- {
438- accumulate = _expressionHelper . GenerateBinaryOrElseTree ( comparisons ) ;
439- }
387+ accumulate = ProcessInExpressions ( accumulate , expressions ) ;
440388
441389 // Since this started with an open paren, make sure to move off the close
442390 _textParser . NextToken ( ) ;
@@ -445,16 +393,29 @@ private Expression ParseIn()
445393 {
446394 Expression right = ParsePrimary ( ) ;
447395
448- if ( ! typeof ( IEnumerable ) . IsAssignableFrom ( right . Type ) )
396+ if ( ! TypeHelper . TryGetAsEnumerable ( right . Type , out _ ) )
449397 {
450- throw ParseError ( _textParser . CurrentToken . Pos , Res . IdentifierImplementingInterfaceExpected , typeof ( IEnumerable ) ) ;
398+ throw ParseError ( _textParser . CurrentToken . Pos , Res . IdentifierImplementingInterfaceExpected , typeof ( IEnumerable < > ) ) ;
451399 }
452400
453- var typeArgs = new [ ] { left . Type } ;
401+ // Handle "it.TestEnum in @0", and the @0 should be a object like a List<string>.
402+ if ( _symbols . Count > 0 && right is ConstantExpression constantExprRight && constantExprRight . Value != null )
403+ {
404+ foreach ( var item in ( IEnumerable ) constantExprRight . Value )
405+ {
406+ expressions . Add ( Expression . Constant ( item ) , token . Pos ) ;
407+ }
454408
455- var args = new [ ] { right , left } ;
409+ accumulate = ProcessInExpressions ( accumulate , expressions ) ;
410+ }
456411
457- accumulate = Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Contains ) , typeArgs , args ) ;
412+ // Handle "'y' in Name"
413+ else
414+ {
415+ var typeArgs = new [ ] { left . Type } ;
416+ var args = new [ ] { right , left } ;
417+ accumulate = Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Contains ) , typeArgs , args ) ;
418+ }
458419 }
459420 else
460421 {
@@ -470,6 +431,71 @@ private Expression ParseIn()
470431 return accumulate ;
471432 }
472433
434+ private Expression ProcessInExpressions ( Expression left , Dictionary < Expression , int > expressions )
435+ {
436+ var values = new List < Expression > ( ) ;
437+ var comparisons = new List < Expression > ( ) ;
438+ Expression ? containsLeft = null ;
439+ string ? containsLeftText = null ;
440+ var canUseContains = true ;
441+
442+ for ( int i = 0 ; i < expressions . Count ; i ++ )
443+ {
444+ var right = expressions . ElementAt ( i ) . Key ;
445+ var tokenPos = expressions . ElementAt ( i ) . Value ;
446+
447+ // if the identifier is an Enum (or nullable Enum), try to convert the right-side also to an Enum.
448+ if ( TypeHelper . GetNonNullableType ( left . Type ) . GetTypeInfo ( ) . IsEnum )
449+ {
450+ if ( right is ConstantExpression constantExprRight )
451+ {
452+ right = ParseEnumToConstantExpression ( tokenPos , left . Type , constantExprRight ) ;
453+ }
454+ else if ( _expressionHelper . TryUnwrapAsConstantExpression ( right , out var unwrappedConstantExprRight ) )
455+ {
456+ right = ParseEnumToConstantExpression ( tokenPos , left . Type , unwrappedConstantExprRight ) ;
457+ }
458+ }
459+
460+ // else, check for direct type match
461+ else if ( left . Type != right . Type )
462+ {
463+ CheckAndPromoteOperands ( typeof ( IEqualitySignatures ) , TokenId . DoubleEqual , "==" , ref left , ref right , tokenPos ) ;
464+ }
465+
466+ var equalsExpression = _expressionHelper . GenerateEqual ( left , right ) ;
467+ comparisons . Add ( equalsExpression ) ;
468+
469+ if ( canUseContains && equalsExpression is BinaryExpression binaryExpression && binaryExpression . NodeType == ExpressionType . Equal )
470+ {
471+ containsLeft ??= binaryExpression . Left ;
472+ containsLeftText ??= binaryExpression . Left . ToString ( ) ;
473+
474+ if ( containsLeft . Type != binaryExpression . Left . Type || ! string . Equals ( containsLeftText , binaryExpression . Left . ToString ( ) , StringComparison . Ordinal ) || binaryExpression . Right . Type != containsLeft . Type )
475+ {
476+ canUseContains = false ;
477+ }
478+ else
479+ {
480+ values . Add ( binaryExpression . Right ) ;
481+ }
482+ }
483+ else
484+ {
485+ canUseContains = false ;
486+ }
487+ }
488+
489+ if ( canUseContains && containsLeft != null )
490+ {
491+ var typeArgs = new [ ] { containsLeft . Type } ;
492+ var args = new Expression [ ] { Expression . NewArrayInit ( containsLeft . Type , values ) , containsLeft } ;
493+ return Expression . Call ( typeof ( Enumerable ) , nameof ( Enumerable . Contains ) , typeArgs , args ) ;
494+ }
495+
496+ return _expressionHelper . GenerateBinaryOrElseTree ( comparisons ) ;
497+ }
498+
473499 // &, | bitwise operators
474500 private Expression ParseLogicalAndOrOperator ( )
475501 {
@@ -2081,9 +2107,9 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string?
20812107 throw ParseError ( errorPos , Res . UnknownPropertyOrField , id , TypeHelper . GetTypeName ( type ) ) ;
20822108 }
20832109
2084- private bool TryFindPropertyOrField ( Type type , string id , Expression ? expression , [ NotNullWhen ( true ) ] out Expression ? propertyOrFieldExpression )
2110+ private bool TryFindPropertyOrField ( Type type , string memberName , Expression ? expression , [ NotNullWhen ( true ) ] out Expression ? propertyOrFieldExpression )
20852111 {
2086- var member = FindPropertyOrField ( type , id , expression == null ) ;
2112+ var member = FindPropertyOrField ( type , memberName , expression == null ) ;
20872113 switch ( member )
20882114 {
20892115 case PropertyInfo property :
0 commit comments