NOTE: this is not a dupe of #1148 - my problem code is not using generics.
Apologies in advance if this is unclear - the tests in the repro solution I've attached should be more eloquent than my writing.
The repro uses AutoMoq because I haven't been able to trigger this issue in Moq directly, but the stack trace generated points directly to Moq as the culprit.
Describe the Bug
Given an object <OuterService>
That has a constructor dependency on an interface <InnerDependency>
And <InnerDependency> defines a method that accepts a parameter decorated with the in modifier
And <OuterService> defines a method that calls the previously-mentioned method on <InnerDependency>
When attempting to AutoMoq <OuterService> using the following:
And yes, I'm aware that using in on an object parameter is only useful to force the compiler to disallow reassignment of said parameter in said method. But that's precisely what I'm using it for: enforcing correctness.
The problem appears to surface if all of the following conditions hold:
The mock is configured with CallBase = true.
The mock is configured with DefaultValue = DefaultValue.Mock.
The invoked method's return type can be mocked.
The invoked method has an in parameter of any type.
(1) and (2) are necessary to get to this line of code. (3) is necessary to get past the ifs at that location in order to arrive at this line of code. And (4) is necessary to trigger the type mismatch there that you've already correctly identified.
(I used a few more test cases to track down this issue, click here to see them.)
Your suggested fix is almost correct. I suspect that the only thing still wrong with it is that you're checking for .HasElementType, which will also yield true for array and pointer types. We don't need to care about pointer types much (since Moq has never claimed to support unsafe code), but we shouldn't turn array types E, E, etc. into E. In order to only target by-ref types in/ref/out E, the check should be .IsByRef instead. Also, no while loop is needed to unpack the non-by-ref type, since the usual .NET languages don't produce nor support types such as ref ref E.