Skip to content

Commit 035a6ad

Browse files
committed
Correction for #87 Parameter type mismatch is causing missing methods
1 parent d8f70fc commit 035a6ad

File tree

4 files changed

+63
-3
lines changed

4 files changed

+63
-3
lines changed

CodingSeb.ExpressionEvaluator.Tests/ExpressionEvaluatorTests.cs

+31
Original file line numberDiff line numberDiff line change
@@ -1548,6 +1548,19 @@ public void Evaluate_WithException_ThrowsExceptionAndDoesNotAssignItInObject()
15481548
objectContainer.AnObjectProperty.ShouldBe(10);
15491549
}
15501550

1551+
/// <summary>
1552+
/// To correct #87 Parameter type mismatch is causing missing methods
1553+
/// </summary>
1554+
[Test]
1555+
[Category("Bug")]
1556+
[Category("#87")]
1557+
public void Evaluate_ParameterTypeMismatchAutoAdapt()
1558+
{
1559+
var evaluator = new ExpressionEvaluator();
1560+
evaluator.Evaluate("DateTime.Now.AddDays(1)")
1561+
.ShouldBeOfType<DateTime>();
1562+
}
1563+
15511564
#endregion
15521565

15531566
#region EvaluateWithSpecificEvaluator
@@ -2240,6 +2253,24 @@ ExpressionEvaluator evaluatorForParamsTests()
22402253
.Returns("2,3.5,,Hello,True")
22412254
.SetCategory("ParamsKeywordMethod");
22422255

2256+
yield return new TestCaseData(evaluatorForParamsTests()
2257+
, "paramsObj.ReturnTrue(2)"
2258+
, null)
2259+
.Returns(true)
2260+
.SetCategory("ParamsKeywordMethod");
2261+
2262+
yield return new TestCaseData(evaluatorForParamsTests()
2263+
, "paramsObj.ReturnTrue(2, \"Hello\")"
2264+
, null)
2265+
.Returns(true)
2266+
.SetCategory("ParamsKeywordMethod");
2267+
2268+
yield return new TestCaseData(evaluatorForParamsTests()
2269+
, "paramsObj.ReturnTrue(2, \"Hello\", \"Test\")"
2270+
, null)
2271+
.Returns(true)
2272+
.SetCategory("ParamsKeywordMethod");
2273+
22432274
#endregion
22442275

22452276
#region Bug resolution

CodingSeb.ExpressionEvaluator.Tests/GlobalSuppressions.cs

+2
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@
1111
[assembly: SuppressMessage("Redundancy", "RCS1213:Remove unused member declaration.", Justification = "<Pending>", Scope = "namespaceanddescendants", Target = "~N:CodingSeb.ExpressionEvaluator.Tests")]
1212
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "<Pending>", Scope = "namespaceanddescendants", Target = "~N:CodingSeb.ExpressionEvaluator.Tests")]
1313
[assembly: SuppressMessage("Design", "RCS1170:Use read-only auto-implemented property.", Justification = "<Pending>", Scope = "namespaceanddescendants", Target = "~N:CodingSeb.ExpressionEvaluator.Tests")]
14+
[assembly: SuppressMessage("Redundancy", "RCS1163:Unused parameter.", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.ExpressionEvaluator.Tests.MethodArgKeywordClass.ReturnTrue(System.Double,System.String[])~System.Boolean")]
15+
[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "<Pending>", Scope = "member", Target = "~M:CodingSeb.ExpressionEvaluator.Tests.MethodArgKeywordClass.ReturnTrue(System.Double,System.String[])~System.Boolean")]

CodingSeb.ExpressionEvaluator.Tests/TestsUtils/MethodArgKeywordClass.cs

+5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public void Join(out string result, string separator, params object[] values)
2828
{
2929
result = string.Join(separator, values);
3030
}
31+
32+
public bool ReturnTrue(double dValue, params string[] values)
33+
{
34+
return true;
35+
}
3136
}
3237

3338
public static class MethodArgKeywordClassExtension

CodingSeb.ExpressionEvaluator/ExpressionEvaluator.cs

