11using System . Collections ;
22using System . Collections . Generic ;
33using System . ComponentModel ;
4+ using System . Diagnostics . CodeAnalysis ;
45using System . Globalization ;
56using System . Linq . Dynamic . Core . Exceptions ;
67using System . Linq . Dynamic . Core . Parser . SupportedMethods ;
@@ -19,10 +20,10 @@ namespace System.Linq.Dynamic.Core.Parser;
1920/// </summary>
2021public class ExpressionParser
2122{
22- private static readonly string methodOrderBy = nameof ( Queryable . OrderBy ) ;
23- private static readonly string methodOrderByDescending = nameof ( Queryable . OrderByDescending ) ;
24- private static readonly string methodThenBy = nameof ( Queryable . ThenBy ) ;
25- private static readonly string methodThenByDescending = nameof ( Queryable . ThenByDescending ) ;
23+ private const string MethodOrderBy = nameof ( Queryable . OrderBy ) ;
24+ private const string MethodOrderByDescending = nameof ( Queryable . OrderByDescending ) ;
25+ private const string MethodThenBy = nameof ( Queryable . ThenBy ) ;
26+ private const string MethodThenByDescending = nameof ( Queryable . ThenByDescending ) ;
2627
2728 private readonly ParsingConfig _parsingConfig ;
2829 private readonly MethodFinder _methodFinder ;
@@ -51,7 +52,7 @@ public class ExpressionParser
5152 /// There was a problem when an expression contained multiple lambdas where
5253 /// the ItName was not cleared and freed for the next lambda. This variable
5354 /// stores the ItName of the last parsed lambda.
54- /// Not used internally by ExpressionParser, but used to preserve compatiblity of parsingConfig.RenameParameterExpression
55+ /// Not used internally by ExpressionParser, but used to preserve compatibility of parsingConfig.RenameParameterExpression
5556 /// which was designed to only work with mono-lambda expressions.
5657 /// </summary>
5758 public string LastLambdaItName { get ; private set ; } = KeywordsHelper . KEYWORD_IT ;
@@ -93,7 +94,7 @@ private void ProcessParameters(ParameterExpression[] parameters)
9394 {
9495 foreach ( ParameterExpression pe in parameters . Where ( p => ! string . IsNullOrEmpty ( p . Name ) ) )
9596 {
96- AddSymbol ( pe . Name , pe ) ;
97+ AddSymbol ( pe . Name ! , pe ) ;
9798 }
9899
99100 // If there is only 1 ParameterExpression, do also allow access using 'it'
@@ -185,11 +186,11 @@ internal IList<DynamicOrdering> ParseOrdering(bool forceThenBy = false)
185186 string methodName ;
186187 if ( forceThenBy || orderings . Count > 0 )
187188 {
188- methodName = ascending ? methodThenBy : methodThenByDescending ;
189+ methodName = ascending ? MethodThenBy : MethodThenByDescending ;
189190 }
190191 else
191192 {
192- methodName = ascending ? methodOrderBy : methodOrderByDescending ;
193+ methodName = ascending ? MethodOrderBy : MethodOrderByDescending ;
193194 }
194195
195196 orderings . Add ( new DynamicOrdering { Selector = expr , Ascending = ascending , MethodName = methodName } ) ;
@@ -404,11 +405,11 @@ private Expression ParseLogicalAndOrOperator()
404405 switch ( op . Id )
405406 {
406407 case TokenId . Ampersand :
407- if ( left . Type == typeof ( string ) && left . NodeType == ExpressionType . Constant && int . TryParse ( ( string ) ( ( ConstantExpression ) left ) . Value , out var parseValue ) && TypeHelper . IsNumericType ( right . Type ) )
408+ if ( left . Type == typeof ( string ) && left . NodeType == ExpressionType . Constant && int . TryParse ( ( string ? ) ( ( ConstantExpression ) left ) . Value , out var parseValue ) && TypeHelper . IsNumericType ( right . Type ) )
408409 {
409410 left = Expression . Constant ( parseValue ) ;
410411 }
411- else if ( right . Type == typeof ( string ) && right . NodeType == ExpressionType . Constant && int . TryParse ( ( string ) ( ( ConstantExpression ) right ) . Value , out parseValue ) && TypeHelper . IsNumericType ( left . Type ) )
412+ else if ( right . Type == typeof ( string ) && right . NodeType == ExpressionType . Constant && int . TryParse ( ( string ? ) ( ( ConstantExpression ) right ) . Value , out parseValue ) && TypeHelper . IsNumericType ( left . Type ) )
412413 {
413414 right = Expression . Constant ( parseValue ) ;
414415 }
@@ -1172,7 +1173,14 @@ private Expression ParseFunctionCast()
11721173 it = args [ 0 ] ;
11731174 }
11741175
1175- return Expression . ConvertChecked ( it , ResolveTypeFromArgumentExpression ( functionName , typeArgument , args . Length ) ) ;
1176+ var destinationType = ResolveTypeFromArgumentExpression ( functionName , typeArgument , args . Length ) ;
1177+
1178+ if ( TryGenerateConversion ( it , destinationType , out var conversionExpression ) )
1179+ {
1180+ return conversionExpression ;
1181+ }
1182+
1183+ return Expression . ConvertChecked ( it , destinationType ) ;
11761184 }
11771185
11781186 private Expression GenerateConditional ( Expression test , Expression expressionIfTrue , Expression expressionIfFalse , bool nullPropagating , int errorPos )
@@ -1360,9 +1368,9 @@ private Expression ParseNew()
13601368 && methodCallExpression . Arguments . Count == 1
13611369 && methodCallExpression . Arguments [ 0 ] is ConstantExpression methodCallExpressionArgument
13621370 && methodCallExpressionArgument . Type == typeof ( string )
1363- && properties . All ( x => x . Name != ( string ) methodCallExpressionArgument . Value ) )
1371+ && properties . All ( x => x . Name != ( string ? ) methodCallExpressionArgument . Value ) )
13641372 {
1365- propName = ( string ) methodCallExpressionArgument . Value ;
1373+ propName = ( string ? ) methodCallExpressionArgument . Value ;
13661374 }
13671375 else
13681376 {
@@ -1442,7 +1450,7 @@ private Expression CreateNewExpression(List<DynamicProperty> properties, List<Ex
14421450 // Create an expression tree that represents creating and initializing a one-dimensional array of type KeyValuePair<string, object>.
14431451 NewArrayExpression newArrayExpression = Expression . NewArrayInit ( typeof ( KeyValuePair < string , object > ) , arrayIndexParams ) ;
14441452
1445- // Get the "public DynamicClass(KeyValuePair<string, object>[] propertylist )" constructor
1453+ // Get the "public DynamicClass(KeyValuePair<string, object>[])" constructor
14461454 ConstructorInfo constructor = type . GetTypeInfo ( ) . DeclaredConstructors . First ( ) ;
14471455
14481456 return Expression . New ( constructor , newArrayExpression ) ;
@@ -1621,7 +1629,7 @@ private Expression ParseTypeAccess(Type type, bool getNext)
16211629 case 0 :
16221630 if ( args . Length == 1 && TryGenerateConversion ( args [ 0 ] , type , out generatedExpression ) )
16231631 {
1624- return generatedExpression ! ;
1632+ return generatedExpression ;
16251633 }
16261634
16271635 throw ParseError ( errorPos , Res . NoMatchingConstructor , TypeHelper . GetTypeName ( type ) ) ;
@@ -1640,56 +1648,61 @@ private Expression ParseTypeAccess(Type type, bool getNext)
16401648 return ParseMemberAccess ( type , null ) ;
16411649 }
16421650
1643- private bool TryGenerateConversion ( Expression sourceExpression , Type type , out Expression ? expression )
1651+ private bool TryGenerateConversion ( Expression sourceExpression , Type destinationType , [ NotNullWhen ( true ) ] out Expression ? expression )
16441652 {
16451653 Type exprType = sourceExpression . Type ;
1646- if ( exprType == type )
1654+ if ( exprType == destinationType )
16471655 {
16481656 expression = sourceExpression ;
16491657 return true ;
16501658 }
16511659
1652- if ( exprType . GetTypeInfo ( ) . IsValueType && type . GetTypeInfo ( ) . IsValueType )
1660+ if ( exprType . GetTypeInfo ( ) . IsValueType && destinationType . GetTypeInfo ( ) . IsValueType )
16531661 {
1654- if ( ( TypeHelper . IsNullableType ( exprType ) || TypeHelper . IsNullableType ( type ) ) && TypeHelper . GetNonNullableType ( exprType ) == TypeHelper . GetNonNullableType ( type ) )
1662+ if ( ( TypeHelper . IsNullableType ( exprType ) || TypeHelper . IsNullableType ( destinationType ) ) && TypeHelper . GetNonNullableType ( exprType ) == TypeHelper . GetNonNullableType ( destinationType ) )
16551663 {
1656- expression = Expression . Convert ( sourceExpression , type ) ;
1664+ expression = Expression . Convert ( sourceExpression , destinationType ) ;
16571665 return true ;
16581666 }
16591667
1660- if ( ( TypeHelper . IsNumericType ( exprType ) || TypeHelper . IsEnumType ( exprType ) ) && TypeHelper . IsNumericType ( type ) || TypeHelper . IsEnumType ( type ) )
1668+ if ( ( TypeHelper . IsNumericType ( exprType ) || TypeHelper . IsEnumType ( exprType ) ) && TypeHelper . IsNumericType ( destinationType ) || TypeHelper . IsEnumType ( destinationType ) )
16611669 {
1662- expression = Expression . ConvertChecked ( sourceExpression , type ) ;
1670+ expression = Expression . ConvertChecked ( sourceExpression , destinationType ) ;
16631671 return true ;
16641672 }
16651673 }
16661674
1667- if ( exprType . IsAssignableFrom ( type ) || type . IsAssignableFrom ( exprType ) || exprType . GetTypeInfo ( ) . IsInterface || type . GetTypeInfo ( ) . IsInterface )
1675+ if ( exprType . IsAssignableFrom ( destinationType ) || destinationType . IsAssignableFrom ( exprType ) || exprType . GetTypeInfo ( ) . IsInterface || destinationType . GetTypeInfo ( ) . IsInterface )
16681676 {
1669- expression = Expression . Convert ( sourceExpression , type ) ;
1677+ expression = Expression . Convert ( sourceExpression , destinationType ) ;
16701678 return true ;
16711679 }
16721680
16731681 // Try to Parse the string rather than just generate the convert statement
1674- if ( sourceExpression . NodeType == ExpressionType . Constant && exprType == typeof ( string ) )
1682+ if ( sourceExpression is ConstantExpression { Value : string constantStringValue } )
16751683 {
1676- string text = ( string ) ( ( ConstantExpression ) sourceExpression ) . Value ;
1677-
1678- var typeConvertor = _typeConverterFactory . GetConverter ( type ) ;
1679- // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
1680- if ( typeConvertor != null && typeConvertor . CanConvertFrom ( typeof ( string ) ) )
1684+ var typeConvertor = _typeConverterFactory . GetConverter ( destinationType ) ;
1685+ if ( typeConvertor . CanConvertFrom ( typeof ( string ) ) )
16811686 {
1682- var value = typeConvertor . ConvertFromInvariantString ( text ) ;
1683- expression = Expression . Constant ( value , type ) ;
1687+ var value = typeConvertor . ConvertFromInvariantString ( constantStringValue ) ;
1688+ expression = Expression . Constant ( value , destinationType ) ;
16841689 return true ;
16851690 }
16861691 }
16871692
16881693 // Check if there are any explicit conversion operators on the source type which fit the requirement (cast to the return type).
1689- bool explicitOperatorAvailable = exprType . GetTypeInfo ( ) . GetDeclaredMethods ( "op_Explicit" ) . Any ( m => m . ReturnType == type ) ;
1694+ bool explicitOperatorAvailable = exprType . GetTypeInfo ( ) . GetDeclaredMethods ( "op_Explicit" ) . Any ( m => m . ReturnType == destinationType ) ;
16901695 if ( explicitOperatorAvailable )
16911696 {
1692- expression = Expression . Convert ( sourceExpression , type ) ;
1697+ expression = Expression . Convert ( sourceExpression , destinationType ) ;
1698+ return true ;
1699+ }
1700+
1701+ // Try to find a destinationType.Parse(...) method for the specific sourceExpression Type.
1702+ var parseMethod = destinationType . GetMethod ( "Parse" , new Type [ ] { sourceExpression . Type } ) ;
1703+ if ( parseMethod != null )
1704+ {
1705+ expression = Expression . Call ( parseMethod , sourceExpression ) ;
16931706 return true ;
16941707 }
16951708
@@ -1788,7 +1801,7 @@ private Expression ParseMemberAccess(Type? type, Expression? expression)
17881801 if ( type == typeof ( object ) )
17891802 {
17901803 // The member is a dynamic or ExpandoObject, so convert this
1791- return _expressionHelper . ConvertToExpandoObjectAndCreateDynamicExpression ( expression , type , id ) ;
1804+ return _expressionHelper . ConvertToExpandoObjectAndCreateDynamicExpression ( expression ! , type , id ) ;
17921805 }
17931806#endif
17941807 // Parse as Lambda
@@ -1798,7 +1811,7 @@ private Expression ParseMemberAccess(Type? type, Expression? expression)
17981811 }
17991812
18001813 // This could be enum like "A.B.C.MyEnum.Value1" or "A.B.C+MyEnum.Value1"
1801- if ( _textParser . CurrentToken . Id == TokenId . Dot || _textParser . CurrentToken . Id == TokenId . Plus )
1814+ if ( _textParser . CurrentToken . Id is TokenId . Dot or TokenId . Plus )
18021815 {
18031816 return ParseAsEnum ( id ) ;
18041817 }
0 commit comments