NSubstitute
NSubstitute copied to clipboard
AmbiguousArgumentsException exception thrown if struct type in matcher has ctor with this call
Describe the bug
As soon as the readonly struct Path in the repro below has a call to another ctor the following AmbiguousArgumentsException is thrown:
NSubstitute.Exceptions.AmbiguousArgumentsException
HResult=0x80131500
Message=Cannot determine argument specifications to use. Please use specifications for all arguments of the same type.
Method signature:
Do<Parent, Base, Child>(Path<Base, Parent>, Func<Parent, Base>)
Method arguments (possible arg matchers are indicated with '*'):
Do<Parent, Base, Child>(NSubRepro.Path`2[NSubRepro.Base,NSubRepro.Parent], *<null>*)
All queued specifications:
any Path<Base, Parent>
any Func<Parent, Child>
Matched argument specifications:
Do<Parent, Base, Child>(NSubRepro.Path`2[NSubRepro.Base,NSubRepro.Parent], ???)
Source=NSubstitute
StackTrace:
at NSubstitute.Core.Arguments.ArgumentSpecificationsFactory.Create(IList`1 argumentSpecs, Object[] arguments, IParameterInfo[] parameterInfos, MethodInfo methodInfo, MatchArgs matchArgs)
at NSubstitute.Core.CallSpecificationFactory.CreateFrom(ICall call, MatchArgs matchArgs)
at NSubstitute.Routing.Handlers.RecordCallSpecificationHandler.Handle(ICall call)
at NSubstitute.Routing.Route.Handle(ICall call)
at NSubstitute.Core.CallRouter.Route(ICall call)
at NSubstitute.Proxies.CastleDynamicProxy.CastleForwardingInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at NSubstitute.Proxies.CastleDynamicProxy.ProxyIdInterceptor.Intercept(IInvocation invocation)
at Castle.DynamicProxy.AbstractInvocation.Proceed()
at Castle.Proxies.ObjectProxy.Do[TParent,TBaseChild,TChild](Path`2 parentPath, Func`2 relation)
at NSubRepro.Program.Main(String[] args) in D:\Repros\NSubRepro\NSubRepro\Program.cs:line 15
To Reproduce
Repro code:
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using NSubstitute;
namespace NSubRepro
{
internal class Program
{
static void Main(string[] args)
{
var testMock = Substitute.For<ITest>();
testMock.Do<Parent, Base, Child>(Arg.Any<Path<Base, Parent>>(), Arg.Any<Func<Parent, Child>>()).Returns(Task.CompletedTask);
}
}
public readonly struct Path<TVisit, TMatch>
{
private readonly ImmutableList<TVisit> _nodes;
// As soon as the call to the other ctor is commented out, it works
public Path() : this(ImmutableList<TVisit>.Empty)
{
}
private Path(TVisit node) : this(ImmutableList<TVisit>.Empty.Add(node))
{
}
private Path(ImmutableList<TVisit> nodes)
{
_nodes = nodes;
}
}
public interface ITest
{
Task Do<TParent, TBaseChild, TChild>(Path<Base,TParent> parentPath, Func<TParent, TBaseChild> relation)
where TParent : Base where TBaseChild : Base where TChild : TBaseChild;
}
public abstract class Base;
public sealed class Parent : Base
{
public List<Child> Children { get; } = [];
}
public sealed class Child : Base;
}
As soon as the this call in the Path ctor is commented, the exception is gone. I'm not sure how that call is responsible for that exception.
Expected behaviour No exception
Environment:
- NSubstitute version: 5.3.0
- NSubstitute.Analyzers version: Csharp 1.0.17
- Platform: .NET 9, x64 Windows 11