+25-3
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,21 @@ protected enum TryBlockEvaluatedState
130130
{ "void", typeof(void) }
131131
};
132132

133+
// Based on https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/y5b434w4(v=vs.110)?redirectedfrom=MSDN
134+
protected static readonly IDictionary<Type, Type[]> implicitCastDict = new Dictionary<Type, Type[]>
135+
{
136+
{ typeof(sbyte), new Type[] { typeof(short), typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
137+
{ typeof(byte), new Type[] { typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
138+
{ typeof(short), new Type[] { typeof(int), typeof(long), typeof(float), typeof(double), typeof(decimal) } },
139+
{ typeof(ushort), new Type[] { typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
140+
{ typeof(int), new Type[] { typeof(long), typeof(float), typeof(double), typeof(decimal) } },
141+
{ typeof(uint), new Type[] {typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
142+
{ typeof(long), new Type[] { typeof(float), typeof(double), typeof(decimal) } },
143+
{ typeof(char), new Type[] { typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(decimal) } },
144+
{ typeof(float), new Type[] { typeof(double) } },
145+
{ typeof(ulong), new Type[] { typeof(float), typeof(double), typeof(decimal) } },
146+
};
147+
133148
protected static readonly IDictionary<string, Func<string, CultureInfo, object>> numberSuffixToParse = new Dictionary<string, Func<string, CultureInfo, object>>(StringComparer.OrdinalIgnoreCase) // Always Case insensitive, like in C#
134149
{
135150
{ "f", (number, culture) => float.Parse(number, NumberStyles.Any, culture) },
@@ -3150,14 +3165,15 @@ protected virtual MethodInfo GetRealMethod(ref Type type, ref object obj, string
31503165
bool parameterValidate(ParameterInfo p) => p.Position >= modifiedArgs.Count
31513166
|| (testForExtention && p.Position == 0)
31523167
|| modifiedArgs[p.Position] == null
3153-
|| p.ParameterType.IsAssignableFrom(modifiedArgs[p.Position].GetType())
3168+
|| IsCastable(modifiedArgs[p.Position].GetType(), p.ParameterType)
31543169
|| typeof(Delegate).IsAssignableFrom(p.ParameterType)
31553170
|| p.IsDefined(typeof(ParamArrayAttribute))
31563171
|| (p.ParameterType.IsByRef && argsWithKeywords.Any(a => a.Index == p.Position + (testForExtention ? 1 : 0)));
31573172

31583173
bool methodByNameFilter(MethodInfo m) => m.Name.Equals(func, StringComparisonForCasing)
3159-
&& (m.GetParameters().Length == modifiedArgs.Count || m.GetParameters().Last().IsDefined(typeof(ParamArrayAttribute), false))
3160-
&& (typeCopy == typeof(Enumerable) || m.GetParameters().All(parameterValidate));
3174+
&& (m.GetParameters().Length == modifiedArgs.Count
3175+
|| (m.GetParameters().Last().IsDefined(typeof(ParamArrayAttribute), false)
3176+
&& m.GetParameters().All(parameterValidate)));
31613177

31623178
List<MethodInfo> methodInfos = type.GetMethods(flag)
31633179
.Where(methodByNameFilter)
@@ -3213,6 +3229,12 @@ bool methodByNameFilter(MethodInfo m) => m.Name.Equals(func, StringComparisonFor
32133229
return methodInfo;
32143230
}
32153231

3232+
protected virtual bool IsCastable(Type fromType, Type toType)
3233+
{
3234+
return toType.IsAssignableFrom(fromType)
3235+
|| (implicitCastDict.ContainsKey(fromType) && implicitCastDict[fromType].Contains(toType));
3236+
}
3237+
32163238
protected virtual MethodInfo TryToCastMethodParametersToMakeItCallable(MethodInfo methodInfoToCast, List<object> modifiedArgs, string genericsTypes, Type[] inferedGenericsTypes)
32173239
{
32183240
MethodInfo methodInfo = null;

0 commit comments

Comments
 (0)