@@ -495,6 +495,7 @@ private string EmitOperation(IOperation operation)
495495 ICoalesceOperation coalesce => EmitCoalesce ( coalesce ) ,
496496 IArrayCreationOperation arrayCreate => EmitArrayCreation ( arrayCreate ) ,
497497 IArrayElementReferenceOperation arrayElement => EmitArrayElementReference ( arrayElement ) ,
498+ IImplicitIndexerReferenceOperation implicitIdx => EmitImplicitIndexerReference ( implicitIdx ) ,
498499 IAnonymousFunctionOperation lambda => EmitNestedLambda ( lambda ) ,
499500 IDelegateCreationOperation delegateCreate => EmitDelegateCreation ( delegateCreate ) ,
500501 ITupleOperation tuple => EmitTuple ( tuple ) ,
@@ -1071,9 +1072,13 @@ private string EmitConversion(IConversionOperation conversion)
10711072 return quoteResult ;
10721073 }
10731074
1074- // Implicit reference upcasts in argument position break EF Core's queryable-chain matching.
1075- if ( conversion . IsImplicit && conversion . Conversion . IsReference && conversion . Parent is IArgumentOperation )
1075+ // Implicit reference upcasts from a concrete reference operands
1076+ if ( conversion . IsImplicit
1077+ && conversion . Conversion . IsReference
1078+ && conversion . Operand . Type is { IsReferenceType : true } )
1079+ {
10761080 return EmitOperation ( conversion . Operand ) ;
1081+ }
10771082
10781083 var resultVar = NextVar ( ) ;
10791084 var operandVar = EmitOperation ( conversion . Operand ) ;
@@ -1634,6 +1639,23 @@ private string EmitDeclarationPattern(IDeclarationPatternOperation declaration,
16341639 return resultVar ;
16351640 }
16361641
1642+ // Switch arm bodies can reference pattern-declared variables (`int i => i + 1`).
1643+ // The pattern itself emits only the TypeIs/Equal/etc. test, so we bind each
1644+ // declared local to a Convert of the governing value before the arm body emits,
1645+ // otherwise the local reference falls through to the closure-capture path and
1646+ // tries to read a non-existent field on __func.Target.
1647+ private void BindPatternDeclarations ( IPatternOperation pattern , string operandVar )
1648+ {
1649+ if ( pattern is IDeclarationPatternOperation decl
1650+ && decl . DeclaredSymbol is ILocalSymbol localSym )
1651+ {
1652+ var convertVar = NextVar ( ) ;
1653+ var typeFqn = decl . NarrowedType . ToDisplayString ( _fqnFormat ) ;
1654+ AppendLine ( $ "var { convertVar } = { Expr } .Convert({ operandVar } , typeof({ typeFqn } ));") ;
1655+ _localToVar [ localSym ] = convertVar ;
1656+ }
1657+ }
1658+
16371659 private string EmitRelationalPattern ( IRelationalPatternOperation relational , string operandVar , ITypeSymbol ? operandType )
16381660 {
16391661 var resultVar = NextVar ( ) ;
@@ -1732,31 +1754,44 @@ private string EmitListPattern(IListPatternOperation listPattern, string operand
17321754 {
17331755 var conditions = new List < string > ( ) ;
17341756
1735- var countProp = operandType ? . GetMembers ( "Count" ) . OfType < IPropertySymbol > ( ) . FirstOrDefault ( )
1736- ?? operandType ? . GetMembers ( "Length" ) . OfType < IPropertySymbol > ( ) . FirstOrDefault ( ) ;
1757+ var arrayType = operandType as IArrayTypeSymbol ;
1758+ IPropertySymbol ? countProp = null ;
1759+ IPropertySymbol ? indexer = null ;
17371760
1738- var indexer = operandType ? . GetMembers ( )
1739- . OfType < IPropertySymbol > ( )
1740- . FirstOrDefault ( p => p . IsIndexer && p . Parameters . Length == 1
1741- && p . Parameters [ 0 ] . Type . SpecialType == SpecialType . System_Int32 ) ;
1742-
1743- if ( countProp is null || indexer is null )
1761+ if ( arrayType is null )
17441762 {
1745- ReportDiagnostic ( Diagnostics . UnsupportedOperation ,
1746- listPattern . Syntax ? . GetLocation ( ) ?? Location . None ,
1747- "ListPattern (type lacks Count/Length or indexer)" ) ;
1748- return EmitUnsupported ( listPattern ) ;
1749- }
1763+ countProp = operandType ? . GetMembers ( "Count" ) . OfType < IPropertySymbol > ( ) . FirstOrDefault ( )
1764+ ?? operandType ? . GetMembers ( "Length" ) . OfType < IPropertySymbol > ( ) . FirstOrDefault ( ) ;
17501765
1751- var countField = _fieldCache . EnsurePropertyInfo ( countProp ) ;
1766+ indexer = operandType ? . GetMembers ( )
1767+ . OfType < IPropertySymbol > ( )
1768+ . FirstOrDefault ( p => p . IsIndexer && p . Parameters . Length == 1
1769+ && p . Parameters [ 0 ] . Type . SpecialType == SpecialType . System_Int32 ) ;
1770+
1771+ if ( countProp is null || indexer is null )
1772+ {
1773+ ReportDiagnostic ( Diagnostics . UnsupportedOperation ,
1774+ listPattern . Syntax ? . GetLocation ( ) ?? Location . None ,
1775+ "ListPattern (type lacks Count/Length or indexer)" ) ;
1776+ return EmitUnsupported ( listPattern ) ;
1777+ }
1778+ }
17521779
17531780 // A `..` slice means minimum-length match; otherwise exact-length.
17541781 var hasSlice = listPattern . Patterns . Any ( p => p is ISlicePatternOperation ) ;
17551782 var fixedPatterns = listPattern . Patterns . Where ( p => p is not ISlicePatternOperation ) . ToList ( ) ;
17561783 var requiredCount = fixedPatterns . Count ;
17571784
17581785 var countAccess = NextVar ( ) ;
1759- AppendLine ( $ "var { countAccess } = { Expr } .Property({ operandVar } , { countField } );") ;
1786+ if ( arrayType is not null )
1787+ {
1788+ AppendLine ( $ "var { countAccess } = { Expr } .ArrayLength({ operandVar } );") ;
1789+ }
1790+ else
1791+ {
1792+ var countField = _fieldCache . EnsurePropertyInfo ( countProp ! ) ;
1793+ AppendLine ( $ "var { countAccess } = { Expr } .Property({ operandVar } , { countField } );") ;
1794+ }
17601795 var countConst = NextVar ( ) ;
17611796 AppendLine ( $ "var { countConst } = { Expr } .Constant({ requiredCount } );") ;
17621797 var lengthCheck = NextVar ( ) ;
@@ -1788,17 +1823,19 @@ private string EmitListPattern(IListPatternOperation listPattern, string operand
17881823 AppendLine ( $ "var { idxConst } = { Expr } .Constant({ elementIndex } );") ;
17891824
17901825 var elementAccess = NextVar ( ) ;
1791- if ( operandType is IArrayTypeSymbol )
1826+ ITypeSymbol elementType ;
1827+ if ( arrayType is not null )
17921828 {
17931829 AppendLine ( $ "var { elementAccess } = { Expr } .ArrayIndex({ operandVar } , { idxConst } );") ;
1830+ elementType = arrayType . ElementType ;
17941831 }
17951832 else
17961833 {
1797- var indexerField = _fieldCache . EnsurePropertyInfo ( indexer ) ;
1834+ var indexerField = _fieldCache . EnsurePropertyInfo ( indexer ! ) ;
17981835 AppendLine ( $ "var { elementAccess } = { Expr } .Property({ operandVar } , { indexerField } , { idxConst } );") ;
1836+ elementType = indexer ! . Type ;
17991837 }
18001838
1801- var elementType = indexer . Type ;
18021839 var subCondition = EmitPattern ( subPattern , elementAccess , elementType ) ;
18031840 conditions . Add ( subCondition ) ;
18041841 elementIndex ++ ;
@@ -2134,6 +2171,8 @@ private string EmitSwitchExpression(ISwitchExpressionOperation switchExpr)
21342171
21352172 var conditionVar = EmitPattern ( arm . Pattern , governingVar , switchExpr . Value . Type ) ;
21362173
2174+ BindPatternDeclarations ( arm . Pattern , governingVar ) ;
2175+
21372176 if ( arm . Guard is not null )
21382177 {
21392178 var guardVar = EmitOperation ( arm . Guard ) ;
@@ -2716,6 +2755,70 @@ private string EmitIndexFromEnd(IUnaryOperation unary)
27162755 return resultVar ;
27172756 }
27182757
2758+ // Lowers `s[range]` on string to `s.Substring(start, length)` so the result lands
2759+ // in expression-tree shape (the language-level Range/Index machinery doesn't survive
2760+ // an expression tree otherwise).
2761+ private string EmitImplicitIndexerReference ( IImplicitIndexerReferenceOperation op )
2762+ {
2763+ if ( op . Instance is null || op . Instance . Type is null )
2764+ return EmitUnsupported ( op ) ;
2765+
2766+ var receiverType = op . Instance . Type ;
2767+ var receiverVar = EmitOperation ( op . Instance ) ;
2768+
2769+ if ( receiverType . SpecialType == SpecialType . System_String && op . Argument is IRangeOperation range )
2770+ {
2771+ var lengthAccessor = NextVar ( ) ;
2772+ AppendLine ( $ "var { lengthAccessor } = { Expr } .Property({ receiverVar } , typeof(global::System.String).GetProperty(\" Length\" ));") ;
2773+
2774+ var startVar = EmitIndexAsInt ( range . LeftOperand , lengthAccessor , defaultIsZero : true ) ;
2775+ var endVar = EmitIndexAsInt ( range . RightOperand , lengthAccessor , defaultIsZero : false ) ;
2776+
2777+ var lengthVar = NextVar ( ) ;
2778+ AppendLine ( $ "var { lengthVar } = { Expr } .Subtract({ endVar } , { startVar } );") ;
2779+
2780+ var substringMethod = NextVar ( ) ;
2781+ AppendLine ( $ "var { substringMethod } = typeof(global::System.String).GetMethod(\" Substring\" , new global::System.Type[] {{ typeof(int), typeof(int) }});") ;
2782+
2783+ var resultVar = NextVar ( ) ;
2784+ AppendLine ( $ "var { resultVar } = { Expr } .Call({ receiverVar } , { substringMethod } , { startVar } , { lengthVar } );") ;
2785+ return resultVar ;
2786+ }
2787+
2788+ return EmitUnsupported ( op ) ;
2789+ }
2790+
2791+ // Emits an int-typed expression representing the absolute offset of an Index operand.
2792+ // `defaultIsZero=true` returns 0 when the operand is omitted (left side of `..`);
2793+ // false returns the receiver length (right side).
2794+ private string EmitIndexAsInt ( IOperation ? indexOperand , string lengthVar , bool defaultIsZero )
2795+ {
2796+ if ( indexOperand is null )
2797+ {
2798+ if ( defaultIsZero )
2799+ {
2800+ var zeroVar = NextVar ( ) ;
2801+ AppendLine ( $ "var { zeroVar } = { Expr } .Constant(0);") ;
2802+ return zeroVar ;
2803+ }
2804+ return lengthVar ;
2805+ }
2806+
2807+ if ( indexOperand is IUnaryOperation { OperatorKind : UnaryOperatorKind . Hat } fromEnd )
2808+ {
2809+ var inner = EmitOperation ( fromEnd . Operand ) ;
2810+ var resultVar = NextVar ( ) ;
2811+ AppendLine ( $ "var { resultVar } = { Expr } .Subtract({ lengthVar } , { inner } );") ;
2812+ return resultVar ;
2813+ }
2814+
2815+ // Plain int (possibly wrapped in a conversion-to-Index that we can ignore).
2816+ if ( indexOperand is IConversionOperation conv && conv . Operand . Type ? . SpecialType == SpecialType . System_Int32 )
2817+ return EmitOperation ( conv . Operand ) ;
2818+
2819+ return EmitOperation ( indexOperand ) ;
2820+ }
2821+
27192822 private string EmitRange ( IRangeOperation range )
27202823 {
27212824 var resultVar = NextVar ( ) ;
@@ -2856,7 +2959,10 @@ private string EmitCollectionExpression(ICollectionExpressionOperation collExpr)
28562959 if ( type is IArrayTypeSymbol arrayType )
28572960 {
28582961 var elementTypeFqn = arrayType . ElementType . ToDisplayString ( _fqnFormat ) ;
2859- AppendLine ( $ "var { resultVar } = { Expr } .NewArrayInit(typeof({ elementTypeFqn } ), { elementsExpr } );") ;
2962+ var arrayArgs = elementVars . Count == 0
2963+ ? $ "typeof({ elementTypeFqn } )"
2964+ : $ "typeof({ elementTypeFqn } ), { elementsExpr } ";
2965+ AppendLine ( $ "var { resultVar } = { Expr } .NewArrayInit({ arrayArgs } );") ;
28602966 }
28612967 else if ( type is INamedTypeSymbol namedType && namedType . IsGenericType
28622968 && namedType . OriginalDefinition . SpecialType == SpecialType . None )
@@ -2872,7 +2978,7 @@ private string EmitCollectionExpression(ICollectionExpressionOperation collExpr)
28722978 . OfType < IMethodSymbol > ( )
28732979 . FirstOrDefault ( m => m . Parameters . Length == 1 ) ;
28742980
2875- if ( addMethod is not null )
2981+ if ( addMethod is not null && elementVars . Count > 0 )
28762982 {
28772983 var addField = _fieldCache . EnsureMethodInfo ( addMethod ) ;
28782984 var elemInitVars = new List < string > ( ) ;
@@ -2886,7 +2992,7 @@ private string EmitCollectionExpression(ICollectionExpressionOperation collExpr)
28862992 }
28872993 else
28882994 {
2889- // No Add method — leave the collection empty .
2995+ // No elements (or no Add method) — return the bare New expression .
28902996 AppendLine ( $ "var { resultVar } = { newVar } ;") ;
28912997 }
28922998 }
0 commit comments