From 141017805f25d332f00370c27de19105606d67de Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:34:45 +0000 Subject: [PATCH 01/93] Enable nullable warnings in Moq.csproj. --- src/Moq/Moq.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Moq/Moq.csproj b/src/Moq/Moq.csproj index b7bd529a0..62019118e 100644 --- a/src/Moq/Moq.csproj +++ b/src/Moq/Moq.csproj @@ -11,6 +11,8 @@ $(DefineConstants);FEATURE_DEFAULT_INTERFACE_IMPLEMENTATIONS + enable + nullable From bbd54e2bd6906be80864dc130e274f9863aaa0c4 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:36:23 +0000 Subject: [PATCH 02/93] Fix up nullability of ExpressionComparer.cs. --- src/Moq/ExpressionComparer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/ExpressionComparer.cs b/src/Moq/ExpressionComparer.cs index 940089a5f..3400f098d 100644 --- a/src/Moq/ExpressionComparer.cs +++ b/src/Moq/ExpressionComparer.cs @@ -21,7 +21,7 @@ sealed class ExpressionComparer : IEqualityComparer { } - public bool Equals(Expression x, Expression y) + public bool Equals(Expression? x, Expression? y) { if (object.ReferenceEquals(x, y)) { @@ -137,7 +137,7 @@ public bool Equals(Expression x, Expression y) public int GetHashCode(Expression obj) { - return obj == null ? 0 : obj.GetHashCode(); + return obj.GetHashCode(); } static bool Equals(ReadOnlyCollection x, ReadOnlyCollection y, Func comparer) From 17cd46db5a5fcb966429e63fc3b741f105cbddec Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:37:03 +0000 Subject: [PATCH 03/93] Fix up nullability in ActionObserver.cs. --- src/Moq/ActionObserver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/ActionObserver.cs b/src/Moq/ActionObserver.cs index 128dce6d1..73550d75f 100644 --- a/src/Moq/ActionObserver.cs +++ b/src/Moq/ActionObserver.cs @@ -27,7 +27,7 @@ namespace Moq /// sealed class ActionObserver : ExpressionReconstructor { - public override Expression> ReconstructExpression(Action action, object[] ctorArgs = null) + public override Expression> ReconstructExpression(Action action, object[]? ctorArgs = null) { using (var matcherObserver = MatcherObserver.Activate()) { @@ -228,7 +228,7 @@ bool CanDistribute(int msi, int asi) } // Creates a proxy (way more light-weight than a `Mock`!) with an invocation `Recorder` attached to it. - static IProxy CreateProxy(Type type, object[] ctorArgs, MatcherObserver matcherObserver, out Recorder recorder) + static IProxy CreateProxy(Type type, object[]? ctorArgs, MatcherObserver matcherObserver, out Recorder recorder) { recorder = new Recorder(matcherObserver); return (IProxy)ProxyFactory.Instance.CreateProxy(type, recorder, Type.EmptyTypes, ctorArgs ?? new object[0]); From 26e6e85398c06d94e28aa693c6a34d9fbb28bead Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:37:53 +0000 Subject: [PATCH 04/93] Fix up nullability in Match.cs. --- src/Moq/Match.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/Match.cs b/src/Moq/Match.cs index 00707390f..70a50d9c5 100644 --- a/src/Moq/Match.cs +++ b/src/Moq/Match.cs @@ -163,9 +163,9 @@ internal static void Register(Match match) public class Match : Match, IEquatable> { internal Predicate Condition { get; set; } - internal Action Success { get; set; } + internal Action? Success { get; set; } - internal Match(Predicate condition, Expression> renderExpression, Action success = null) + internal Match(Predicate condition, Expression> renderExpression, Action? success = null) { this.Condition = condition; this.RenderExpression = renderExpression.Body.Apply(EvaluateCaptures.Rewriter); From 4ec4e53f59656bfc075c5ced9e38b6270747d09f Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:38:44 +0000 Subject: [PATCH 05/93] Fix up nullability in IProtectedAsMock.cs. --- src/Moq/Protected/IProtectedAsMock.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Moq/Protected/IProtectedAsMock.cs b/src/Moq/Protected/IProtectedAsMock.cs index 878faaf3b..d22feda8c 100644 --- a/src/Moq/Protected/IProtectedAsMock.cs +++ b/src/Moq/Protected/IProtectedAsMock.cs @@ -111,11 +111,11 @@ public interface IProtectedAsMock : IFluentInterface /// Lambda expression that specifies the method invocation. /// /// Number of times that the invocation is expected to have occurred. - /// If omitted, assumed to be . /// + /// If omitted, assumed to be . /// Message to include in the thrown if verification fails. /// The specified invocation did not occur (or did not occur the specified number of times). - void Verify(Expression> expression, Times? times = null, string failMessage = null); + void Verify(Expression> expression, Times? times = null, string? failMessage = null); /// /// Verifies that a specific invocation matching the given expression was performed on the mock. @@ -129,7 +129,7 @@ public interface IProtectedAsMock : IFluentInterface /// /// Message to include in the thrown if verification fails. /// The specified invocation did not occur (or did not occur the specified number of times). - void Verify(Expression> expression, Times? times = null, string failMessage = null); + void Verify(Expression> expression, Times? times = null, string? failMessage = null); /// /// Verifies that a property was set on the mock. @@ -143,7 +143,7 @@ public interface IProtectedAsMock : IFluentInterface /// /// The invocation was not called the number of times specified by . /// - void VerifySet(Action setterExpression, Times? times = null, string failMessage = null); + void VerifySet(Action setterExpression, Times? times = null, string? failMessage = null); /// /// Verifies that a property was read on the mock. @@ -156,6 +156,6 @@ public interface IProtectedAsMock : IFluentInterface /// /// Message to include in the thrown if verification fails. /// The specified invocation did not occur (or did not occur the specified number of times). - void VerifyGet(Expression> expression, Times? times = null, string failMessage = null); + void VerifyGet(Expression> expression, Times? times = null, string? failMessage = null); } } From c738dbb19f9ccefb48f94a3c916d7a21baa2934e Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:43:45 +0000 Subject: [PATCH 06/93] Fix up nullability in MethodExpectation.cs. --- src/Moq/MethodExpectation.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Moq/MethodExpectation.cs b/src/Moq/MethodExpectation.cs index 4112a4c10..2fa843de4 100644 --- a/src/Moq/MethodExpectation.cs +++ b/src/Moq/MethodExpectation.cs @@ -68,16 +68,16 @@ public static MethodExpectation CreateFrom(Invocation invocation) public readonly IReadOnlyList Arguments; readonly IMatcher[] argumentMatchers; - IAwaitableFactory awaitableFactory; - MethodInfo methodImplementation; - Expression[] partiallyEvaluatedArguments; + IAwaitableFactory? awaitableFactory; + MethodInfo? methodImplementation; + Expression[]? partiallyEvaluatedArguments; #if DEBUG - Type proxyType; + Type? proxyType; #endif readonly bool exactGenericTypeArguments; - public MethodExpectation(LambdaExpression expression, MethodInfo method, IReadOnlyList arguments = null, bool exactGenericTypeArguments = false, bool skipMatcherInitialization = false, bool allowNonOverridable = false) + public MethodExpectation(LambdaExpression expression, MethodInfo method, IReadOnlyList? arguments = null, bool exactGenericTypeArguments = false, bool skipMatcherInitialization = false, bool allowNonOverridable = false) { Debug.Assert(expression != null); Debug.Assert(method != null); From 05b0ef8507b5d20af96f7974ae074ecec056cc33 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:45:54 +0000 Subject: [PATCH 07/93] Fix up some nullable issues in ProtectedAsMock.cs. --- src/Moq/Protected/ProtectedAsMock.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Moq/Protected/ProtectedAsMock.cs b/src/Moq/Protected/ProtectedAsMock.cs index c3d61024f..4ebf84276 100644 --- a/src/Moq/Protected/ProtectedAsMock.cs +++ b/src/Moq/Protected/ProtectedAsMock.cs @@ -153,7 +153,7 @@ public ISetupSequentialAction SetupSequence(Expression> expressi return new SetupSequencePhrase(setup); } - public void Verify(Expression> expression, Times? times = null, string failMessage = null) + public void Verify(Expression> expression, Times? times = null, string? failMessage = null) { Guard.NotNull(expression, nameof(expression)); @@ -170,7 +170,7 @@ public void Verify(Expression> expression, Times? times = null, Mock.Verify(this.mock, rewrittenExpression, times ?? Times.AtLeastOnce(), failMessage); } - public void Verify(Expression> expression, Times? times = null, string failMessage = null) + public void Verify(Expression> expression, Times? times = null, string? failMessage = null) { Guard.NotNull(expression, nameof(expression)); @@ -187,7 +187,7 @@ public void Verify(Expression> expression, Times Mock.Verify(this.mock, rewrittenExpression, times ?? Times.AtLeastOnce(), failMessage); } - public void VerifySet(Action setterExpression, Times? times = null, string failMessage = null) + public void VerifySet(Action setterExpression, Times? times = null, string? failMessage = null) { Guard.NotNull(setterExpression, nameof(setterExpression)); @@ -195,7 +195,7 @@ public void VerifySet(Action setterExpression, Times? times = null, str Mock.VerifySet(mock, rewrittenExpression, times.HasValue ? times.Value : Times.AtLeastOnce(), failMessage); } - public void VerifyGet(Expression> expression, Times? times = null, string failMessage = null) + public void VerifyGet(Expression> expression, Times? times = null, string? failMessage = null) { Guard.NotNull(expression, nameof(expression)); From 18e987e9d18fdf5314c5b92860970d6f53981970 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:55:19 +0000 Subject: [PATCH 08/93] Fix up nullability in AwaitableFactory*.cs. --- src/Moq/Async/AwaitableFactory`1.cs | 5 +++-- src/Moq/Async/AwaitableFactory`2.cs | 9 +++++---- src/Moq/Async/IAwaitableFactory.cs | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Moq/Async/AwaitableFactory`1.cs b/src/Moq/Async/AwaitableFactory`1.cs index 3b88a9194..09dfda2aa 100644 --- a/src/Moq/Async/AwaitableFactory`1.cs +++ b/src/Moq/Async/AwaitableFactory`1.cs @@ -14,12 +14,13 @@ namespace Moq.Async /// for awaitables that do not produce a result when awaited. /// abstract class AwaitableFactory : IAwaitableFactory + where TAwaitable : notnull { Type IAwaitableFactory.ResultType => typeof(void); public abstract TAwaitable CreateCompleted(); - object IAwaitableFactory.CreateCompleted(object result) + object IAwaitableFactory.CreateCompleted(object? result) { Debug.Assert(result == null); @@ -50,7 +51,7 @@ Expression IAwaitableFactory.CreateResultExpression(Expression awaitableExpressi return new AwaitExpression(awaitableExpression, this); } - bool IAwaitableFactory.TryGetResult(object awaitable, out object result) + bool IAwaitableFactory.TryGetResult(object awaitable, out object? result) { Debug.Assert(awaitable is TAwaitable); diff --git a/src/Moq/Async/AwaitableFactory`2.cs b/src/Moq/Async/AwaitableFactory`2.cs index a0476053e..e953f0cbe 100644 --- a/src/Moq/Async/AwaitableFactory`2.cs +++ b/src/Moq/Async/AwaitableFactory`2.cs @@ -14,16 +14,17 @@ namespace Moq.Async /// for awaitables that produce a result when awaited. /// abstract class AwaitableFactory : IAwaitableFactory + where TAwaitable : notnull { public Type ResultType => typeof(TResult); - public abstract TAwaitable CreateCompleted(TResult result); + public abstract TAwaitable CreateCompleted(TResult? result); - object IAwaitableFactory.CreateCompleted(object result) + object IAwaitableFactory.CreateCompleted(object? result) { Debug.Assert(result is TResult || result == null); - return this.CreateCompleted((TResult)result); + return this.CreateCompleted((TResult?)result); } public abstract TAwaitable CreateFaulted(Exception exception); @@ -49,7 +50,7 @@ object IAwaitableFactory.CreateFaulted(IEnumerable exceptions) public abstract Expression CreateResultExpression(Expression awaitableExpression); - bool IAwaitableFactory.TryGetResult(object awaitable, out object result) + bool IAwaitableFactory.TryGetResult(object awaitable, out object? result) { Debug.Assert(awaitable is TAwaitable); diff --git a/src/Moq/Async/IAwaitableFactory.cs b/src/Moq/Async/IAwaitableFactory.cs index 8af39e826..633cea616 100644 --- a/src/Moq/Async/IAwaitableFactory.cs +++ b/src/Moq/Async/IAwaitableFactory.cs @@ -11,7 +11,7 @@ interface IAwaitableFactory { Type ResultType { get; } - object CreateCompleted(object result = null); + object CreateCompleted(object? result = null); object CreateFaulted(Exception exception); @@ -19,6 +19,6 @@ interface IAwaitableFactory Expression CreateResultExpression(Expression awaitableExpression); - bool TryGetResult(object awaitable, out object result); + bool TryGetResult(object awaitable, out object? result); } } From 69727d4dc5422e406d1e7195aecdf8399098ccba Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:56:00 +0000 Subject: [PATCH 09/93] Fix up nullability in Awaitable.cs. --- src/Moq/Async/Awaitable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Async/Awaitable.cs b/src/Moq/Async/Awaitable.cs index 192d64aea..9afe9df9b 100644 --- a/src/Moq/Async/Awaitable.cs +++ b/src/Moq/Async/Awaitable.cs @@ -14,7 +14,7 @@ static class Awaitable /// this method will return 42. /// /// The (possibly awaitable) object to be "unwrapped". - public static object TryGetResultRecursive(object obj) + public static object? TryGetResultRecursive(object? obj) { if (obj != null && AwaitableFactory.TryGet(obj.GetType()) is { } awaitableFactory From 8f1ea9ebbe746b8c5872dbd6f31ccf3f7331cb38 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 12:57:40 +0000 Subject: [PATCH 10/93] Fix up nullability in TypeMatcherAttribute.cs. --- src/Moq/TypeMatcherAttribute.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Moq/TypeMatcherAttribute.cs b/src/Moq/TypeMatcherAttribute.cs index 7e7db6402..96a0a6834 100644 --- a/src/Moq/TypeMatcherAttribute.cs +++ b/src/Moq/TypeMatcherAttribute.cs @@ -16,7 +16,7 @@ namespace Moq [AttributeUsage(AttributeTargets.Class | AttributeTargets.Delegate | AttributeTargets.Enum | AttributeTargets.Interface | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] public class TypeMatcherAttribute : Attribute { - readonly Type type; + readonly Type? type; /// /// Initializes a new instance of the class. @@ -39,14 +39,9 @@ public TypeMatcherAttribute() /// The of a type that implements . public TypeMatcherAttribute(Type type) { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - this.type = type; + this.type = type ?? throw new ArgumentNullException(nameof(type)); } - internal Type Type => this.type; + internal Type? Type => this.type; } } From 44bfa2d8c224306d512d4cb4e41aaa6ca1b5dbc8 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 13:19:29 +0000 Subject: [PATCH 11/93] Fix up nullability in Extensions.cs. A new symbol has been defined for ease of use of nullability checks. --- src/Moq/Extensions.cs | 58 +++++++++++++++++++++++++++++-------------- src/Moq/Moq.csproj | 2 +- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/Moq/Extensions.cs b/src/Moq/Extensions.cs index 0a920c72f..ba68add37 100644 --- a/src/Moq/Extensions.cs +++ b/src/Moq/Extensions.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -19,13 +20,21 @@ public static bool CanCreateInstance(this Type type) { return type.IsValueType || type.GetConstructor(Type.EmptyTypes) != null; } - - public static bool CanRead(this PropertyInfo property, out MethodInfo getter) + +#if NULLABLE_REFERENCE_TYPES + public static bool CanRead(this PropertyInfo property, [NotNullWhen(true)] out MethodInfo? getter) +#else + public static bool CanRead(this PropertyInfo property, out MethodInfo? getter) +#endif { return property.CanRead(out getter, out _); } - public static bool CanRead(this PropertyInfo property, out MethodInfo getter, out PropertyInfo getterProperty) +#if NULLABLE_REFERENCE_TYPES + public static bool CanRead(this PropertyInfo property, [NotNullWhen(true)] out MethodInfo? getter, [NotNullWhen(true)] out PropertyInfo? getterProperty) +#else + public static bool CanRead(this PropertyInfo property, out MethodInfo? getter, out PropertyInfo? getterProperty) +#endif { if (property.CanRead) { @@ -63,12 +72,21 @@ public static bool CanRead(this PropertyInfo property, out MethodInfo getter, ou return false; } - public static bool CanWrite(this PropertyInfo property, out MethodInfo setter) +#if NULLABLE_REFERENCE_TYPES + public static bool CanWrite(this PropertyInfo property, [NotNullWhen(true)] out MethodInfo? setter) +#else + public static bool CanWrite(this PropertyInfo property, out MethodInfo? setter) +#endif { return property.CanWrite(out setter, out _); } - public static bool CanWrite(this PropertyInfo property, out MethodInfo setter, out PropertyInfo setterProperty) +#if NULLABLE_REFERENCE_TYPES + public static bool CanWrite(this PropertyInfo property, [NotNullWhen(true)] out MethodInfo? setter, [NotNullWhen(true)] out PropertyInfo? setterProperty) + +#else + public static bool CanWrite(this PropertyInfo property, out MethodInfo? setter, out PropertyInfo? setterProperty) +#endif { if (property.CanWrite) { @@ -94,7 +112,7 @@ public static bool CanWrite(this PropertyInfo property, out MethodInfo setter, o var baseProperty = baseGetter .DeclaringType - .GetMember(property.Name, MemberTypes.Property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + !.GetMember(property.Name, MemberTypes.Property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Cast() .First(p => p.GetGetMethod(nonPublic: true) == baseGetter); return baseProperty.CanWrite(out setter, out setterProperty); @@ -109,7 +127,7 @@ public static bool CanWrite(this PropertyInfo property, out MethodInfo setter, o /// /// Gets the default value for the specified type. This is the Reflection counterpart of C#'s operator. /// - public static object GetDefaultValue(this Type type) + public static object? GetDefaultValue(this Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } @@ -129,20 +147,20 @@ public static MethodInfo GetImplementingMethod(this MethodInfo method, Type prox method = method.GetGenericMethodDefinition(); } - var declaringType = method.DeclaringType; + var declaringType = method.DeclaringType!; if (declaringType.IsInterface) { Debug.Assert(declaringType.IsAssignableFrom(proxyType)); - var map = GetInterfaceMap(proxyType, method.DeclaringType); + var map = GetInterfaceMap(proxyType, declaringType); var index = Array.IndexOf(map.InterfaceMethods, method); Debug.Assert(index >= 0); return map.TargetMethods[index].GetBaseDefinition(); } else if (declaringType.IsDelegateType()) { - return proxyType.GetMethod("Invoke"); + return proxyType.GetMethod("Invoke")!; } else { @@ -152,7 +170,7 @@ public static MethodInfo GetImplementingMethod(this MethodInfo method, Type prox } } - public static object InvokePreserveStack(this Delegate del, IReadOnlyList args = null) + public static object InvokePreserveStack(this Delegate del, IReadOnlyList? args = null) { try { @@ -160,7 +178,7 @@ public static object InvokePreserveStack(this Delegate del, IReadOnlyList(this Delegate function, return false; } - static MethodInfo GetInvokeMethodFromUntypedDelegateCallback(Delegate callback) + static MethodInfo? GetInvokeMethodFromUntypedDelegateCallback(Delegate callback) { // Section 8.9.3 of 4th Ed ECMA 335 CLI spec requires delegates to have an 'Invoke' method. // However, there is not a requirement for 'public', or for it to be unambiguous. @@ -415,8 +437,8 @@ public static Type SubstituteTypeMatchers(this Type type, Type other) } else if (type.HasElementType && other.HasElementType) { - var te = type.GetElementType(); - var oe = other.GetElementType(); + var te = type.GetElementType()!; + var oe = other.GetElementType()!; if (type.IsArray && other.IsArray) { diff --git a/src/Moq/Moq.csproj b/src/Moq/Moq.csproj index 62019118e..e74667224 100644 --- a/src/Moq/Moq.csproj +++ b/src/Moq/Moq.csproj @@ -10,7 +10,7 @@ - $(DefineConstants);FEATURE_DEFAULT_INTERFACE_IMPLEMENTATIONS + $(DefineConstants);NULLABLE_REFERENCE_TYPES;FEATURE_DEFAULT_INTERFACE_IMPLEMENTATIONS enable nullable From 46c00d49237051d8eed5c9665c521eeeb5979ae7 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 13:21:14 +0000 Subject: [PATCH 12/93] Fix up nullability in RaiseEvent.cs. --- src/Moq/Behaviors/RaiseEvent.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Moq/Behaviors/RaiseEvent.cs b/src/Moq/Behaviors/RaiseEvent.cs index 8707b00ea..1aad4760f 100644 --- a/src/Moq/Behaviors/RaiseEvent.cs +++ b/src/Moq/Behaviors/RaiseEvent.cs @@ -11,10 +11,10 @@ sealed class RaiseEvent : Behavior { Mock mock; LambdaExpression expression; - Delegate eventArgsFunc; - object[] eventArgsParams; + Delegate? eventArgsFunc; + object[]? eventArgsParams; - public RaiseEvent(Mock mock, LambdaExpression expression, Delegate eventArgsFunc, object[] eventArgsParams) + public RaiseEvent(Mock mock, LambdaExpression expression, Delegate? eventArgsFunc, object[]? eventArgsParams) { Debug.Assert(mock != null); Debug.Assert(expression != null); @@ -36,7 +36,7 @@ public override void Execute(Invocation invocation) } else { - var argsFuncType = this.eventArgsFunc.GetType(); + var argsFuncType = this.eventArgsFunc!.GetType(); if (argsFuncType.IsGenericType && argsFuncType.GetGenericArguments().Length == 1) { args = new object[] { this.mock.Object, this.eventArgsFunc.InvokePreserveStack() }; From b634569c1557c052c3511f4f00b8b49318a7e7ed Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 13:27:12 +0000 Subject: [PATCH 13/93] Fix up nullability in Expectation.cs and MethodExpectation.cs. --- src/Moq/Expectation.cs | 12 +++++++++--- src/Moq/MethodExpectation.cs | 13 +++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Moq/Expectation.cs b/src/Moq/Expectation.cs index 7557a7e37..c69f80533 100644 --- a/src/Moq/Expectation.cs +++ b/src/Moq/Expectation.cs @@ -2,6 +2,7 @@ // All rights reserved. Licensed under the BSD 3-Clause License; see License.txt. using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using Moq.Async; @@ -16,18 +17,23 @@ abstract class Expectation : IEquatable { public abstract LambdaExpression Expression { get; } - public virtual bool HasResultExpression(out IAwaitableFactory awaitableFactory) +#if NULLABLE_REFERENCE_TYPES + + public virtual bool HasResultExpression([NotNullWhen(true)] out IAwaitableFactory? awaitableFactory) +#else + public virtual bool HasResultExpression(out IAwaitableFactory? awaitableFactory) +#endif { awaitableFactory = null; return false; } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Expectation other && this.Equals(other); } - public abstract bool Equals(Expectation other); + public abstract bool Equals(Expectation? other); public abstract override int GetHashCode(); diff --git a/src/Moq/MethodExpectation.cs b/src/Moq/MethodExpectation.cs index 2fa843de4..9b3af6828 100644 --- a/src/Moq/MethodExpectation.cs +++ b/src/Moq/MethodExpectation.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -38,14 +39,14 @@ public static MethodExpectation CreateFrom(Invocation invocation) for (int i = 0; i < n; ++i) { var parameterType = parameterTypes[i]; - if (parameterType.IsByRef) parameterType = parameterType.GetElementType(); + if (parameterType.IsByRef) parameterType = parameterType.GetElementType()!; arguments[i] = E.Constant(invocation.Arguments[i], parameterType); } } LambdaExpression expression; { - var mock = E.Parameter(method.DeclaringType, "mock"); + var mock = E.Parameter(method.DeclaringType!, "mock"); expression = E.Lambda(E.Call(mock, method, arguments).Apply(UpgradePropertyAccessorMethods.Rewriter), mock); } @@ -111,7 +112,11 @@ public void AddResultExpression(Func add, IAwaitableFactory awaitableFacto this.awaitableFactory = awaitableFactory; } - public override bool HasResultExpression(out IAwaitableFactory awaitableFactory) +#if NULLABLE_REFERENCE_TYPES + public override bool HasResultExpression([NotNullWhen(true)] out IAwaitableFactory? awaitableFactory) +#else + public override bool HasResultExpression(out IAwaitableFactory? awaitableFactory) +#endif { return (awaitableFactory = this.awaitableFactory) != null; } @@ -199,7 +204,7 @@ bool IsOverride(Invocation invocation) return true; } - public override bool Equals(Expectation obj) + public override bool Equals(Expectation? obj) { if (obj is not MethodExpectation other) return false; From 393279deaddeec27938611619efd8f90f5bb1bbc Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 13:41:08 +0000 Subject: [PATCH 14/93] Fix up AwaitableFactory`2.CreateCompleted(object?) to correctly assume non-nullability. --- src/Moq/Async/AwaitableFactory`2.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Moq/Async/AwaitableFactory`2.cs b/src/Moq/Async/AwaitableFactory`2.cs index e953f0cbe..bb0e3e212 100644 --- a/src/Moq/Async/AwaitableFactory`2.cs +++ b/src/Moq/Async/AwaitableFactory`2.cs @@ -18,13 +18,14 @@ abstract class AwaitableFactory : IAwaitableFactory { public Type ResultType => typeof(TResult); - public abstract TAwaitable CreateCompleted(TResult? result); + public abstract TAwaitable CreateCompleted(TResult result); object IAwaitableFactory.CreateCompleted(object? result) { + // TODO: result should only be null if TResult is a nullable type. Debug.Assert(result is TResult || result == null); - return this.CreateCompleted((TResult?)result); + return this.CreateCompleted((TResult)result!); } public abstract TAwaitable CreateFaulted(Exception exception); From 0c2c4cc851b622d41b67859563873102e9dfa903 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 13:42:40 +0000 Subject: [PATCH 15/93] Simplify TaskFactory.CreateCompleted. --- src/Moq/Async/TaskFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Async/TaskFactory.cs b/src/Moq/Async/TaskFactory.cs index d43276181..303f7f820 100644 --- a/src/Moq/Async/TaskFactory.cs +++ b/src/Moq/Async/TaskFactory.cs @@ -19,7 +19,7 @@ sealed class TaskFactory : AwaitableFactory public override Task CreateCompleted() { - return Task.FromResult(default); + return Task.CompletedTask; } public override Task CreateFaulted(Exception exception) From c0989d77a46acda2bccb7b3aed58acac5b6ccc2e Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 13:45:45 +0000 Subject: [PATCH 16/93] Fix nullability for AsInterface.cs. --- src/Moq/AsInterface.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/AsInterface.cs b/src/Moq/AsInterface.cs index db6b6636e..dd4ae509f 100644 --- a/src/Moq/AsInterface.cs +++ b/src/Moq/AsInterface.cs @@ -49,7 +49,7 @@ public override DefaultValueProvider DefaultValueProvider public override TInterface Object { - get { return this.owner.Object as TInterface; } + get { return (TInterface) this.owner.Object; } } internal override SetupCollection MutableSetups => this.owner.MutableSetups; @@ -72,7 +72,7 @@ protected override object OnGetObject() public override string ToString() { - return this.owner.ToString(); + return this.owner.ToString()!; } } } From 3e499c0e766eca5f7dbea983e8522dd8690a3239 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 13:54:37 +0000 Subject: [PATCH 17/93] Fix up nullability in EventHandlerCollection.cs. --- src/Moq/EventHandlerCollection.cs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/Moq/EventHandlerCollection.cs b/src/Moq/EventHandlerCollection.cs index ceb7be911..35749ec2f 100644 --- a/src/Moq/EventHandlerCollection.cs +++ b/src/Moq/EventHandlerCollection.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; namespace Moq @@ -36,19 +37,31 @@ public void Remove(EventInfo @event, Delegate eventHandler) { lock (this.eventHandlers) { - this.eventHandlers[@event] = Delegate.Remove(this.TryGet(@event), eventHandler); + var resultingDelegate = Delegate.Remove(this.TryGet(@event), eventHandler); + if (resultingDelegate == null) + { + eventHandlers.Remove(@event); + } + else + { + eventHandlers[@event] = resultingDelegate; + } } } - public bool TryGet(EventInfo @event, out Delegate handlers) +#if NULLABLE_REFERENCE_TYPES + public bool TryGet(EventInfo @event, [NotNullWhen(true)] out Delegate? handlers) +#else + public bool TryGet(EventInfo @event, out Delegate? handlers) +#endif { lock (this.eventHandlers) { - return this.eventHandlers.TryGetValue(@event, out handlers) && handlers != null; + return this.eventHandlers.TryGetValue(@event, out handlers); } } - Delegate TryGet(EventInfo @event) + Delegate? TryGet(EventInfo @event) { return this.eventHandlers.TryGetValue(@event, out var handlers) ? handlers : null; } From aa15e961b3d3fd6baff99702a479c3926c138b0f Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:18:40 +0000 Subject: [PATCH 18/93] Fix up nullable types in ExpressionReconstructor.cs. --- src/Moq/ExpressionReconstructor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/ExpressionReconstructor.cs b/src/Moq/ExpressionReconstructor.cs index 27a9f529c..c70600ccd 100644 --- a/src/Moq/ExpressionReconstructor.cs +++ b/src/Moq/ExpressionReconstructor.cs @@ -29,6 +29,6 @@ protected ExpressionReconstructor() /// /// The delegate for which to reconstruct a LINQ expression tree. /// Arguments to pass to a parameterized constructor of . (Optional.) - public abstract Expression> ReconstructExpression(Action action, object[] ctorArgs = null); + public abstract Expression> ReconstructExpression(Action action, object[]? ctorArgs = null); } } From 059e59ab0a738a623237153ad9b5eac8ae85af11 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:23:21 +0000 Subject: [PATCH 19/93] Fix nullability in Condition.cs. Add guard clause in Mock.When as nothing ever calls `new Condition(null)` so we can simply rely on nullable annotations, so long as we guard the public interface. --- src/Moq/Condition.cs | 6 +++--- src/Moq/Mock`1.cs | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Moq/Condition.cs b/src/Moq/Condition.cs index 70ab3b434..c245974a8 100644 --- a/src/Moq/Condition.cs +++ b/src/Moq/Condition.cs @@ -8,15 +8,15 @@ namespace Moq sealed class Condition { Func condition; - Action success; + Action? success; - public Condition(Func condition, Action success = null) + public Condition(Func condition, Action? success = null) { this.condition = condition; this.success = success; } - public bool IsTrue => this.condition?.Invoke() == true; + public bool IsTrue => this.condition.Invoke(); public void SetupEvaluatedSuccessfully() => this.success?.Invoke(); } diff --git a/src/Moq/Mock`1.cs b/src/Moq/Mock`1.cs index d9e697875..54a5f295c 100644 --- a/src/Moq/Mock`1.cs +++ b/src/Moq/Mock`1.cs @@ -680,6 +680,11 @@ public ISetupSequentialAction SetupSequence(Expression> expression) /// public ISetupConditionResult When(Func condition) { + if (condition == null) + { + throw new ArgumentNullException(nameof(condition)); + } + return new WhenPhrase(this, new Condition(condition)); } From 1fa5dda7ec39c7bb2661d80d0f8e9c87ecdbe889 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:25:33 +0000 Subject: [PATCH 20/93] Fix up nullability in StubbedPropertySetup.cs. --- src/Moq/StubbedPropertySetup.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Moq/StubbedPropertySetup.cs b/src/Moq/StubbedPropertySetup.cs index eb1bb2283..363ad9a4b 100644 --- a/src/Moq/StubbedPropertySetup.cs +++ b/src/Moq/StubbedPropertySetup.cs @@ -70,10 +70,10 @@ protected override void VerifySelf() sealed class PropertyAccessorExpectation : Expectation { readonly LambdaExpression expression; - readonly MethodInfo getter; - readonly MethodInfo setter; + readonly MethodInfo? getter; + readonly MethodInfo? setter; - public PropertyAccessorExpectation(LambdaExpression expression, MethodInfo getter, MethodInfo setter) + public PropertyAccessorExpectation(LambdaExpression expression, MethodInfo? getter, MethodInfo? setter) { Debug.Assert(expression != null); Debug.Assert(expression.IsProperty()); @@ -86,7 +86,7 @@ public PropertyAccessorExpectation(LambdaExpression expression, MethodInfo gette public override LambdaExpression Expression => this.expression; - public override bool Equals(Expectation obj) + public override bool Equals(Expectation? obj) { return obj is PropertyAccessorExpectation other && other.getter == this.getter @@ -101,7 +101,7 @@ public override int GetHashCode() public override bool IsMatch(Invocation invocation) { var methodName = invocation.Method.Name; - return methodName == this.getter.Name || methodName == this.setter.Name; + return methodName == this.getter?.Name || methodName == this.setter?.Name; } } } From 6ee4e219ef9db609caf72bce2cdb8406f62af7ad Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:29:34 +0000 Subject: [PATCH 21/93] Fix up nullability in StubbedPropertiesSetup.cs. --- src/Moq/ISetup.cs | 2 +- src/Moq/Setup.cs | 10 +++++----- src/Moq/StubbedPropertiesSetup.cs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Moq/ISetup.cs b/src/Moq/ISetup.cs index 03255edba..6c18df3d0 100644 --- a/src/Moq/ISetup.cs +++ b/src/Moq/ISetup.cs @@ -126,7 +126,7 @@ public interface ISetup /// e.g. by or by . /// /// - Expression OriginalExpression { get; } + Expression? OriginalExpression { get; } /// /// Verifies this setup and optionally all verifiable setups of its inner mock (if present and known). diff --git a/src/Moq/Setup.cs b/src/Moq/Setup.cs index be6f754db..14a2d11b4 100644 --- a/src/Moq/Setup.cs +++ b/src/Moq/Setup.cs @@ -15,11 +15,11 @@ namespace Moq abstract class Setup : ISetup { readonly Expectation expectation; - readonly Expression originalExpression; + readonly Expression? originalExpression; readonly Mock mock; Flags flags; - protected Setup(Expression originalExpression, Mock mock, Expectation expectation) + protected Setup(Expression? originalExpression, Mock mock, Expectation expectation) { Debug.Assert(mock != null); Debug.Assert(expectation != null); @@ -29,7 +29,7 @@ protected Setup(Expression originalExpression, Mock mock, Expectation expectatio this.mock = mock; } - public virtual Condition Condition => null; + public virtual Condition? Condition => null; public Expectation Expectation => this.expectation; @@ -47,7 +47,7 @@ protected Setup(Expression originalExpression, Mock mock, Expectation expectatio public Mock Mock => this.mock; - public Expression OriginalExpression => this.originalExpression; + public Expression? OriginalExpression => this.originalExpression; public bool IsMatched => (this.flags & Flags.Matched) != 0; @@ -204,7 +204,7 @@ void Verify(bool recursive, Func predicate) this.Verify(recursive, predicate, verifiedMocks); } - protected static Mock TryGetInnerMockFrom(object returnValue) + protected static Mock? TryGetInnerMockFrom(object returnValue) { return (Awaitable.TryGetResultRecursive(returnValue) as IMocked)?.Mock; } diff --git a/src/Moq/StubbedPropertiesSetup.cs b/src/Moq/StubbedPropertiesSetup.cs index 7350b9a6b..d02b6dcb2 100644 --- a/src/Moq/StubbedPropertiesSetup.cs +++ b/src/Moq/StubbedPropertiesSetup.cs @@ -15,7 +15,7 @@ sealed class StubbedPropertiesSetup : Setup readonly ConcurrentDictionary values; readonly DefaultValueProvider defaultValueProvider; - public StubbedPropertiesSetup(Mock mock, DefaultValueProvider defaultValueProvider = null) + public StubbedPropertiesSetup(Mock mock, DefaultValueProvider? defaultValueProvider = null) : base(originalExpression: null, mock, new PropertyAccessorExpectation(mock)) { this.values = new ConcurrentDictionary(); @@ -79,7 +79,7 @@ public PropertyAccessorExpectation(Mock mock) Debug.Assert(mock != null); var mockType = mock.GetType(); - var setupAllPropertiesMethod = mockType.GetMethod(nameof(Mock.SetupAllProperties)); + var setupAllPropertiesMethod = mockType.GetMethod(nameof(Mock.SetupAllProperties))!; var mockedType = setupAllPropertiesMethod.ReturnType.GetGenericArguments()[0]; var mockGetMethod = Mock.GetMethod.MakeGenericMethod(mockedType); var mockParam = E.Parameter(mockedType, "m"); @@ -88,7 +88,7 @@ public PropertyAccessorExpectation(Mock mock) public override LambdaExpression Expression => this.expression; - public override bool Equals(Expectation other) + public override bool Equals(Expectation? other) { return other is PropertyAccessorExpectation pae && ExpressionComparer.Default.Equals(this.expression, pae.expression); } From 2e03d1b6b671dc1e7919dea8ab8f9f516e4da677 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:34:26 +0000 Subject: [PATCH 22/93] Fix up nullability in Invocation.cs. --- src/Moq/IInvocation.cs | 6 +++--- src/Moq/Invocation.cs | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Moq/IInvocation.cs b/src/Moq/IInvocation.cs index c2982b920..957256c2a 100644 --- a/src/Moq/IInvocation.cs +++ b/src/Moq/IInvocation.cs @@ -25,7 +25,7 @@ public interface IInvocation /// /// Gets the setup that matched this invocation (or if there was no matching setup). /// - ISetup MatchingSetup { get; } + ISetup? MatchingSetup { get; } /// /// Gets whether this invocation was successfully verified by any of the various `Verify` methods. @@ -35,11 +35,11 @@ public interface IInvocation /// /// The value being returned for a non-void method if no exception was thrown. /// - object ReturnValue { get; } + object? ReturnValue { get; } /// /// Optional exception if the method invocation results in an exception being thrown. /// - Exception Exception { get; } + Exception? Exception { get; } } } \ No newline at end of file diff --git a/src/Moq/Invocation.cs b/src/Moq/Invocation.cs index 9b8a237f2..3473b8d07 100644 --- a/src/Moq/Invocation.cs +++ b/src/Moq/Invocation.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text; @@ -15,10 +16,10 @@ abstract class Invocation : IInvocation { object[] arguments; MethodInfo method; - MethodInfo methodImplementation; + MethodInfo? methodImplementation; readonly Type proxyType; - object result; - Setup matchingSetup; + object? result; + Setup? matchingSetup; bool verified; /// @@ -67,11 +68,11 @@ public MethodInfo MethodImplementation IReadOnlyList IInvocation.Arguments => this.arguments; - public ISetup MatchingSetup => this.matchingSetup; + public ISetup? MatchingSetup => this.matchingSetup; public Type ProxyType => this.proxyType; - public object ReturnValue + public object? ReturnValue { get => this.result is ExceptionResult ? null : this.result; set @@ -80,8 +81,10 @@ public object ReturnValue this.result = value; } } - - public Exception Exception +#if NULLABLE_REFERENCE_TYPES + [DisallowNull] +#endif + public Exception? Exception { get => this.result is ExceptionResult r ? r.Exception : null; set @@ -134,7 +137,7 @@ public override string ToString() var method = this.Method; var builder = new StringBuilder(); - builder.AppendNameOf(method.DeclaringType); + builder.AppendNameOf(method.DeclaringType!); builder.Append('.'); if (method.IsGetAccessor()) From f55e6668b4c1a601a2a9637a4ef11fdd06307df6 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:42:53 +0000 Subject: [PATCH 23/93] Fix up nullability in Extensions.cs. --- src/Moq/Extensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/Extensions.cs b/src/Moq/Extensions.cs index ba68add37..ba85a6ac4 100644 --- a/src/Moq/Extensions.cs +++ b/src/Moq/Extensions.cs @@ -60,7 +60,7 @@ public static bool CanRead(this PropertyInfo property, out MethodInfo? getter, o var baseProperty = baseSetter .DeclaringType - .GetMember(property.Name, MemberTypes.Property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + !.GetMember(property.Name, MemberTypes.Property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .Cast() .First(p => p.GetSetMethod(nonPublic: true) == baseSetter); return baseProperty.CanRead(out getter, out getterProperty); @@ -513,7 +513,7 @@ public static IEnumerable FindAllInnerMocks(this SetupCollection setups) .Where(innerMock => innerMock != null); } - public static Mock FindLastInnerMock(this SetupCollection setups, Func predicate) + public static Mock? FindLastInnerMock(this SetupCollection setups, Func predicate) { return setups.FindLast(setup => !setup.IsConditional && predicate(setup))?.InnerMocks.SingleOrDefault(); } From d8328f942f69f95ad89e6afd486778b8afe152da Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:49:54 +0000 Subject: [PATCH 24/93] Fix nullability in InnerMockSetup.cs. --- src/Moq/InnerMockSetup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/InnerMockSetup.cs b/src/Moq/InnerMockSetup.cs index ef7ed6e17..ea6414b8b 100644 --- a/src/Moq/InnerMockSetup.cs +++ b/src/Moq/InnerMockSetup.cs @@ -11,9 +11,9 @@ namespace Moq { sealed class InnerMockSetup : SetupWithOutParameterSupport { - readonly object returnValue; + readonly object? returnValue; - public InnerMockSetup(Expression originalExpression, Mock mock, MethodExpectation expectation, object returnValue) + public InnerMockSetup(Expression originalExpression, Mock mock, MethodExpectation expectation, object? returnValue) : base(originalExpression, mock, expectation) { Debug.Assert(Awaitable.TryGetResultRecursive(returnValue) is IMocked); From 063899e378850103489c70f1a9c8479cd6eb07e9 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:52:09 +0000 Subject: [PATCH 25/93] Fix up nullability in StubbedPropertySetup.cs. --- src/Moq/StubbedPropertySetup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/StubbedPropertySetup.cs b/src/Moq/StubbedPropertySetup.cs index 363ad9a4b..0bb7ade60 100644 --- a/src/Moq/StubbedPropertySetup.cs +++ b/src/Moq/StubbedPropertySetup.cs @@ -10,9 +10,9 @@ namespace Moq { sealed class StubbedPropertySetup : Setup { - object value; + object? value; - public StubbedPropertySetup(Mock mock, LambdaExpression expression, MethodInfo getter, MethodInfo setter, object initialValue) + public StubbedPropertySetup(Mock mock, LambdaExpression expression, MethodInfo? getter, MethodInfo? setter, object? initialValue) : base(originalExpression: null, mock, new PropertyAccessorExpectation(expression, getter, setter)) { // NOTE: From 44bd2dbda0b9e1e94e106219561a529c59d9d806 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:53:31 +0000 Subject: [PATCH 26/93] Fix nullability in Setup.cs. --- src/Moq/ISetup.cs | 2 +- src/Moq/Setup.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Moq/ISetup.cs b/src/Moq/ISetup.cs index 6c18df3d0..9defbc549 100644 --- a/src/Moq/ISetup.cs +++ b/src/Moq/ISetup.cs @@ -49,7 +49,7 @@ public interface ISetup // /// The setup has more than one inner mock. // [Obsolete("Use 'InnerMocks' instead.")] // [EditorBrowsable(EditorBrowsableState.Never)] - Mock InnerMock { get; } + Mock? InnerMock { get; } // /// // /// Gets the inner mocks of this setup (if present and known). diff --git a/src/Moq/Setup.cs b/src/Moq/Setup.cs index 14a2d11b4..5a578b78a 100644 --- a/src/Moq/Setup.cs +++ b/src/Moq/Setup.cs @@ -35,7 +35,7 @@ protected Setup(Expression? originalExpression, Mock mock, Expectation expectati public LambdaExpression Expression => this.expectation.Expression; - Mock ISetup.InnerMock => this.InnerMocks.SingleOrDefault(); + Mock? ISetup.InnerMock => this.InnerMocks.SingleOrDefault(); public virtual IEnumerable InnerMocks => Enumerable.Empty(); @@ -204,7 +204,7 @@ void Verify(bool recursive, Func predicate) this.Verify(recursive, predicate, verifiedMocks); } - protected static Mock? TryGetInnerMockFrom(object returnValue) + protected static Mock? TryGetInnerMockFrom(object? returnValue) { return (Awaitable.TryGetResultRecursive(returnValue) as IMocked)?.Mock; } From 6171f9b95e21a44eb3f06f027189ac7e631a2d00 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 14:54:16 +0000 Subject: [PATCH 27/93] Fix nullability in SetupCollection.cs. --- src/Moq/SetupCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/SetupCollection.cs b/src/Moq/SetupCollection.cs index 7c210c9a6..c21907bfb 100644 --- a/src/Moq/SetupCollection.cs +++ b/src/Moq/SetupCollection.cs @@ -101,7 +101,7 @@ public List FindAll(Func predicate) return setups; } - public Setup FindLast(Func predicate) + public Setup? FindLast(Func predicate) { // Fast path (no `lock`) when there are no setups: if (this.setups.Count == 0) From 07ea33e0768213aa9cec1958433efdf2d9a57d9b Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 16:28:56 +0000 Subject: [PATCH 28/93] Fix up nullability in StringBuilderExtensions.cs. --- src/Moq/StringBuilderExtensions.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Moq/StringBuilderExtensions.cs b/src/Moq/StringBuilderExtensions.cs index 127e665b1..064745d67 100644 --- a/src/Moq/StringBuilderExtensions.cs +++ b/src/Moq/StringBuilderExtensions.cs @@ -95,7 +95,7 @@ public static StringBuilder AppendParameterType(this StringBuilder stringBuilder _ => "ref ", }); - parameterType = parameterType.GetElementType(); + parameterType = parameterType.GetElementType()!; } if (parameterType.IsArray && parameter.IsDefined(typeof(ParamArrayAttribute), true)) @@ -106,7 +106,7 @@ public static StringBuilder AppendParameterType(this StringBuilder stringBuilder return stringBuilder.AppendFormattedName(parameterType); } - public static StringBuilder AppendValueOf(this StringBuilder stringBuilder, object obj) + public static StringBuilder AppendValueOf(this StringBuilder stringBuilder, object? obj) { if (obj == null) { @@ -148,6 +148,8 @@ public static StringBuilder AppendValueOf(this StringBuilder stringBuilder, obje stringBuilder.AppendValueOf(enumerator.Current); } + + (enumerator as IDisposable)?.Dispose(); stringBuilder.Append(']'); } else From e63957e48d85c1f3e6ea165f2dbd0a91afdf76ab Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 16:35:47 +0000 Subject: [PATCH 29/93] Fix up nullability in AwaitableFactory.cs. --- src/Moq/Async/AwaitableFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Async/AwaitableFactory.cs b/src/Moq/Async/AwaitableFactory.cs index 8c51868bf..8de61a3e7 100644 --- a/src/Moq/Async/AwaitableFactory.cs +++ b/src/Moq/Async/AwaitableFactory.cs @@ -27,7 +27,7 @@ static IAwaitableFactory Create(Type awaitableFactoryType, Type awaitableType) { return (IAwaitableFactory)Activator.CreateInstance( awaitableFactoryType.MakeGenericType( - awaitableType.GetGenericArguments())); + awaitableType.GetGenericArguments()))!; } public static IAwaitableFactory? TryGet(Type type) From 4742a09ff940523cca4f29a6e1a79d4c9fae917e Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 16:39:24 +0000 Subject: [PATCH 30/93] Fix up nullability in Times.cs. --- src/Moq/Times.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Times.cs b/src/Moq/Times.cs index e92ac06dd..1906d01ae 100644 --- a/src/Moq/Times.cs +++ b/src/Moq/Times.cs @@ -192,7 +192,7 @@ public bool Equals(Times other) /// if has the same value as this instance; /// otherwise, . /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Times other && this.Equals(other); } From 1e71f78dd4b4b3ffdc864e0e27959b2f1a70243e Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 16:42:16 +0000 Subject: [PATCH 31/93] Fix up nullability in EmptyDefaultValueProvider.cs. --- src/Moq/EmptyDefaultValueProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/EmptyDefaultValueProvider.cs b/src/Moq/EmptyDefaultValueProvider.cs index 3f6e05242..d80fb8477 100644 --- a/src/Moq/EmptyDefaultValueProvider.cs +++ b/src/Moq/EmptyDefaultValueProvider.cs @@ -28,7 +28,7 @@ internal EmptyDefaultValueProvider() static object CreateArray(Type type, Mock mock) { - var elementType = type.GetElementType(); + var elementType = type.GetElementType()!; var lengths = new int[type.GetArrayRank()]; return Array.CreateInstance(elementType, lengths); } From 5419049a9668db450d1ac6d3cf631de3e8b8bc25 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 16:47:37 +0000 Subject: [PATCH 32/93] Fix up TaskFactory`1.cs nullability. --- src/Moq/Async/TaskFactory`1.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Moq/Async/TaskFactory`1.cs b/src/Moq/Async/TaskFactory`1.cs index 1c419e6f1..1baefd3ae 100644 --- a/src/Moq/Async/TaskFactory`1.cs +++ b/src/Moq/Async/TaskFactory`1.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Threading.Tasks; @@ -33,10 +34,14 @@ public override Expression CreateResultExpression(Expression awaitableExpression { return Expression.MakeMemberAccess( awaitableExpression, - typeof(Task).GetProperty(nameof(Task.Result))); + typeof(Task).GetProperty(nameof(Task.Result))!); } - public override bool TryGetResult(Task task, out TResult result) +#if NULLABLE_REFERENCE_TYPES + public override bool TryGetResult(Task task, [MaybeNullWhen(false)] out TResult result) +#else + public override bool TryGetResult(Task task, out TResult? result) +#endif { if (task.Status == TaskStatus.RanToCompletion) { From c25d29a43e90c68433eb899a750a346f6211de3b Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 16:50:45 +0000 Subject: [PATCH 33/93] Fix up nullability in ActionObserver.cs. --- src/Moq/ActionObserver.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Moq/ActionObserver.cs b/src/Moq/ActionObserver.cs index 73550d75f..5c5996b6a 100644 --- a/src/Moq/ActionObserver.cs +++ b/src/Moq/ActionObserver.cs @@ -34,7 +34,7 @@ public override Expression> ReconstructExpression(Action action, // Create the root recording proxy: var root = (T)CreateProxy(typeof(T), ctorArgs, matcherObserver, out var rootRecorder); - Exception error = null; + Exception? error = null; try { // Execute the delegate. The root recorder will automatically "mock" return values @@ -61,7 +61,7 @@ public override Expression> ReconstructExpression(Action action, var invocation = recorder.Invocation; if (invocation != null) { - var resultType = invocation.Method.DeclaringType; + var resultType = invocation.Method.DeclaringType!; if (resultType.IsAssignableFrom(body.Type) == false) { if (AwaitableFactory.TryGet(body.Type) is { } awaitableHandler @@ -128,7 +128,7 @@ Expression[] GetArgumentExpressions(Invocation invocation, Match[] matches) // it will have left behind a `default(T)` argument, possibly coerced to the parameter type. // Therefore, we attempt to reproduce such coercions using `Convert.ChangeType`: Type defaultValueType = matches[matchIndex].RenderExpression.Type; - object defaultValue = defaultValueType.GetDefaultValue(); + object? defaultValue = defaultValueType.GetDefaultValue(); try { defaultValue = Convert.ChangeType(defaultValue, parameterTypes[argumentIndex]); @@ -241,9 +241,9 @@ sealed class Recorder : IInterceptor { readonly MatcherObserver matcherObserver; int creationTimestamp; - Invocation invocation; - int invocationTimestamp; - object returnValue; + Invocation? invocation; + int? invocationTimestamp; + object? returnValue; public Recorder(MatcherObserver matcherObserver) { @@ -253,18 +253,18 @@ public Recorder(MatcherObserver matcherObserver) this.creationTimestamp = this.matcherObserver.GetNextTimestamp(); } - public Invocation Invocation => this.invocation; + public Invocation? Invocation => this.invocation; public IEnumerable Matches { get { Debug.Assert(this.invocationTimestamp != default); - return this.matcherObserver.GetMatchesBetween(this.creationTimestamp, this.invocationTimestamp); + return this.matcherObserver.GetMatchesBetween(this.creationTimestamp, this.invocationTimestamp!.Value); } } - public Recorder Next => (Awaitable.TryGetResultRecursive(this.returnValue) as IProxy)?.Interceptor as Recorder; + public Recorder? Next => (Awaitable.TryGetResultRecursive(this.returnValue) as IProxy)?.Interceptor as Recorder; public void Intercept(Invocation invocation) { From a886aa4b14745690a7b99f32bbe027e8e471212b Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 16:52:28 +0000 Subject: [PATCH 34/93] Fix up nullability in ValueTaskFactory`1.cs. --- src/Moq/Async/ValueTaskFactory`1.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Moq/Async/ValueTaskFactory`1.cs b/src/Moq/Async/ValueTaskFactory`1.cs index 53eaa60ed..e14ddca67 100644 --- a/src/Moq/Async/ValueTaskFactory`1.cs +++ b/src/Moq/Async/ValueTaskFactory`1.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Threading.Tasks; @@ -33,10 +34,14 @@ public override Expression CreateResultExpression(Expression awaitableExpression { return Expression.MakeMemberAccess( awaitableExpression, - typeof(ValueTask).GetProperty(nameof(ValueTask.Result))); + typeof(ValueTask).GetProperty(nameof(ValueTask.Result))!); } +#if NULLABLE_REFERENCE_TYPES + public override bool TryGetResult(ValueTask valueTask, [MaybeNullWhen(false)] out TResult result) +#else public override bool TryGetResult(ValueTask valueTask, out TResult result) +#endif { if (valueTask.IsCompletedSuccessfully) { From f4f0a5dae8134597267d4905128cc6bc19309fd9 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 16:53:21 +0000 Subject: [PATCH 35/93] Fix up nullability for Invocation.cs. --- src/Moq/IInvocation.cs | 2 +- src/Moq/Invocation.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Moq/IInvocation.cs b/src/Moq/IInvocation.cs index 957256c2a..f543b5f26 100644 --- a/src/Moq/IInvocation.cs +++ b/src/Moq/IInvocation.cs @@ -20,7 +20,7 @@ public interface IInvocation /// /// Gets the arguments of the invocation. /// - IReadOnlyList Arguments { get; } + IReadOnlyList Arguments { get; } /// /// Gets the setup that matched this invocation (or if there was no matching setup). diff --git a/src/Moq/Invocation.cs b/src/Moq/Invocation.cs index 3473b8d07..19a918c7c 100644 --- a/src/Moq/Invocation.cs +++ b/src/Moq/Invocation.cs @@ -14,7 +14,7 @@ namespace Moq { abstract class Invocation : IInvocation { - object[] arguments; + object?[] arguments; MethodInfo method; MethodInfo? methodImplementation; readonly Type proxyType; @@ -28,7 +28,7 @@ abstract class Invocation : IInvocation /// The of the concrete proxy object on which a method is being invoked. /// The method being invoked. /// The arguments with which the specified is being invoked. - protected Invocation(Type proxyType, MethodInfo method, params object[] arguments) + protected Invocation(Type proxyType, MethodInfo method, params object?[] arguments) { Debug.Assert(proxyType != null); Debug.Assert(arguments != null); @@ -64,9 +64,9 @@ public MethodInfo MethodImplementation /// Arguments may be modified. Derived classes must ensure that by-reference parameters are written back /// when the invocation is ended by a call to any of the three Returns methods. /// - public object[] Arguments => this.arguments; + public object?[] Arguments => this.arguments; - IReadOnlyList IInvocation.Arguments => this.arguments; + IReadOnlyList IInvocation.Arguments => this.arguments; public ISetup? MatchingSetup => this.matchingSetup; From bd6b53427fb85dbba299d0676a5acb0b4f00c4c1 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 17:52:49 +0000 Subject: [PATCH 36/93] Tidy nullability in UpgradePropertyAccessorMethods.cs. --- .../Visitors/UpgradePropertyAccessorMethods.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Moq/Expressions/Visitors/UpgradePropertyAccessorMethods.cs b/src/Moq/Expressions/Visitors/UpgradePropertyAccessorMethods.cs index 62554952d..05d0645b9 100644 --- a/src/Moq/Expressions/Visitors/UpgradePropertyAccessorMethods.cs +++ b/src/Moq/Expressions/Visitors/UpgradePropertyAccessorMethods.cs @@ -48,7 +48,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node) if (argumentCount == 0) { // getter: - var property = node.Method.DeclaringType.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var property = node.Method.DeclaringType!.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); Debug.Assert(property != null && property.GetGetMethod(true) == node.Method); return Expression.MakeMemberAccess(instance, property); @@ -58,10 +58,10 @@ protected override Expression VisitMethodCall(MethodCallExpression node) // indexer getter: var parameterTypes = node.Method.GetParameterTypes(); var argumentTypes = parameterTypes.ToArray(); - var indexer = node.Method.DeclaringType.GetProperty(name, node.Method.ReturnType, argumentTypes); + var indexer = node.Method.DeclaringType!.GetProperty(name, node.Method.ReturnType, argumentTypes); Debug.Assert(indexer != null && indexer.GetGetMethod(true) == node.Method); - return Expression.MakeIndex(instance, indexer, arguments); + return Expression.MakeIndex(instance!, indexer, arguments); } } else if (node.Method.IsSetAccessor()) @@ -72,7 +72,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node) if (argumentCount == 1) { // setter: - var property = node.Method.DeclaringType.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + var property = node.Method.DeclaringType!.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); Debug.Assert(property != null && property.GetSetMethod(true) == node.Method); var value = node.Arguments[0]; @@ -83,12 +83,12 @@ protected override Expression VisitMethodCall(MethodCallExpression node) // indexer setter: var parameterTypes = node.Method.GetParameterTypes(); var argumentTypes = parameterTypes.Take(parameterTypes.Count - 1).ToArray(); - var indexer = node.Method.DeclaringType.GetProperty(name, parameterTypes.Last(), argumentTypes); + var indexer = node.Method.DeclaringType!.GetProperty(name, parameterTypes.Last(), argumentTypes); Debug.Assert(indexer != null && indexer.GetSetMethod(true) == node.Method); var indices = arguments.Take(argumentCount - 1); var value = arguments.Last(); - return Expression.Assign(Expression.MakeIndex(instance, indexer, indices), value); + return Expression.Assign(Expression.MakeIndex(instance!, indexer, indices), value); } } } From a9ec75c9e7395318351f46c9ada754a90f486f29 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:02:56 +0000 Subject: [PATCH 37/93] Fix up nullability in Extensions.cs. --- src/Moq/Extensions.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Moq/Extensions.cs b/src/Moq/Extensions.cs index ba85a6ac4..d40e98b12 100644 --- a/src/Moq/Extensions.cs +++ b/src/Moq/Extensions.cs @@ -170,11 +170,11 @@ public static MethodInfo GetImplementingMethod(this MethodInfo method, Type prox } } - public static object InvokePreserveStack(this Delegate del, IReadOnlyList? args = null) + public static object? InvokePreserveStack(this Delegate del, IReadOnlyList? args = null) { try { - return del.DynamicInvoke((args as object[]) ?? args?.ToArray()); + return del.DynamicInvoke((args as object?[]) ?? args?.ToArray()); } catch (TargetInvocationException ex) { @@ -428,7 +428,7 @@ public static Type SubstituteTypeMatchers(this Type type, Type other) if (type.IsTypeMatcher(out var typeMatcherType)) { - var typeMatcher = (ITypeMatcher)Activator.CreateInstance(typeMatcherType); + var typeMatcher = (ITypeMatcher)Activator.CreateInstance(typeMatcherType)!; if (typeMatcher.Matches(other)) { @@ -509,8 +509,7 @@ static InterfaceMapping GetInterfaceMap(Type type, Type interfaceType) public static IEnumerable FindAllInnerMocks(this SetupCollection setups) { return setups.FindAll(setup => !setup.IsConditional) - .SelectMany(setup => setup.InnerMocks) - .Where(innerMock => innerMock != null); + .SelectMany(setup => setup.InnerMocks); } public static Mock? FindLastInnerMock(this SetupCollection setups, Func predicate) From 031f0313ebb39a5358e2de82c5dbb03aa01adb74 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:09:09 +0000 Subject: [PATCH 38/93] Tidy nullability in AwaitableFactory`2.cs. --- src/Moq/Async/AwaitableFactory`2.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Moq/Async/AwaitableFactory`2.cs b/src/Moq/Async/AwaitableFactory`2.cs index bb0e3e212..0931da051 100644 --- a/src/Moq/Async/AwaitableFactory`2.cs +++ b/src/Moq/Async/AwaitableFactory`2.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; @@ -47,7 +48,11 @@ object IAwaitableFactory.CreateFaulted(IEnumerable exceptions) return this.CreateFaulted(exceptions); } +#if NULLABLE_REFERENCE_TYPES + public abstract bool TryGetResult(TAwaitable awaitable, [MaybeNullWhen(false)] out TResult result); +#else public abstract bool TryGetResult(TAwaitable awaitable, out TResult result); +#endif public abstract Expression CreateResultExpression(Expression awaitableExpression); From 41521ec434536e49be1c071b81193894446d7f60 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:10:01 +0000 Subject: [PATCH 39/93] Fix up nullability in DefaultValueProvider.cs. --- src/Moq/DefaultValueProvider.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Moq/DefaultValueProvider.cs b/src/Moq/DefaultValueProvider.cs index c2bb2b34b..290da47db 100644 --- a/src/Moq/DefaultValueProvider.cs +++ b/src/Moq/DefaultValueProvider.cs @@ -49,7 +49,7 @@ protected DefaultValueProvider() /// /// Implementations may assume that all parameters have valid, non-, non- values. /// - protected internal abstract object GetDefaultValue(Type type, Mock mock); + protected internal abstract object? GetDefaultValue(Type type, Mock mock); /// /// @@ -65,7 +65,7 @@ protected DefaultValueProvider() /// /// Implementations may assume that all parameters have valid, non-, non- values. /// - protected internal virtual object GetDefaultParameterValue(ParameterInfo parameter, Mock mock) + protected internal virtual object? GetDefaultParameterValue(ParameterInfo parameter, Mock mock) { Debug.Assert(parameter != null); Debug.Assert(parameter.ParameterType != typeof(void)); @@ -88,7 +88,7 @@ protected internal virtual object GetDefaultParameterValue(ParameterInfo paramet /// /// Implementations may assume that all parameters have valid, non-, non- values. /// - protected internal virtual object GetDefaultReturnValue(MethodInfo method, Mock mock) + protected internal virtual object? GetDefaultReturnValue(MethodInfo method, Mock mock) { Debug.Assert(method != null); Debug.Assert(method.ReturnType != typeof(void)); From 654fe2261b142e211c207ddb5c9d42eeff92e2b3 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:10:39 +0000 Subject: [PATCH 40/93] Fix up nullability in LookupOrFallbackDefaultValueProvider.cs. --- .../LookupOrFallbackDefaultValueProvider.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Moq/LookupOrFallbackDefaultValueProvider.cs b/src/Moq/LookupOrFallbackDefaultValueProvider.cs index a5ea28548..173617047 100644 --- a/src/Moq/LookupOrFallbackDefaultValueProvider.cs +++ b/src/Moq/LookupOrFallbackDefaultValueProvider.cs @@ -34,14 +34,14 @@ namespace Moq [EditorBrowsable(EditorBrowsableState.Advanced)] public abstract class LookupOrFallbackDefaultValueProvider : DefaultValueProvider { - Dictionary> factories; + Dictionary?> factories; /// /// Initializes a new instance of the class. /// protected LookupOrFallbackDefaultValueProvider() { - this.factories = new Dictionary>() + this.factories = new Dictionary?>() { ["System.ValueTuple`1"] = CreateValueTupleOf, ["System.ValueTuple`2"] = CreateValueTupleOf, @@ -61,7 +61,7 @@ protected LookupOrFallbackDefaultValueProvider() /// The type(s) for which to remove any registered factory function. protected void Deregister(Type factoryKey) { - Debug.Assert(factoryKey != null); + Debug.Assert(factoryKey.FullName != null); // NOTE: In order to be able to unregister the default logic for awaitable types, // we need a way (below) to know when to delegate to an `IAwaitableFactory`, and when not to. @@ -93,7 +93,7 @@ protected void Register(Type factoryKey, Func factory) } /// - protected internal sealed override object GetDefaultParameterValue(ParameterInfo parameter, Mock mock) + protected internal sealed override object? GetDefaultParameterValue(ParameterInfo parameter, Mock mock) { Debug.Assert(parameter != null); Debug.Assert(parameter.ParameterType != typeof(void)); @@ -103,7 +103,7 @@ protected internal sealed override object GetDefaultParameterValue(ParameterInfo } /// - protected internal sealed override object GetDefaultReturnValue(MethodInfo method, Mock mock) + protected internal sealed override object? GetDefaultReturnValue(MethodInfo method, Mock mock) { Debug.Assert(method != null); Debug.Assert(method.ReturnType != typeof(void)); @@ -113,7 +113,7 @@ protected internal sealed override object GetDefaultReturnValue(MethodInfo metho } /// - protected internal sealed override object GetDefaultValue(Type type, Mock mock) + protected internal sealed override object? GetDefaultValue(Type type, Mock mock) { Debug.Assert(type != null); Debug.Assert(type != typeof(void)); @@ -123,8 +123,8 @@ protected internal sealed override object GetDefaultValue(Type type, Mock mock) : type.IsArray ? typeof(Array) : type; - Func factory; - if (this.factories.TryGetValue(handlerKey, out factory) || this.factories.TryGetValue(handlerKey.FullName, out factory)) + Func? factory; + if (this.factories.TryGetValue(handlerKey, out factory) || this.factories.TryGetValue(handlerKey.FullName!, out factory)) { if (factory != null) // This prevents delegation to an `IAwaitableFactory` for deregistered awaitable types; see note above. { @@ -147,7 +147,7 @@ protected internal sealed override object GetDefaultValue(Type type, Mock mock) /// /// The type of which to produce a value. /// The on which an unexpected invocation has occurred. - protected virtual object GetFallbackDefaultValue(Type type, Mock mock) + protected virtual object? GetFallbackDefaultValue(Type type, Mock mock) { Debug.Assert(type != null); Debug.Assert(type != typeof(void)); @@ -159,12 +159,12 @@ protected virtual object GetFallbackDefaultValue(Type type, Mock mock) object CreateValueTupleOf(Type type, Mock mock) { var itemTypes = type.GetGenericArguments(); - var items = new object[itemTypes.Length]; + var items = new object?[itemTypes.Length]; for (int i = 0, n = itemTypes.Length; i < n; ++i) { items[i] = this.GetDefaultValue(itemTypes[i], mock); } - return Activator.CreateInstance(type, items); + return Activator.CreateInstance(type, items)!; } } } From 12b4428be8f1a11016d17ec820be6e9347d415b9 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:13:30 +0000 Subject: [PATCH 41/93] Fix up nullability in Match.cs. --- src/Moq/Match.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Moq/Match.cs b/src/Moq/Match.cs index 70a50d9c5..eb28e4a40 100644 --- a/src/Moq/Match.cs +++ b/src/Moq/Match.cs @@ -199,15 +199,19 @@ static bool CanCast(object value) } /// - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Match other && this.Equals(other); } /// - public bool Equals(Match other) + public bool Equals(Match? other) { - if (this.Condition == other.Condition) + if (other == null) + { + return false; + } + else if (this.Condition == other.Condition) { return true; } From c61ad9fd15fe4bd49810245860557a9223bc7284 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:17:22 +0000 Subject: [PATCH 42/93] Tidy nullability in ReturnBaseOrDefaultValue.cs. --- src/Moq/Behaviors/ReturnBaseOrDefaultValue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Behaviors/ReturnBaseOrDefaultValue.cs b/src/Moq/Behaviors/ReturnBaseOrDefaultValue.cs index 20b186817..7d604b96c 100644 --- a/src/Moq/Behaviors/ReturnBaseOrDefaultValue.cs +++ b/src/Moq/Behaviors/ReturnBaseOrDefaultValue.cs @@ -31,7 +31,7 @@ public override void Execute(Invocation invocation) var tryCallDefaultInterfaceImplementation = false; #endif - var declaringType = method.DeclaringType; + var declaringType = method.DeclaringType!; if (declaringType.IsInterface) { if (this.mock.MockedType.IsInterface) From f9e995b2e6f1c41a2aca79102bf4432bec386c80 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:21:48 +0000 Subject: [PATCH 43/93] Fix up nullability in InvocationCollection.cs. --- src/Moq/InvocationCollection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/InvocationCollection.cs b/src/Moq/InvocationCollection.cs index decfcd4cc..2783f2cb2 100644 --- a/src/Moq/InvocationCollection.cs +++ b/src/Moq/InvocationCollection.cs @@ -10,7 +10,7 @@ namespace Moq { sealed class InvocationCollection : IInvocationList { - Invocation[] invocations; + Invocation[] invocations = new Invocation[0]; int capacity = 0; int count = 0; @@ -72,7 +72,7 @@ public void Clear() lock (this.invocationsLock) { // Replace the collection so readers with a reference to the old collection aren't interrupted - this.invocations = null; + this.invocations = new Invocation[0]; this.count = 0; this.capacity = 0; From b749b314088f5e86e2adcf579367c9ef42d67524 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:24:22 +0000 Subject: [PATCH 44/93] Tidy nullability in Guard.cs. --- src/Moq/Guard.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Moq/Guard.cs b/src/Moq/Guard.cs index 77d658769..a36437c2f 100644 --- a/src/Moq/Guard.cs +++ b/src/Moq/Guard.cs @@ -28,7 +28,7 @@ public static void CanCreateInstance(Type type) } } - public static void ImplementsInterface(Type interfaceType, Type type, string paramName = null) + public static void ImplementsInterface(Type interfaceType, Type type, string? paramName = null) { Debug.Assert(interfaceType != null); Debug.Assert(interfaceType.IsInterface); @@ -92,7 +92,7 @@ public static void IsOverridable(MethodInfo method, Expression expression) string.Format( CultureInfo.CurrentCulture, method.IsExtensionMethod() ? Resources.UnsupportedExtensionMethod : Resources.UnsupportedStaticMember, - $"{method.DeclaringType.GetFormattedName()}.{method.Name}"))); + $"{method.DeclaringType!.GetFormattedName()}.{method.Name}"))); } else if (!method.CanOverride()) { @@ -104,18 +104,18 @@ public static void IsOverridable(MethodInfo method, Expression expression) string.Format( CultureInfo.CurrentCulture, Resources.UnsupportedNonOverridableMember, - $"{method.DeclaringType.GetFormattedName()}.{method.Name}"))); + $"{method.DeclaringType!.GetFormattedName()}.{method.Name}"))); } } public static void IsVisibleToProxyFactory(MethodInfo method) { - if (ProxyFactory.Instance.IsMethodVisible(method, out string messageIfNotVisible) == false) + if (ProxyFactory.Instance.IsMethodVisible(method, out string? messageIfNotVisible) == false) { throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, Resources.MethodNotVisibleToProxyFactory, - method.DeclaringType.Name, + method.DeclaringType!.Name, method.Name, messageIfNotVisible)); } @@ -226,7 +226,7 @@ public static void CanRead(PropertyInfo property) throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, Resources.PropertyGetNotFound, - property.DeclaringType.Name, property.Name)); + property.DeclaringType!.Name, property.Name)); } } @@ -237,7 +237,7 @@ public static void CanWrite(PropertyInfo property) throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, Resources.PropertySetNotFound, - property.DeclaringType.Name, property.Name)); + property.DeclaringType!.Name, property.Name)); } } } From 148309c1cb1291196af86c7d271ff371d3ecad83 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:25:46 +0000 Subject: [PATCH 45/93] Fix up nullability in RaiseEvent.cs. --- src/Moq/Behaviors/RaiseEvent.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Moq/Behaviors/RaiseEvent.cs b/src/Moq/Behaviors/RaiseEvent.cs index 1aad4760f..556f5d370 100644 --- a/src/Moq/Behaviors/RaiseEvent.cs +++ b/src/Moq/Behaviors/RaiseEvent.cs @@ -12,9 +12,9 @@ sealed class RaiseEvent : Behavior Mock mock; LambdaExpression expression; Delegate? eventArgsFunc; - object[]? eventArgsParams; + object?[]? eventArgsParams; - public RaiseEvent(Mock mock, LambdaExpression expression, Delegate? eventArgsFunc, object[]? eventArgsParams) + public RaiseEvent(Mock mock, LambdaExpression expression, Delegate? eventArgsFunc, object?[]? eventArgsParams) { Debug.Assert(mock != null); Debug.Assert(expression != null); @@ -28,7 +28,7 @@ public RaiseEvent(Mock mock, LambdaExpression expression, Delegate? eventArgsFun public override void Execute(Invocation invocation) { - object[] args; + object?[] args; if (this.eventArgsParams != null) { @@ -39,11 +39,11 @@ public override void Execute(Invocation invocation) var argsFuncType = this.eventArgsFunc!.GetType(); if (argsFuncType.IsGenericType && argsFuncType.GetGenericArguments().Length == 1) { - args = new object[] { this.mock.Object, this.eventArgsFunc.InvokePreserveStack() }; + args = new object?[] { this.mock.Object, this.eventArgsFunc.InvokePreserveStack() }; } else { - args = new object[] { this.mock.Object, this.eventArgsFunc.InvokePreserveStack(invocation.Arguments) }; + args = new object?[] { this.mock.Object, this.eventArgsFunc.InvokePreserveStack(invocation.Arguments) }; } } From a77d923fad61d15a68e2b14a45f2c9f3f71e762b Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:26:55 +0000 Subject: [PATCH 46/93] Fix up nullability in EmptyDefaultValueProvider.cs. --- src/Moq/EmptyDefaultValueProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/EmptyDefaultValueProvider.cs b/src/Moq/EmptyDefaultValueProvider.cs index d80fb8477..42f282af8 100644 --- a/src/Moq/EmptyDefaultValueProvider.cs +++ b/src/Moq/EmptyDefaultValueProvider.cs @@ -57,7 +57,7 @@ static object CreateQueryableOf(Type type, Mock mock) return typeof(Queryable).GetMethods("AsQueryable") .Single(x => x.IsGenericMethod) .MakeGenericMethod(elementType) - .Invoke(null, new[] { array }); + .Invoke(null, new[] { array })!; } } } From 8f046014bea6fe0a930638554f6d081d38669426 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:27:41 +0000 Subject: [PATCH 47/93] Fix up nullability in Pair.cs --- src/Moq/Pair.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Pair.cs b/src/Moq/Pair.cs index 973a73f62..c8e1e4133 100644 --- a/src/Moq/Pair.cs +++ b/src/Moq/Pair.cs @@ -28,7 +28,7 @@ public bool Equals(Pair other) && object.Equals(this.Item2, other.Item2); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { return obj is Pair other && this.Equals(other); } From a42abc0683b3b6920c1864ea633dfe58ffa037d0 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:29:14 +0000 Subject: [PATCH 48/93] Fix up nullability in SetupWithOutParameterSupport.cs. --- src/Moq/SetupWithOutParameterSupport.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Moq/SetupWithOutParameterSupport.cs b/src/Moq/SetupWithOutParameterSupport.cs index d08ee3518..eabf6dd35 100644 --- a/src/Moq/SetupWithOutParameterSupport.cs +++ b/src/Moq/SetupWithOutParameterSupport.cs @@ -13,9 +13,9 @@ namespace Moq { abstract class SetupWithOutParameterSupport : MethodSetup { - readonly List> outValues; + readonly List>? outValues; - protected SetupWithOutParameterSupport(Expression originalExpression, Mock mock, MethodExpectation expectation) + protected SetupWithOutParameterSupport(Expression? originalExpression, Mock mock, MethodExpectation expectation) : base(originalExpression, mock, expectation) { Debug.Assert(expectation != null); @@ -34,9 +34,9 @@ public sealed override void SetOutParameters(Invocation invocation) } } - static List> GetOutValues(IReadOnlyList arguments, ParameterInfo[] parameters) + static List>? GetOutValues(IReadOnlyList arguments, ParameterInfo[] parameters) { - List> outValues = null; + List>? outValues = null; for (int i = 0, n = parameters.Length; i < n; ++i) { var parameter = parameters[i]; @@ -51,10 +51,10 @@ static List> GetOutValues(IReadOnlyList ar if (outValues == null) { - outValues = new List>(); + outValues = new List>(); } - outValues.Add(new KeyValuePair(i, constant.Value)); + outValues.Add(new KeyValuePair(i, constant.Value)); } } } From 5ea40e64ce275aded77cedd50c553729f10c1e2c Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:33:24 +0000 Subject: [PATCH 49/93] Fix up nullability in CastleProxyFactory.cs. --- src/Moq/Interception/CastleProxyFactory.cs | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Moq/Interception/CastleProxyFactory.cs b/src/Moq/Interception/CastleProxyFactory.cs index 5bca46b2d..f07cbce7b 100644 --- a/src/Moq/Interception/CastleProxyFactory.cs +++ b/src/Moq/Interception/CastleProxyFactory.cs @@ -62,7 +62,7 @@ public override object CreateProxy(Type mockType, Moq.IInterceptor interceptor, var options = new ProxyGenerationOptions(); options.AddDelegateTypeMixin(mockType); var container = GetClassGenerator(mockType).CreateClassProxy(typeof(object), additionalInterfaces, options, new Interceptor(interceptor)); - return Delegate.CreateDelegate(mockType, container, container.GetType().GetMethod("Invoke")); + return Delegate.CreateDelegate(mockType, container, container.GetType().GetMethod("Invoke")!); } try @@ -91,7 +91,7 @@ public override bool IsTypeVisible(Type type) sealed class Interceptor : Castle.DynamicProxy.IInterceptor { - static readonly MethodInfo proxyInterceptorGetter = typeof(IProxy).GetProperty(nameof(IProxy.Interceptor)).GetMethod; + static readonly MethodInfo proxyInterceptorGetter = typeof(IProxy).GetProperty(nameof(IProxy.Interceptor))!.GetMethod!; Moq.IInterceptor interceptor; @@ -129,7 +129,7 @@ public void Intercept(Castle.DynamicProxy.IInvocation underlying) sealed class Invocation : Moq.Invocation { - Castle.DynamicProxy.IInvocation underlying; + Castle.DynamicProxy.IInvocation? underlying; internal Invocation(Castle.DynamicProxy.IInvocation underlying) : base(underlying.Proxy.GetType(), underlying.Method, underlying.Arguments) { @@ -142,7 +142,7 @@ protected internal override object CallBase() #if FEATURE_DEFAULT_INTERFACE_IMPLEMENTATIONS var method = this.Method; - if (method.DeclaringType.IsInterface && !method.IsAbstract) + if (method.DeclaringType!.IsInterface && !method.IsAbstract) { // As of version 4.4.0, DynamicProxy cannot proceed to default method implementations of interfaces. // we need to find and call those manually. @@ -165,12 +165,12 @@ public void DetachFromUnderlying() // Finding and calling default interface implementations currently involves a lot of reflection, // we are using two caches to speed up these operations for repeated calls. static ConcurrentDictionary, MethodInfo> mostSpecificOverrides; - static ConcurrentDictionary> nonVirtualInvocationThunks; + static ConcurrentDictionary> nonVirtualInvocationThunks; static CastleProxyFactory() { mostSpecificOverrides = new ConcurrentDictionary, MethodInfo>(); - nonVirtualInvocationThunks = new ConcurrentDictionary>(); + nonVirtualInvocationThunks = new ConcurrentDictionary>(); } /// @@ -189,7 +189,7 @@ public static MethodInfo FindMostSpecificOverride(MethodInfo declaration, Type p var genericParameterCount = declaration.IsGenericMethod ? declaration.GetGenericArguments().Length : 0; var returnType = declaration.ReturnType; var parameterTypes = declaration.GetParameterTypes().ToArray(); - var declaringType = declaration.DeclaringType; + var declaringType = declaration.DeclaringType!; // If the base class has a method implementation, then by rule (2) it will be more specific // than any candidate method from an implemented interface: @@ -232,7 +232,7 @@ public static MethodInfo FindMostSpecificOverride(MethodInfo declaration, Type p // No, it is the most specific override so far. Add it to the list, but before doing so, // remove all less specific overrides from it: - candidateMethods.ExceptWith(candidateMethods.Where(cm => cm.DeclaringType.IsAssignableFrom(implementedInterface)).ToArray()); + candidateMethods.ExceptWith(candidateMethods.Where(cm => cm.DeclaringType!.IsAssignableFrom(implementedInterface)).ToArray()); candidateMethods.Add(candidateMethod); } @@ -256,7 +256,7 @@ public static MethodInfo FindMostSpecificOverride(MethodInfo declaration, Type p /// Performs a non-virtual (non-polymorphic) call to the given /// using the specified object and . /// - public static object DynamicInvokeNonVirtually(MethodInfo method, object instance, object[] arguments) + public static object DynamicInvokeNonVirtually(MethodInfo method, object instance, object?[] arguments) { // There are a couple of probable alternatives to the following implementation that // unfortunately don't work in practice: @@ -293,7 +293,7 @@ public static object DynamicInvokeNonVirtually(MethodInfo method, object instanc { if (parameterTypes[i].IsByRef) { - parameterTypes[i] = parameterTypes[i].GetElementType(); + parameterTypes[i] = parameterTypes[i].GetElementType()!; } } @@ -320,7 +320,7 @@ public static object DynamicInvokeNonVirtually(MethodInfo method, object instanc // Perform the actual call. il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Castclass, method.DeclaringType); + il.Emit(OpCodes.Castclass, method.DeclaringType!); for (var i = 0; i < n; ++i) { il.Emit(originalParameterTypes[i].IsByRef ? OpCodes.Ldloca : OpCodes.Ldloc, arguments[i]); @@ -355,7 +355,7 @@ public static object DynamicInvokeNonVirtually(MethodInfo method, object instanc il.Emit(OpCodes.Ldloc, returnValue); il.Emit(OpCodes.Ret); - return (Func)dynamicMethod.CreateDelegate(typeof(Func)); + return (Func)dynamicMethod.CreateDelegate(typeof(Func)); }); return thunk.Invoke(instance, arguments); From e8ff3dba4462347eb2843df8e1f95b3e90b91256 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:34:19 +0000 Subject: [PATCH 50/93] Fix up a couple more Setup classes. --- src/Moq/InnerMockSetup.cs | 2 +- src/Moq/MethodSetup.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/InnerMockSetup.cs b/src/Moq/InnerMockSetup.cs index ea6414b8b..3eaef8cac 100644 --- a/src/Moq/InnerMockSetup.cs +++ b/src/Moq/InnerMockSetup.cs @@ -13,7 +13,7 @@ sealed class InnerMockSetup : SetupWithOutParameterSupport { readonly object? returnValue; - public InnerMockSetup(Expression originalExpression, Mock mock, MethodExpectation expectation, object? returnValue) + public InnerMockSetup(Expression? originalExpression, Mock mock, MethodExpectation expectation, object? returnValue) : base(originalExpression, mock, expectation) { Debug.Assert(Awaitable.TryGetResultRecursive(returnValue) is IMocked); diff --git a/src/Moq/MethodSetup.cs b/src/Moq/MethodSetup.cs index c6bb2b4fb..19fdee19c 100644 --- a/src/Moq/MethodSetup.cs +++ b/src/Moq/MethodSetup.cs @@ -11,7 +11,7 @@ namespace Moq /// abstract class MethodSetup : Setup { - protected MethodSetup(Expression originalExpression, Mock mock, MethodExpectation expectation) + protected MethodSetup(Expression? originalExpression, Mock mock, MethodExpectation expectation) : base(originalExpression, mock, expectation) { } From d2c730de56e9b10176ce5d3eda41a6bc7e5a910f Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:36:43 +0000 Subject: [PATCH 51/93] Tidy nullability in Match.cs. --- src/Moq/Match.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Moq/Match.cs b/src/Moq/Match.cs index eb28e4a40..c001c5a98 100644 --- a/src/Moq/Match.cs +++ b/src/Moq/Match.cs @@ -68,7 +68,7 @@ public abstract class Match : IMatcher /// internal static TValue Matcher() { - return default(TValue); + return default(TValue)!; } internal abstract bool Matches(object argument, Type parameterType); @@ -104,7 +104,7 @@ public static T Create(Predicate condition) public static T Create(Predicate condition, Expression> renderExpression) { Match.Register(new Match(condition, renderExpression)); - return default(T); + return default(T)!; } /// @@ -131,7 +131,7 @@ public static T Create(Func condition, Expression Guard.NotNull(renderExpression, nameof(renderExpression)); Match.Register(new MatchFactory(condition, renderExpression)); - return default(T); + return default(T)!; } internal static void Register(Match match) @@ -185,7 +185,7 @@ internal override void SetupEvaluatedSuccessfully(object argument, Type paramete this.Success?.Invoke((T)argument); } - static bool CanCast(object value) + static bool CanCast(object? value) { if (value != null) { @@ -260,7 +260,7 @@ internal override void SetupEvaluatedSuccessfully(object argument, Type paramete Debug.Assert(this.Matches(argument, parameterType)); } - static bool CanCast(object value) + static bool CanCast(object? value) { if (value != null) { @@ -273,7 +273,7 @@ static bool CanCast(object value) } } - static readonly MethodInfo canCastMethod = typeof(MatchFactory).GetMethod("CanCast", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly); + static readonly MethodInfo canCastMethod = typeof(MatchFactory).GetMethod(nameof(CanCast), BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)!; // TODO: Check whether we need to implement `IEquatable<>` to make this work with delegate-based // setup & verification methods such as `SetupSet`! From ec8cd306e2a27c123ad2b323dd4e4e5494f1aa99 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:38:11 +0000 Subject: [PATCH 52/93] Tidy nullability in InterfaceProxy.cs. --- src/Moq/Interception/InterfaceProxy.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Moq/Interception/InterfaceProxy.cs b/src/Moq/Interception/InterfaceProxy.cs index 2b3a272c9..3d8b91347 100644 --- a/src/Moq/Interception/InterfaceProxy.cs +++ b/src/Moq/Interception/InterfaceProxy.cs @@ -17,19 +17,19 @@ namespace Moq.Internals [EditorBrowsable(EditorBrowsableState.Never)] public abstract class InterfaceProxy { - static MethodInfo equalsMethod = typeof(object).GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance); - static MethodInfo getHashCodeMethod = typeof(object).GetMethod("GetHashCode", BindingFlags.Public | BindingFlags.Instance); - static MethodInfo toStringMethod = typeof(object).GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance); + static MethodInfo equalsMethod = typeof(object).GetMethod("Equals", BindingFlags.Public | BindingFlags.Instance)!; + static MethodInfo getHashCodeMethod = typeof(object).GetMethod("GetHashCode", BindingFlags.Public | BindingFlags.Instance)!; + static MethodInfo toStringMethod = typeof(object).GetMethod("ToString", BindingFlags.Public | BindingFlags.Instance)!; /// [DebuggerHidden] - public sealed override bool Equals(object obj) + public sealed override bool Equals(object? obj) { // Forward this call to the interceptor, so that `object.Equals` can be set up. var interceptor = (IInterceptor)((IProxy)this).Interceptor; var invocation = new Invocation(this.GetType(), equalsMethod, obj); interceptor.Intercept(invocation); - return (bool)invocation.ReturnValue; + return (bool)invocation.ReturnValue!; } /// @@ -40,25 +40,25 @@ public sealed override int GetHashCode() var interceptor = (IInterceptor)((IProxy)this).Interceptor; var invocation = new Invocation(this.GetType(), getHashCodeMethod); interceptor.Intercept(invocation); - return (int)invocation.ReturnValue; + return (int)invocation.ReturnValue!; } /// [DebuggerHidden] - public sealed override string ToString() + public sealed override string? ToString() { // Forward this call to the interceptor, so that `object.ToString` can be set up. var interceptor = (IInterceptor)((IProxy)this).Interceptor; var invocation = new Invocation(this.GetType(), toStringMethod); interceptor.Intercept(invocation); - return (string)invocation.ReturnValue; + return (string?)invocation.ReturnValue; } sealed class Invocation : Moq.Invocation { - static object[] noArguments = new object[0]; + static object?[] noArguments = new object?[0]; - public Invocation(Type proxyType, MethodInfo method, params object[] arguments) + public Invocation(Type proxyType, MethodInfo method, params object?[] arguments) : base(proxyType, method, arguments) { } From 0b51a0880fb6f3514b8d0a2de694ed51a1f67d7d Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Tue, 25 Feb 2025 18:45:40 +0000 Subject: [PATCH 53/93] Fix up nullability in ReturnValue.cs. --- src/Moq/Behaviors/ReturnValue.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Moq/Behaviors/ReturnValue.cs b/src/Moq/Behaviors/ReturnValue.cs index 8824ecd20..97c2ca601 100644 --- a/src/Moq/Behaviors/ReturnValue.cs +++ b/src/Moq/Behaviors/ReturnValue.cs @@ -5,14 +5,14 @@ namespace Moq.Behaviors { sealed class ReturnValue : Behavior { - readonly object value; + readonly object? value; - public ReturnValue(object value) + public ReturnValue(object? value) { this.value = value; } - public object Value => this.value; + public object? Value => this.value; public override void Execute(Invocation invocation) { From a72a6fe546517ed2e397647e318ae170001bdd3d Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 19:22:41 +0000 Subject: [PATCH 54/93] FIx up nullability in EvaluateCaptures.cs. --- src/Moq/Expressions/Visitors/EvaluateCaptures.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Expressions/Visitors/EvaluateCaptures.cs b/src/Moq/Expressions/Visitors/EvaluateCaptures.cs index 594729560..b44040be1 100644 --- a/src/Moq/Expressions/Visitors/EvaluateCaptures.cs +++ b/src/Moq/Expressions/Visitors/EvaluateCaptures.cs @@ -22,7 +22,7 @@ protected override Expression VisitMember(MemberExpression node) { if (node.Member is FieldInfo fi && node.Expression is ConstantExpression ce - && node.Member.DeclaringType.IsDefined(typeof(CompilerGeneratedAttribute))) + && fi.DeclaringType!.IsDefined(typeof(CompilerGeneratedAttribute))) { return Expression.Constant(fi.GetValue(ce.Value), node.Type); } From 607ab002dafff0bb8c4c836d2cae773a8259d010 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 19:30:50 +0000 Subject: [PATCH 55/93] Tidy nullability in RefMatcher.cs. --- src/Moq/Matchers/RefMatcher.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/Matchers/RefMatcher.cs b/src/Moq/Matchers/RefMatcher.cs index 1d3efec67..cc4cfb705 100644 --- a/src/Moq/Matchers/RefMatcher.cs +++ b/src/Moq/Matchers/RefMatcher.cs @@ -8,10 +8,10 @@ namespace Moq.Matchers { class RefMatcher : IMatcher { - readonly object reference; + readonly object? reference; readonly bool referenceIsValueType; - public RefMatcher(object reference) + public RefMatcher(object? reference) { this.reference = reference; this.referenceIsValueType = reference?.GetType().IsValueType ?? false; From eaf298afd3daa9ee6fe027bc99e91b8fc1193788 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 22:56:49 +0000 Subject: [PATCH 56/93] Fix up nullability in IMatcher.cs and derived types. --- src/Moq/IMatcher.cs | 4 ++-- src/Moq/Matchers/AnyMatcher.cs | 4 ++-- src/Moq/Matchers/ConstantMatcher.cs | 10 +++++----- src/Moq/Matchers/ExpressionMatcher.cs | 4 ++-- src/Moq/Matchers/LazyEvalMatcher.cs | 4 ++-- src/Moq/Matchers/MatcherAttributeMatcher.cs | 14 +++++++------- src/Moq/Matchers/ParamArrayMatcher.cs | 6 +++--- src/Moq/Matchers/RefMatcher.cs | 4 ++-- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/Moq/IMatcher.cs b/src/Moq/IMatcher.cs index 1cd0dceef..e32c55ad5 100644 --- a/src/Moq/IMatcher.cs +++ b/src/Moq/IMatcher.cs @@ -7,8 +7,8 @@ namespace Moq { interface IMatcher { - bool Matches(object argument, Type parameterType); + bool Matches(object? argument, Type parameterType); - void SetupEvaluatedSuccessfully(object argument, Type parameterType); + void SetupEvaluatedSuccessfully(object? argument, Type parameterType); } } diff --git a/src/Moq/Matchers/AnyMatcher.cs b/src/Moq/Matchers/AnyMatcher.cs index 11135338c..e7680e4e8 100644 --- a/src/Moq/Matchers/AnyMatcher.cs +++ b/src/Moq/Matchers/AnyMatcher.cs @@ -14,9 +14,9 @@ sealed class AnyMatcher : IMatcher { } - public bool Matches(object argument, Type parameterType) => true; + public bool Matches(object? argument, Type parameterType) => true; - public void SetupEvaluatedSuccessfully(object argument, Type parameterType) + public void SetupEvaluatedSuccessfully(object? argument, Type parameterType) { Debug.Assert(this.Matches(argument, parameterType)); } diff --git a/src/Moq/Matchers/ConstantMatcher.cs b/src/Moq/Matchers/ConstantMatcher.cs index f43a2e84e..eb8aa06ba 100644 --- a/src/Moq/Matchers/ConstantMatcher.cs +++ b/src/Moq/Matchers/ConstantMatcher.cs @@ -10,14 +10,14 @@ namespace Moq.Matchers { class ConstantMatcher : IMatcher { - object constantValue; + object? constantValue; - public ConstantMatcher(object constantValue) + public ConstantMatcher(object? constantValue) { this.constantValue = constantValue; } - public bool Matches(object argument, Type parameterType) + public bool Matches(object? argument, Type parameterType) { if (object.Equals(argument, constantValue)) { @@ -36,14 +36,14 @@ public bool Matches(object argument, Type parameterType) return false; } - public void SetupEvaluatedSuccessfully(object argument, Type parameterType) + public void SetupEvaluatedSuccessfully(object? argument, Type parameterType) { Debug.Assert(this.Matches(argument, parameterType)); } bool MatchesEnumerable(IEnumerable enumerable) { - var constValues = (IEnumerable)constantValue; + var constValues = (IEnumerable)constantValue!; return constValues.Cast().SequenceEqual(enumerable.Cast()); } } diff --git a/src/Moq/Matchers/ExpressionMatcher.cs b/src/Moq/Matchers/ExpressionMatcher.cs index 72b910a82..7cd11e52e 100644 --- a/src/Moq/Matchers/ExpressionMatcher.cs +++ b/src/Moq/Matchers/ExpressionMatcher.cs @@ -16,13 +16,13 @@ public ExpressionMatcher(Expression expression) this.expression = expression; } - public bool Matches(object argument, Type parameterType) + public bool Matches(object? argument, Type parameterType) { return argument is Expression valueExpression && ExpressionComparer.Default.Equals(this.expression, valueExpression); } - public void SetupEvaluatedSuccessfully(object argument, Type parameterType) + public void SetupEvaluatedSuccessfully(object? argument, Type parameterType) { Debug.Assert(this.Matches(argument, parameterType)); } diff --git a/src/Moq/Matchers/LazyEvalMatcher.cs b/src/Moq/Matchers/LazyEvalMatcher.cs index 3f5fc8cfc..f8d2e2948 100644 --- a/src/Moq/Matchers/LazyEvalMatcher.cs +++ b/src/Moq/Matchers/LazyEvalMatcher.cs @@ -16,13 +16,13 @@ public LazyEvalMatcher(Expression expression) this.expression = expression; } - public bool Matches(object argument, Type parameterType) + public bool Matches(object? argument, Type parameterType) { var eval = Evaluator.PartialEval(this.expression); return eval is ConstantExpression ce && new ConstantMatcher(ce.Value).Matches(argument, parameterType); } - public void SetupEvaluatedSuccessfully(object argument, Type parameterType) + public void SetupEvaluatedSuccessfully(object? argument, Type parameterType) { Debug.Assert(this.Matches(argument, parameterType)); } diff --git a/src/Moq/Matchers/MatcherAttributeMatcher.cs b/src/Moq/Matchers/MatcherAttributeMatcher.cs index 1ce03f04c..cc8472a13 100644 --- a/src/Moq/Matchers/MatcherAttributeMatcher.cs +++ b/src/Moq/Matchers/MatcherAttributeMatcher.cs @@ -43,7 +43,7 @@ static MethodInfo ResolveValidatorMethod(MethodCallExpression call) { var expectedParametersTypes = new[] { call.Method.ReturnType }.Concat(call.Method.GetParameters().Select(p => p.ParameterType)).ToArray(); - MethodInfo method = null; + MethodInfo? method; if (call.Method.IsGenericMethod) { @@ -51,7 +51,7 @@ static MethodInfo ResolveValidatorMethod(MethodCallExpression call) // passing generic type arguments for the query. var genericArgs = call.Method.GetGenericArguments(); - method = call.Method.DeclaringType.GetMethods(call.Method.Name) + method = call.Method.DeclaringType!.GetMethods(call.Method.Name) .Where(m => m.IsGenericMethodDefinition && m.GetGenericArguments().Length == @@ -63,7 +63,7 @@ static MethodInfo ResolveValidatorMethod(MethodCallExpression call) } else { - method = call.Method.DeclaringType.GetMethod(call.Method.Name, expectedParametersTypes); + method = call.Method.DeclaringType!.GetMethod(call.Method.Name, expectedParametersTypes); } // throw if validatorMethod doesn't exists @@ -74,22 +74,22 @@ static MethodInfo ResolveValidatorMethod(MethodCallExpression call) call.Method.IsStatic ? "static " : String.Empty, call.Method.Name, String.Join(", ", expectedParametersTypes.Select(x => x.Name).ToArray()), - call.Method.DeclaringType.ToString())); + call.Method.DeclaringType!.ToString())); } return method; } - public bool Matches(object argument, Type parameterType) + public bool Matches(object? argument, Type parameterType) { // use matcher Expression to get extra arguments var extraArgs = this.expression.Arguments.Select(ae => ((ConstantExpression)ae.PartialEval()).Value); var args = new[] { argument }.Concat(extraArgs).ToArray(); // for static and non-static method var instance = this.expression.Object == null ? null : (this.expression.Object.PartialEval() as ConstantExpression).Value; - return (bool)validatorMethod.Invoke(instance, args); + return (bool)validatorMethod.Invoke(instance, args)!; } - public void SetupEvaluatedSuccessfully(object argument, Type parameterType) + public void SetupEvaluatedSuccessfully(object? argument, Type parameterType) { Debug.Assert(this.Matches(argument, parameterType)); } diff --git a/src/Moq/Matchers/ParamArrayMatcher.cs b/src/Moq/Matchers/ParamArrayMatcher.cs index 4f1a2f46e..237b0f58e 100644 --- a/src/Moq/Matchers/ParamArrayMatcher.cs +++ b/src/Moq/Matchers/ParamArrayMatcher.cs @@ -17,7 +17,7 @@ public ParamArrayMatcher(IMatcher[] matchers) this.matchers = matchers; } - public bool Matches(object argument, Type parameterType) + public bool Matches(object? argument, Type parameterType) { if (argument is not Array values || this.matchers.Length != values.Length) { @@ -37,13 +37,13 @@ public bool Matches(object argument, Type parameterType) return true; } - public void SetupEvaluatedSuccessfully(object argument, Type parameterType) + public void SetupEvaluatedSuccessfully(object? argument, Type parameterType) { Debug.Assert(this.Matches(argument, parameterType)); Debug.Assert(argument is Array array && array.Length == this.matchers.Length); var values = (Array)argument; - var elementType = parameterType.GetElementType(); + var elementType = parameterType.GetElementType()!; for (int i = 0, n = this.matchers.Length; i < n; ++i) { this.matchers[i].SetupEvaluatedSuccessfully(values.GetValue(i), elementType); diff --git a/src/Moq/Matchers/RefMatcher.cs b/src/Moq/Matchers/RefMatcher.cs index cc4cfb705..effb206ef 100644 --- a/src/Moq/Matchers/RefMatcher.cs +++ b/src/Moq/Matchers/RefMatcher.cs @@ -17,13 +17,13 @@ public RefMatcher(object? reference) this.referenceIsValueType = reference?.GetType().IsValueType ?? false; } - public bool Matches(object argument, Type parameterType) + public bool Matches(object? argument, Type parameterType) { return this.referenceIsValueType ? object.Equals(this.reference, argument) : object.ReferenceEquals(this.reference, argument); } - public void SetupEvaluatedSuccessfully(object value, Type parameterType) + public void SetupEvaluatedSuccessfully(object? value, Type parameterType) { Debug.Assert(this.Matches(value, parameterType)); } From d67fd7e84f28672878de2ecd0432cda9859c1b12 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:27:15 +0000 Subject: [PATCH 57/93] Tidy nullability in Match.cs. --- src/Moq/Match.cs | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/Moq/Match.cs b/src/Moq/Match.cs index c001c5a98..b8d09475c 100644 --- a/src/Moq/Match.cs +++ b/src/Moq/Match.cs @@ -71,14 +71,15 @@ internal static TValue Matcher() return default(TValue)!; } - internal abstract bool Matches(object argument, Type parameterType); + internal abstract bool Matches(object? argument, Type parameterType); - internal abstract void SetupEvaluatedSuccessfully(object argument, Type parameterType); + internal abstract void SetupEvaluatedSuccessfully(object? argument, Type parameterType); - bool IMatcher.Matches(object argument, Type parameterType) => this.Matches(argument, parameterType); - - void IMatcher.SetupEvaluatedSuccessfully(object value, Type parameterType) => this.SetupEvaluatedSuccessfully(value, parameterType); + bool IMatcher.Matches(object? argument, Type parameterType) => this.Matches(argument, parameterType); + void IMatcher.SetupEvaluatedSuccessfully(object? value, Type parameterType) => this.SetupEvaluatedSuccessfully(value, parameterType); + + // TODO: Consider making the constructor set this to avoid the nullable reference warning. internal Expression RenderExpression { get; set; } /// @@ -89,7 +90,7 @@ internal static TValue Matcher() public static T Create(Predicate condition) { Match.Register(new Match(condition, () => Matcher())); - return default(T); + return default(T)!; } /// @@ -125,7 +126,7 @@ public static T Create(Predicate condition, Expression> renderExpr /// /// A lambda representation of the matcher. /// - public static T Create(Func condition, Expression> renderExpression) + public static T Create(Func condition, Expression> renderExpression) { Guard.NotNull(condition, nameof(condition)); Guard.NotNull(renderExpression, nameof(renderExpression)); @@ -172,17 +173,17 @@ internal Match(Predicate condition, Expression> renderExpression, Act this.Success = success; } - internal override bool Matches(object argument, Type parameterType) + internal override bool Matches(object? argument, Type parameterType) { - return CanCast(argument) && this.Condition((T)argument); + return CanCast(argument) && this.Condition((T)argument!); } - internal override void SetupEvaluatedSuccessfully(object argument, Type parameterType) + internal override void SetupEvaluatedSuccessfully(object? argument, Type parameterType) { Debug.Assert(this.Matches(argument, parameterType)); Debug.Assert(CanCast(argument)); - this.Success?.Invoke((T)argument); + this.Success?.Invoke((T)argument!); } static bool CanCast(object? value) @@ -238,9 +239,9 @@ public bool Equals(Match? other) sealed class MatchFactory : Match { - readonly Func condition; + readonly Func condition; - internal MatchFactory(Func condition, LambdaExpression renderExpression) + internal MatchFactory(Func condition, LambdaExpression renderExpression) { Debug.Assert(condition != null); Debug.Assert(renderExpression != null); @@ -249,13 +250,13 @@ internal MatchFactory(Func condition, LambdaExpression rende this.RenderExpression = renderExpression.Body.Apply(EvaluateCaptures.Rewriter); } - internal override bool Matches(object argument, Type parameterType) + internal override bool Matches(object? argument, Type parameterType) { - var canCast = (Predicate)Delegate.CreateDelegate(typeof(Predicate), canCastMethod.MakeGenericMethod(parameterType)); + var canCast = (Predicate)Delegate.CreateDelegate(typeof(Predicate), canCastMethod.MakeGenericMethod(parameterType)); return canCast(argument) && condition(argument, parameterType); } - internal override void SetupEvaluatedSuccessfully(object argument, Type parameterType) + internal override void SetupEvaluatedSuccessfully(object? argument, Type parameterType) { Debug.Assert(this.Matches(argument, parameterType)); } From 47b24803d7ef71d7a1fbde74a6a2bde804df35e1 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:27:42 +0000 Subject: [PATCH 58/93] Tidy nullability in MatcherFactory.cs. --- src/Moq/MatcherFactory.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/MatcherFactory.cs b/src/Moq/MatcherFactory.cs index 6c6484615..b11476092 100644 --- a/src/Moq/MatcherFactory.cs +++ b/src/Moq/MatcherFactory.cs @@ -52,7 +52,7 @@ public static Pair CreateMatcher(Expression argument, Para var member = memberExpression.Member; if (member.Name == nameof(It.Ref.IsAny)) { - var memberDeclaringType = member.DeclaringType; + var memberDeclaringType = member.DeclaringType!; if (memberDeclaringType.IsGenericType) { var memberDeclaringTypeDefinition = memberDeclaringType.GetGenericTypeDefinition(); @@ -77,7 +77,7 @@ public static Pair CreateMatcher(Expression argument, Para var newArrayExpression = (NewArrayExpression)argument; Debug.Assert(newArrayExpression.Type.IsArray); - var elementType = newArrayExpression.Type.GetElementType(); + var elementType = newArrayExpression.Type.GetElementType()!; var n = newArrayExpression.Expressions.Count; var matchers = new IMatcher[n]; From e3eb36a3d440fc162549fa4031e5bb4c68e84190 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:31:04 +0000 Subject: [PATCH 59/93] Tidy nullability in MatcherObserver.cs. --- src/Moq/MatcherObserver.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Moq/MatcherObserver.cs b/src/Moq/MatcherObserver.cs index 41c8e32f1..e30c3692d 100644 --- a/src/Moq/MatcherObserver.cs +++ b/src/Moq/MatcherObserver.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Moq @@ -21,7 +22,7 @@ namespace Moq sealed class MatcherObserver : IDisposable { [ThreadStatic] - static Stack activations; + static Stack? activations; public static MatcherObserver Activate() { @@ -37,7 +38,11 @@ public static MatcherObserver Activate() return activation; } - public static bool IsActive(out MatcherObserver observer) +#if NULLABLE_REFERENCE_TYPES + public static bool IsActive([NotNullWhen(true)] out MatcherObserver? observer) +#else + public static bool IsActive(out MatcherObserver? observer) +#endif { var activations = MatcherObserver.activations; @@ -54,7 +59,7 @@ public static bool IsActive(out MatcherObserver observer) } int timestamp; - List observations; + List? observations; MatcherObserver() { @@ -94,7 +99,11 @@ public void OnMatch(Match match) /// and if so, returns the last one. /// /// The observed matcher observed last. - public bool TryGetLastMatch(out Match match) +#if NULLABLE_REFERENCE_TYPES + public bool TryGetLastMatch([NotNullWhen(true)] out Match? match) +#else + public bool TryGetLastMatch(out Match? match) +#endif { if (this.observations != null && this.observations.Count > 0) { From e4b46b76012c3f010b0b21f1a2692cef2fb29c6c Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:32:14 +0000 Subject: [PATCH 60/93] Fix up nullability in MockException.cs. --- src/Moq/MockException.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/MockException.cs b/src/Moq/MockException.cs index 538a9349f..ead78710c 100644 --- a/src/Moq/MockException.cs +++ b/src/Moq/MockException.cs @@ -56,7 +56,7 @@ internal static MockException IncorrectNumberOfCalls(MethodCall setup, Times tim internal static MockException NoMatchingCalls( Mock rootMock, LambdaExpression expression, - string failMessage, + string? failMessage, Times times, int callCount) { @@ -172,7 +172,7 @@ internal static MockException FromInnerMockOf(ISetup setup, MockException error) /// and whose reason(s) is the combination of the given ' reason(s). /// Used by when it finds one or more mocks with verification errors. /// - internal static MockException Combined(IEnumerable errors, string preamble) + internal static MockException Combined(IEnumerable errors, string? preamble) { Debug.Assert(errors != null); Debug.Assert(errors.Any()); From 7e59fe96d67296463889928aa0ea294a886f9f46 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:32:47 +0000 Subject: [PATCH 61/93] Fix up nullability in ParamArrayMatcher.cs. --- src/Moq/Matchers/ParamArrayMatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Matchers/ParamArrayMatcher.cs b/src/Moq/Matchers/ParamArrayMatcher.cs index 237b0f58e..a65b2ee75 100644 --- a/src/Moq/Matchers/ParamArrayMatcher.cs +++ b/src/Moq/Matchers/ParamArrayMatcher.cs @@ -24,7 +24,7 @@ public bool Matches(object? argument, Type parameterType) return false; } - var elementType = parameterType.GetElementType(); + var elementType = parameterType.GetElementType()!; for (int index = 0; index < values.Length; index++) { From f61f8b767ab16efb9c6807e5beaead447abd44e3 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:33:08 +0000 Subject: [PATCH 62/93] Fix up nullability in ReturnComputedValue.cs. --- src/Moq/Behaviors/ReturnComputedValue.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/Behaviors/ReturnComputedValue.cs b/src/Moq/Behaviors/ReturnComputedValue.cs index 750debe60..363a2baf8 100644 --- a/src/Moq/Behaviors/ReturnComputedValue.cs +++ b/src/Moq/Behaviors/ReturnComputedValue.cs @@ -8,9 +8,9 @@ namespace Moq.Behaviors { sealed class ReturnComputedValue : Behavior { - readonly Func valueFactory; + readonly Func valueFactory; - public ReturnComputedValue(Func valueFactory) + public ReturnComputedValue(Func valueFactory) { Debug.Assert(valueFactory != null); From 7c2626eb62f4e2d0c64448b6da0a843f1868da36 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:42:20 +0000 Subject: [PATCH 63/93] Tidy nullability in SetupPhrase.cs. --- src/Moq/Language/Flow/SetupPhrase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/Language/Flow/SetupPhrase.cs b/src/Moq/Language/Flow/SetupPhrase.cs index e6409b9a6..dcbdff386 100644 --- a/src/Moq/Language/Flow/SetupPhrase.cs +++ b/src/Moq/Language/Flow/SetupPhrase.cs @@ -286,9 +286,9 @@ public void Verifiable(string failMessage) public void Verifiable(Times times) => this.Verifiable(times, null); - public void Verifiable(Func times, string failMessage) => this.Verifiable(times(), failMessage); + public void Verifiable(Func times, string? failMessage) => this.Verifiable(times(), failMessage); - public void Verifiable(Times times, string failMessage) + public void Verifiable(Times times, string? failMessage) { this.setup.MarkAsVerifiable(); this.setup.SetExpectedInvocationCount(times); From 9284486d86b722b159e18a601514d9a1eae26759 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:42:48 +0000 Subject: [PATCH 64/93] Make best guess at how ThrowComputedException.cs should work. --- src/Moq/Behaviors/ThrowComputedException.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Moq/Behaviors/ThrowComputedException.cs b/src/Moq/Behaviors/ThrowComputedException.cs index 8afdc72c7..b1404d991 100644 --- a/src/Moq/Behaviors/ThrowComputedException.cs +++ b/src/Moq/Behaviors/ThrowComputedException.cs @@ -8,9 +8,9 @@ namespace Moq.Behaviors { sealed class ThrowComputedException : Behavior { - readonly Func exceptionFactory; + readonly Func exceptionFactory; - public ThrowComputedException(Func exceptionFactory) + public ThrowComputedException(Func exceptionFactory) { Debug.Assert(exceptionFactory != null); @@ -19,6 +19,7 @@ public ThrowComputedException(Func exceptionFactory) public override void Execute(Invocation invocation) { + // TODO: Technically this permits `throw null` here. throw this.exceptionFactory.Invoke(invocation); } } From 314574c16eb8bf7eed5cb7be1206e70f41a69672 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:47:28 +0000 Subject: [PATCH 65/93] Tidy nullability in Mock.cs. --- src/Moq/Mock.cs | 72 +++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index 958494f17..41a5a9060 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -23,7 +23,7 @@ namespace Moq public abstract partial class Mock : IFluentInterface { internal static readonly MethodInfo GetMethod = - typeof(Mock).GetMethod(nameof(Get), BindingFlags.Public | BindingFlags.Static); + typeof(Mock).GetMethod(nameof(Get), BindingFlags.Public | BindingFlags.Static)!; /// /// Initializes a new instance of the class. @@ -313,7 +313,7 @@ internal void Verify(Func predicate, HashSet verifiedMocks) } } - internal static void Verify(Mock mock, LambdaExpression expression, Times times, string failMessage) + internal static void Verify(Mock mock, LambdaExpression expression, Times times, string? failMessage) { Guard.NotNull(times, nameof(times)); @@ -333,7 +333,7 @@ internal static void Verify(Mock mock, LambdaExpression expression, Times times, } } - internal static void VerifyGet(Mock mock, LambdaExpression expression, Times times, string failMessage) + internal static void VerifyGet(Mock mock, LambdaExpression expression, Times times, string? failMessage) { Guard.NotNull(expression, nameof(expression)); @@ -346,7 +346,7 @@ internal static void VerifyGet(Mock mock, LambdaExpression expression, Times tim Mock.Verify(mock, expression, times, failMessage); } - internal static void VerifySet(Mock mock, LambdaExpression expression, Times times, string failMessage) + internal static void VerifySet(Mock mock, LambdaExpression expression, Times times, string? failMessage) { Guard.NotNull(expression, nameof(expression)); Guard.IsAssignmentToPropertyOrIndexer(expression, nameof(expression)); @@ -354,7 +354,7 @@ internal static void VerifySet(Mock mock, LambdaExpression expression, Times tim Mock.Verify(mock, expression, times, failMessage); } - internal static void VerifyAdd(Mock mock, LambdaExpression expression, Times times, string failMessage) + internal static void VerifyAdd(Mock mock, LambdaExpression expression, Times times, string? failMessage) { Guard.NotNull(expression, nameof(expression)); Guard.IsEventAdd(expression, nameof(expression)); @@ -362,7 +362,7 @@ internal static void VerifyAdd(Mock mock, LambdaExpression expression, Times tim Mock.Verify(mock, expression, times, failMessage); } - internal static void VerifyRemove(Mock mock, LambdaExpression expression, Times times, string failMessage) + internal static void VerifyRemove(Mock mock, LambdaExpression expression, Times times, string? failMessage) { Guard.NotNull(expression, nameof(expression)); Guard.IsEventRemove(expression, nameof(expression)); @@ -379,7 +379,7 @@ static void VerifyNoOtherCalls(Mock mock, HashSet verifiedMocks) { if (!verifiedMocks.Add(mock)) return; - var unverifiedInvocations = mock.MutableInvocations.ToArray(invocation => !invocation.IsVerified); + Invocation?[] unverifiedInvocations = mock.MutableInvocations.ToArray(invocation => !invocation.IsVerified); var innerMocks = mock.MutableSetups.FindAllInnerMocks(); @@ -410,7 +410,7 @@ static void VerifyNoOtherCalls(Mock mock, HashSet verifiedMocks) var remainingUnverifiedInvocations = unverifiedInvocations.Where(i => i != null); if (remainingUnverifiedInvocations.Any()) { - throw MockException.UnverifiedInvocations(mock, remainingUnverifiedInvocations); + throw MockException.UnverifiedInvocations(mock, remainingUnverifiedInvocations!); } } @@ -493,7 +493,7 @@ static int GetMatchingInvocationCount( #region Setup - internal static MethodCall Setup(Mock mock, LambdaExpression expression, Condition condition) + internal static MethodCall Setup(Mock mock, LambdaExpression expression, Condition? condition) { Guard.NotNull(expression, nameof(expression)); @@ -505,7 +505,7 @@ internal static MethodCall Setup(Mock mock, LambdaExpression expression, Conditi }); } - internal static MethodCall SetupGet(Mock mock, LambdaExpression expression, Condition condition) + internal static MethodCall SetupGet(Mock mock, LambdaExpression expression, Condition? condition) { Guard.NotNull(expression, nameof(expression)); @@ -527,7 +527,7 @@ internal static MethodCall SetupSet(Mock mock, LambdaExpression expression, Cond } internal static readonly MethodInfo SetupReturnsMethod = - typeof(Mock).GetMethod(nameof(SetupReturns), BindingFlags.NonPublic | BindingFlags.Static); + typeof(Mock).GetMethod(nameof(SetupReturns), BindingFlags.NonPublic | BindingFlags.Static)!; // This specialized setup method is used to set up a single `Mock.Of` predicate. // Unlike other setup methods, LINQ to Mocks can set non-interceptable properties, which is handy when initializing DTOs. @@ -535,7 +535,7 @@ internal static bool SetupReturns(Mock mock, LambdaExpression expression, object { Guard.NotNull(expression, nameof(expression)); - Mock.SetupRecursive(mock, expression, setupLast: (targetMock, oe, part) => + Mock.SetupRecursive(mock, expression, setupLast: (targetMock, oe, part) => { var originalExpression = (LambdaExpression)oe; @@ -636,7 +636,7 @@ internal static StubbedPropertySetup SetupProperty(Mock mock, LambdaExpression e } static TSetup SetupRecursive(Mock mock, LambdaExpression expression, Func setupLast, bool allowNonOverridableLastProperty = false) - where TSetup : ISetup + where TSetup : ISetup? { Debug.Assert(mock != null); Debug.Assert(expression != null); @@ -647,7 +647,7 @@ static TSetup SetupRecursive(Mock mock, LambdaExpression expression, Fun } static TSetup SetupRecursive(Mock mock, LambdaExpression originalExpression, Stack parts, Func setupLast) - where TSetup : ISetup + where TSetup : ISetup? { var part = parts.Pop(); var (expr, method, arguments) = part; @@ -658,7 +658,7 @@ static TSetup SetupRecursive(Mock mock, LambdaExpression originalExpress } else { - Mock innerMock = mock.MutableSetups.FindLastInnerMock(setup => setup.Matches(part)); + Mock? innerMock = mock.MutableSetups.FindLastInnerMock(setup => setup.Matches(part)); if (innerMock == null) { var returnValue = mock.GetDefaultValue(method, out innerMock, useAlternateProvider: DefaultValueProvider.Mock); @@ -697,16 +697,18 @@ internal static void RaiseEvent(Mock mock, Action action, object[] argumen Mock.RaiseEvent(mock, expression, parts, arguments); } - internal static Task RaiseEventAsync(Mock mock, Action action, object[] arguments) + internal static Task RaiseEventAsync(Mock mock, Action action, object?[] arguments) { Guard.NotNull(action, nameof(action)); var expression = ExpressionReconstructor.Instance.ReconstructExpression(action, mock.ConstructorArguments); var parts = expression.Split(); + + // TODO: Will this code never return null? return (Task)Mock.RaiseEvent(mock, expression, parts, arguments); } - internal static object RaiseEvent(Mock mock, LambdaExpression expression, Stack parts, object[] arguments) + internal static object? RaiseEvent(Mock mock, LambdaExpression expression, Stack parts, object?[] arguments) { const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly; @@ -719,28 +721,22 @@ internal static object RaiseEvent(Mock mock, LambdaExpression expression, Stack< if (method.IsEventAddAccessor()) { var implementingMethod = method.GetImplementingMethod(mock.Object.GetType()); - @event = implementingMethod.DeclaringType.GetEvents(bindingFlags).SingleOrDefault(e => e.GetAddMethod(true) == implementingMethod); - if (@event == null) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - Resources.SetupNotEventAdd, - part.Expression)); - } + @event = implementingMethod.DeclaringType!.GetEvents(bindingFlags) + .SingleOrDefault(e => e.GetAddMethod(true) == implementingMethod) + ?? throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + Resources.SetupNotEventAdd, + part.Expression)); + } else if (method.IsEventRemoveAccessor()) { var implementingMethod = method.GetImplementingMethod(mock.Object.GetType()); - @event = implementingMethod.DeclaringType.GetEvents(bindingFlags).SingleOrDefault(e => e.GetRemoveMethod(true) == implementingMethod); - if (@event == null) - { - throw new ArgumentException( - string.Format( - CultureInfo.CurrentCulture, - Resources.SetupNotEventRemove, - part.Expression)); - } + @event = implementingMethod.DeclaringType!.GetEvents(bindingFlags) + .SingleOrDefault(e => e.GetRemoveMethod(true) == implementingMethod) + ?? throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, + Resources.SetupNotEventRemove, + part.Expression)); + } else { @@ -820,7 +816,7 @@ internal bool ImplementsInterface(Type interfaceType) #region Default Values - internal abstract Dictionary ConfiguredDefaultValues { get; } + internal abstract Dictionary ConfiguredDefaultValues { get; } /// /// Defines the default return value for all mocked methods or properties with return type . @@ -835,13 +831,13 @@ public void SetReturnsDefault(TReturn value) this.ConfiguredDefaultValues[typeof(TReturn)] = value; } - internal object GetDefaultValue(MethodInfo method, out Mock candidateInnerMock, DefaultValueProvider useAlternateProvider = null) + internal object? GetDefaultValue(MethodInfo method, out Mock? candidateInnerMock, DefaultValueProvider? useAlternateProvider = null) { Debug.Assert(method != null); Debug.Assert(method.ReturnType != null); Debug.Assert(method.ReturnType != typeof(void)); - if (this.ConfiguredDefaultValues.TryGetValue(method.ReturnType, out object configuredDefaultValue)) + if (this.ConfiguredDefaultValues.TryGetValue(method.ReturnType, out object? configuredDefaultValue)) { candidateInnerMock = null; return configuredDefaultValue; From e4cda524574d96afb347b972927cdd4163ecc7f9 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:55:02 +0000 Subject: [PATCH 66/93] Tidy nullability in ActionObserver.cs. --- src/Moq/ActionObserver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/ActionObserver.cs b/src/Moq/ActionObserver.cs index 5c5996b6a..c9d23e521 100644 --- a/src/Moq/ActionObserver.cs +++ b/src/Moq/ActionObserver.cs @@ -178,7 +178,7 @@ Expression[] GetArgumentExpressions(Invocation invocation, Match[] matches) CultureInfo.CurrentCulture, Resources.MatcherAssignmentFailedDuringExpressionReconstruction, matches.Length, - $"{invocation.Method.DeclaringType.GetFormattedName()}.{invocation.Method.Name}")); + $"{invocation.Method.DeclaringType!.GetFormattedName()}.{invocation.Method.Name}")); } bool CanDistribute(int msi, int asi) From 954b72dc7af16add951cfa0a23e7b69d9d37a4a1 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:56:13 +0000 Subject: [PATCH 67/93] Tidy nullability in StubbedPropertiesSetup.cs. --- src/Moq/StubbedPropertiesSetup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/StubbedPropertiesSetup.cs b/src/Moq/StubbedPropertiesSetup.cs index d02b6dcb2..15620cf12 100644 --- a/src/Moq/StubbedPropertiesSetup.cs +++ b/src/Moq/StubbedPropertiesSetup.cs @@ -12,13 +12,13 @@ namespace Moq { sealed class StubbedPropertiesSetup : Setup { - readonly ConcurrentDictionary values; + readonly ConcurrentDictionary values; readonly DefaultValueProvider defaultValueProvider; public StubbedPropertiesSetup(Mock mock, DefaultValueProvider? defaultValueProvider = null) : base(originalExpression: null, mock, new PropertyAccessorExpectation(mock)) { - this.values = new ConcurrentDictionary(); + this.values = new ConcurrentDictionary(); this.defaultValueProvider = defaultValueProvider ?? mock.DefaultValueProvider; this.MarkAsVerifiable(); From d9f2d749a72793e40a34f2088dc30f65301d12e8 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:58:31 +0000 Subject: [PATCH 68/93] Tidy nulability in Capture.cs. --- src/Moq/Capture.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Capture.cs b/src/Moq/Capture.cs index 0798bc94e..fd04d1441 100644 --- a/src/Moq/Capture.cs +++ b/src/Moq/Capture.cs @@ -76,7 +76,7 @@ public static T In(IList collection, Expression> predicate) public static T With(CaptureMatch match) { Match.Register(match); - return default(T); + return default(T)!; } } } From 5369d150ce9f5951afec11516cc69d444883d557 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Tue, 25 Feb 2025 23:59:02 +0000 Subject: [PATCH 69/93] Tidy nullability in ProtectedAsMock.cs and ProtectedMock.cs. --- src/Moq/Protected/IProtectedAsMock.cs | 2 +- src/Moq/Protected/ProtectedAsMock.cs | 2 +- src/Moq/Protected/ProtectedMock.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Moq/Protected/IProtectedAsMock.cs b/src/Moq/Protected/IProtectedAsMock.cs index d22feda8c..8c1c9a060 100644 --- a/src/Moq/Protected/IProtectedAsMock.cs +++ b/src/Moq/Protected/IProtectedAsMock.cs @@ -89,7 +89,7 @@ public interface IProtectedAsMock : IFluentInterface /// Type of the property. Typically omitted as it can be inferred from the expression. /// Lambda expression that specifies the property. /// Initial value for the property. - Mock SetupProperty(Expression> expression, TProperty initialValue = default(TProperty)); + Mock SetupProperty(Expression> expression, TProperty? initialValue = default(TProperty)); /// /// Return a sequence of values, once per call. diff --git a/src/Moq/Protected/ProtectedAsMock.cs b/src/Moq/Protected/ProtectedAsMock.cs index 4ebf84276..73b624a00 100644 --- a/src/Moq/Protected/ProtectedAsMock.cs +++ b/src/Moq/Protected/ProtectedAsMock.cs @@ -100,7 +100,7 @@ public ISetupGetter SetupGet(Expression(setup); } - public Mock SetupProperty(Expression> expression, TProperty initialValue = default(TProperty)) + public Mock SetupProperty(Expression> expression, TProperty? initialValue = default(TProperty)) { Guard.NotNull(expression, nameof(expression)); diff --git a/src/Moq/Protected/ProtectedMock.cs b/src/Moq/Protected/ProtectedMock.cs index d6c8e808f..9291421ab 100644 --- a/src/Moq/Protected/ProtectedMock.cs +++ b/src/Moq/Protected/ProtectedMock.cs @@ -314,7 +314,7 @@ static Expression> GetMemberAccess(PropertyInfo proper return Expression.Lambda>(Expression.MakeMemberAccess(param, property), param); } - static MethodInfo GetMethod(string methodName, Type[] genericTypeArguments, bool exact, params object[] args) + static MethodInfo? GetMethod(string methodName, Type[]? genericTypeArguments, bool exact, params object[] args) { var argTypes = ToArgTypes(args); var methods = typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) @@ -343,7 +343,7 @@ static Expression> GetMethodCall(MethodInfo method, object[] args) } // TODO should support arguments for property indexers - static PropertyInfo GetProperty(string propertyName) + static PropertyInfo? GetProperty(string propertyName) { return typeof(T).GetProperty( propertyName, From 1902930e42b0d8b713c5c49aa945117dea15ad29 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:02:56 +0000 Subject: [PATCH 70/93] Tidy nullability in MockDefaultValueProvider.cs. --- src/Moq/MockDefaultValueProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/MockDefaultValueProvider.cs b/src/Moq/MockDefaultValueProvider.cs index ce97416b1..2458e9758 100644 --- a/src/Moq/MockDefaultValueProvider.cs +++ b/src/Moq/MockDefaultValueProvider.cs @@ -19,7 +19,7 @@ internal MockDefaultValueProvider() internal override DefaultValue Kind => DefaultValue.Mock; - protected override object GetFallbackDefaultValue(Type type, Mock mock) + protected override object? GetFallbackDefaultValue(Type type, Mock mock) { Debug.Assert(type != null); Debug.Assert(type != typeof(void)); @@ -34,7 +34,7 @@ protected override object GetFallbackDefaultValue(Type type, Mock mock) { // Create a new mock to be placed to InnerMocks dictionary if it's missing there var mockType = typeof(Mock<>).MakeGenericType(type); - Mock newMock = (Mock)Activator.CreateInstance(mockType, mock.Behavior); + Mock newMock = (Mock)Activator.CreateInstance(mockType, mock.Behavior)!; newMock.DefaultValueProvider = mock.DefaultValueProvider; if (mock.MutableSetups.FindLast(s => s is StubbedPropertiesSetup) is StubbedPropertiesSetup sts) { From 459d68bdcac564010ab454c5d200c1fcd4a25217 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:11:02 +0000 Subject: [PATCH 71/93] Improve nullability in MethodCall.cs - still some strangeness left over. --- src/Moq/MethodCall.cs | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/Moq/MethodCall.cs b/src/Moq/MethodCall.cs index ffe4ce15d..2cf489fd8 100644 --- a/src/Moq/MethodCall.cs +++ b/src/Moq/MethodCall.cs @@ -20,16 +20,16 @@ namespace Moq { sealed partial class MethodCall : SetupWithOutParameterSupport { - VerifyInvocationCount verifyInvocationCount; - Behavior callback; - Behavior raiseEvent; - Behavior returnOrThrow; - Behavior afterReturnCallback; - Condition condition; - string failMessage; - string declarationSite; - - public MethodCall(Expression originalExpression, Mock mock, Condition condition, MethodExpectation expectation) + VerifyInvocationCount? verifyInvocationCount; + Behavior? callback; + Behavior? raiseEvent; + Behavior? returnOrThrow; + Behavior? afterReturnCallback; + Condition? condition; + string? failMessage; + string? declarationSite; + + public MethodCall(Expression originalExpression, Mock mock, Condition? condition, MethodExpectation expectation) : base(originalExpression, mock, expectation) { this.condition = condition; @@ -40,12 +40,12 @@ public MethodCall(Expression originalExpression, Mock mock, Condition condition, } } - public string FailMessage + public string? FailMessage { get => this.failMessage; } - public override Condition Condition => this.condition; + public override Condition? Condition => this.condition; public override IEnumerable InnerMocks { @@ -59,7 +59,7 @@ public override IEnumerable InnerMocks } } - static string GetUserCodeCallSite() + static string? GetUserCodeCallSite() { try { @@ -68,14 +68,14 @@ static string GetUserCodeCallSite() var frame = new StackTrace(true) .GetFrames() .SkipWhile(f => f.GetMethod() != thisMethod) - .SkipWhile(f => f.GetMethod().DeclaringType == null || f.GetMethod().DeclaringType.Assembly == mockAssembly) + .SkipWhile(f => f.GetMethod()!.DeclaringType == null || f.GetMethod()!.DeclaringType!.Assembly == mockAssembly) .FirstOrDefault(); var member = frame?.GetMethod(); if (member != null) { var declaredAt = new StringBuilder(); - declaredAt.AppendNameOf(member.DeclaringType).Append('.').AppendNameOf(member, false); - var fileName = Path.GetFileName(frame.GetFileName()); + declaredAt.AppendNameOf(member.DeclaringType!).Append('.').AppendNameOf(member, false); + var fileName = Path.GetFileName(frame!.GetFileName()); if (fileName != null) { declaredAt.Append(" in ").Append(fileName); @@ -144,7 +144,7 @@ public void SetCallbackBehavior(Delegate callback) throw new ArgumentNullException(nameof(callback)); } - ref Behavior behavior = ref (this.returnOrThrow == null) ? ref this.callback + ref Behavior? behavior = ref (this.returnOrThrow == null) ? ref this.callback : ref this.afterReturnCallback; if (callback is Action callbackWithoutArguments) @@ -224,7 +224,7 @@ public void SetReturnValueBehavior(object value) this.returnOrThrow = new ReturnValue(value); } - public void SetReturnComputedValueBehavior(Delegate valueFactory) + public void SetReturnComputedValueBehavior(Delegate? valueFactory) { Debug.Assert(this.Method.ReturnType != typeof(void)); Debug.Assert(this.returnOrThrow == null); @@ -302,7 +302,7 @@ public void SetThrowExceptionBehavior(Exception exception) this.returnOrThrow = new ThrowException(exception); } - public void SetThrowComputedExceptionBehavior(Delegate exceptionFactory) + public void SetThrowComputedExceptionBehavior(Delegate? exceptionFactory) { Debug.Assert(this.returnOrThrow == null); @@ -314,6 +314,7 @@ public void SetThrowComputedExceptionBehavior(Delegate exceptionFactory) // and instead of in `Throws(TException)`, we ended up in `Throws(Delegate)` or `Throws(Func)`, // which likely isn't what the user intended. // So here we do what we would've done in `Throws(TException)`: + // TODO: ThrowException expects non-null argument. this.returnOrThrow = new ThrowException(default); } else From 782f1da59f400436e6e786e98840b08cab96befe Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:17:13 +0000 Subject: [PATCH 72/93] Tidy nullability in Guard.cs. --- src/Moq/Guard.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Moq/Guard.cs b/src/Moq/Guard.cs index a36437c2f..647c11dc1 100644 --- a/src/Moq/Guard.cs +++ b/src/Moq/Guard.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq.Expressions; using System.Reflection; @@ -165,7 +166,11 @@ public static void IsEventRemove(LambdaExpression expression, string paramName) /// Ensures the given is not null. /// Throws otherwise. /// - public static void NotNull(object value, string paramName) +#if NULLABLE_REFERENCE_TYPES + public static void NotNull([NotNull] object? value, string paramName) +#else + public static void NotNull(object? value, string paramName) +#endif { if (value == null) { From 70694226024a046284e0c90e4b2679f2ccd91f87 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:25:35 +0000 Subject: [PATCH 73/93] Tidy nullability in CastleProxyFactory.cs, InterceptionAspects.cs and ProxyFactory.cs. --- src/Moq/Interception/CastleProxyFactory.cs | 2 +- src/Moq/Interception/InterceptionAspects.cs | 4 ++-- src/Moq/Interception/ProxyFactory.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Moq/Interception/CastleProxyFactory.cs b/src/Moq/Interception/CastleProxyFactory.cs index f07cbce7b..c17d9be42 100644 --- a/src/Moq/Interception/CastleProxyFactory.cs +++ b/src/Moq/Interception/CastleProxyFactory.cs @@ -44,7 +44,7 @@ ProxyGenerator GetClassGenerator(Type mockType) } /// - public override object CreateProxy(Type mockType, Moq.IInterceptor interceptor, Type[] interfaces, object[] arguments) + public override object CreateProxy(Type mockType, Moq.IInterceptor interceptor, Type[] interfaces, object?[] arguments) { // All generated proxies need to implement `IProxy`: var additionalInterfaces = new Type[1 + interfaces.Length]; diff --git a/src/Moq/Interception/InterceptionAspects.cs b/src/Moq/Interception/InterceptionAspects.cs index a3bf1b790..6b92f5e7c 100644 --- a/src/Moq/Interception/InterceptionAspects.cs +++ b/src/Moq/Interception/InterceptionAspects.cs @@ -117,7 +117,7 @@ public static bool Handle(Invocation invocation, Mock mock) if (methodName[0] == 'a' && methodName[3] == '_' && invocation.Method.IsEventAddAccessor()) { var implementingMethod = invocation.Method.GetImplementingMethod(invocation.ProxyType); - var @event = implementingMethod.DeclaringType.GetEvents(bindingFlags).SingleOrDefault(e => e.GetAddMethod(true) == implementingMethod); + var @event = implementingMethod.DeclaringType!.GetEvents(bindingFlags).SingleOrDefault(e => e.GetAddMethod(true) == implementingMethod); if (@event != null) { if (mock.CallBase && !invocation.Method.IsAbstract) @@ -135,7 +135,7 @@ public static bool Handle(Invocation invocation, Mock mock) else if (methodName[0] == 'r' && methodName.Length > 7 && methodName[6] == '_' && invocation.Method.IsEventRemoveAccessor()) { var implementingMethod = invocation.Method.GetImplementingMethod(invocation.ProxyType); - var @event = implementingMethod.DeclaringType.GetEvents(bindingFlags).SingleOrDefault(e => e.GetRemoveMethod(true) == implementingMethod); + var @event = implementingMethod.DeclaringType!.GetEvents(bindingFlags).SingleOrDefault(e => e.GetRemoveMethod(true) == implementingMethod); if (@event != null) { if (mock.CallBase && !invocation.Method.IsAbstract) diff --git a/src/Moq/Interception/ProxyFactory.cs b/src/Moq/Interception/ProxyFactory.cs index 6ad56f18c..ca35694b9 100644 --- a/src/Moq/Interception/ProxyFactory.cs +++ b/src/Moq/Interception/ProxyFactory.cs @@ -13,7 +13,7 @@ abstract class ProxyFactory /// public static ProxyFactory Instance { get; } = new CastleProxyFactory(); - public abstract object CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, object[] arguments); + public abstract object CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, object?[] arguments); public abstract bool IsMethodVisible(MethodInfo method, out string messageIfNotVisible); From c0e646c62a45debcd810b56f097b5a1124617156 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:31:06 +0000 Subject: [PATCH 74/93] Fix up nullability in MatcherAttributeMatcher.cs. --- src/Moq/Matchers/MatcherAttributeMatcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Matchers/MatcherAttributeMatcher.cs b/src/Moq/Matchers/MatcherAttributeMatcher.cs index cc8472a13..1cd0f7bc7 100644 --- a/src/Moq/Matchers/MatcherAttributeMatcher.cs +++ b/src/Moq/Matchers/MatcherAttributeMatcher.cs @@ -85,7 +85,7 @@ public bool Matches(object? argument, Type parameterType) var extraArgs = this.expression.Arguments.Select(ae => ((ConstantExpression)ae.PartialEval()).Value); var args = new[] { argument }.Concat(extraArgs).ToArray(); // for static and non-static method - var instance = this.expression.Object == null ? null : (this.expression.Object.PartialEval() as ConstantExpression).Value; + var instance = this.expression.Object == null ? null : ((ConstantExpression)this.expression.Object.PartialEval()).Value; return (bool)validatorMethod.Invoke(instance, args)!; } From ddf91e0af5785c4551c2f66f1cf71e0cdc622cdb Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:37:57 +0000 Subject: [PATCH 75/93] Tidy nullability in ActionObserver.cs. --- src/Moq/ActionObserver.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Moq/ActionObserver.cs b/src/Moq/ActionObserver.cs index c9d23e521..aa685924e 100644 --- a/src/Moq/ActionObserver.cs +++ b/src/Moq/ActionObserver.cs @@ -27,7 +27,7 @@ namespace Moq /// sealed class ActionObserver : ExpressionReconstructor { - public override Expression> ReconstructExpression(Action action, object[]? ctorArgs = null) + public override Expression> ReconstructExpression(Action action, object?[]? ctorArgs = null) { using (var matcherObserver = MatcherObserver.Activate()) { @@ -228,10 +228,10 @@ bool CanDistribute(int msi, int asi) } // Creates a proxy (way more light-weight than a `Mock`!) with an invocation `Recorder` attached to it. - static IProxy CreateProxy(Type type, object[]? ctorArgs, MatcherObserver matcherObserver, out Recorder recorder) + static IProxy CreateProxy(Type type, object?[]? ctorArgs, MatcherObserver matcherObserver, out Recorder recorder) { recorder = new Recorder(matcherObserver); - return (IProxy)ProxyFactory.Instance.CreateProxy(type, recorder, Type.EmptyTypes, ctorArgs ?? new object[0]); + return (IProxy)ProxyFactory.Instance.CreateProxy(type, recorder, Type.EmptyTypes, ctorArgs ?? new object?[0]); } From 7fbc056e6f292691b3447eb76382ef301f915aeb Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:38:47 +0000 Subject: [PATCH 76/93] Tidy nullability in MockSetupsBuilder.cs. --- src/Moq/Linq/MockSetupsBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Linq/MockSetupsBuilder.cs b/src/Moq/Linq/MockSetupsBuilder.cs index 77a946eeb..c73583486 100644 --- a/src/Moq/Linq/MockSetupsBuilder.cs +++ b/src/Moq/Linq/MockSetupsBuilder.cs @@ -103,7 +103,7 @@ protected override Expression VisitUnary(UnaryExpression node) return base.VisitUnary(node); } - static Expression ConvertToSetup(Expression left, Expression right) + static Expression? ConvertToSetup(Expression left, Expression right) { switch (left.NodeType) { From 9abd02b3ee276d0c1e3d645e63c11a3163cfa7b6 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:47:13 +0000 Subject: [PATCH 77/93] Tidy nullability in StringBuilderExtensions.AppendExpression.cs. --- src/Moq/StringBuilderExtensions.AppendExpression.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Moq/StringBuilderExtensions.AppendExpression.cs b/src/Moq/StringBuilderExtensions.AppendExpression.cs index 5204b502b..518e52038 100644 --- a/src/Moq/StringBuilderExtensions.AppendExpression.cs +++ b/src/Moq/StringBuilderExtensions.AppendExpression.cs @@ -17,7 +17,7 @@ namespace Moq // These methods are intended to create more readable string representations for use in failure messages. partial class StringBuilderExtensions { - public static StringBuilder AppendExpression(this StringBuilder builder, Expression expression) + public static StringBuilder AppendExpression(this StringBuilder builder, Expression? expression) { if (expression == null) { @@ -273,7 +273,7 @@ static StringBuilder AppendExpression(this StringBuilder builder, MemberExpressi } else { - builder.AppendNameOf(expression.Member.DeclaringType); + builder.AppendNameOf(expression.Member.DeclaringType!); } return builder.Append('.') @@ -300,7 +300,7 @@ static StringBuilder AppendExpression(this StringBuilder builder, MethodCallExpr { Debug.Assert(method.IsStatic); - builder.AppendNameOf(method.DeclaringType); + builder.AppendNameOf(method.DeclaringType!); } if (method.IsGetAccessor()) @@ -380,7 +380,7 @@ static StringBuilder AppendExpression(this StringBuilder builder, LambdaExpressi static StringBuilder AppendExpression(this StringBuilder builder, NewExpression expression) { - Type type = (expression.Constructor == null) ? expression.Type : expression.Constructor.DeclaringType; + Type type = (expression.Constructor == null) ? expression.Type : expression.Constructor.DeclaringType!; return builder.Append("new ") .AppendNameOf(type) .AppendCommaSeparated("(", expression.Arguments, AppendExpression, ")"); @@ -395,7 +395,7 @@ static StringBuilder AppendExpression(this StringBuilder builder, NewArrayExpres case ExpressionType.NewArrayBounds: return builder.Append("new ") - .AppendNameOf(expression.Type.GetElementType()) + .AppendNameOf(expression.Type.GetElementType()!) .AppendCommaSeparated("[", expression.Expressions, AppendExpression, "]"); } From ba857e7bbba6dca8a641bda86b596d762e996212 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:47:59 +0000 Subject: [PATCH 78/93] Tidy nullability in ExpressionReconstructor.cs. --- src/Moq/ExpressionReconstructor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/ExpressionReconstructor.cs b/src/Moq/ExpressionReconstructor.cs index c70600ccd..196fc8209 100644 --- a/src/Moq/ExpressionReconstructor.cs +++ b/src/Moq/ExpressionReconstructor.cs @@ -29,6 +29,6 @@ protected ExpressionReconstructor() /// /// The delegate for which to reconstruct a LINQ expression tree. /// Arguments to pass to a parameterized constructor of . (Optional.) - public abstract Expression> ReconstructExpression(Action action, object[]? ctorArgs = null); + public abstract Expression> ReconstructExpression(Action action, object?[]? ctorArgs = null); } } From 26221cd7d67a1b7c698ff20e746a65bdeff96064 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:53:35 +0000 Subject: [PATCH 79/93] Tidy nullability of Evaluator.cs. --- src/Moq/Evaluator.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Moq/Evaluator.cs b/src/Moq/Evaluator.cs index fd9614a90..4ce3c4681 100644 --- a/src/Moq/Evaluator.cs +++ b/src/Moq/Evaluator.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Moq @@ -53,7 +54,10 @@ internal Expression Eval(Expression exp) return this.Visit(exp); } - public override Expression Visit(Expression exp) +#if NULLABLE_REFERENCE_TYPES + [return: NotNullIfNotNull("exp")] +#endif + public override Expression? Visit(Expression? exp) { if (exp == null) { @@ -85,7 +89,7 @@ static Expression Evaluate(Expression e) class Nominator : ExpressionVisitor { Func fnCanBeEvaluated; - HashSet candidates; + HashSet candidates = null!; bool cannotBeEvaluated; internal Nominator(Func fnCanBeEvaluated) @@ -100,7 +104,7 @@ internal HashSet Nominate(Expression expression) return this.candidates; } - public override Expression Visit(Expression expression) + public override Expression? Visit(Expression? expression) { if (expression != null && expression.NodeType != ExpressionType.Quote) { From 110780f27aa40507cebce8f79d0f2db2883882af Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 00:59:35 +0000 Subject: [PATCH 80/93] Tidy nullability in ItExpr.cs. --- src/Moq/Protected/ItExpr.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/Protected/ItExpr.cs b/src/Moq/Protected/ItExpr.cs index 4e655bf42..e2e77633a 100644 --- a/src/Moq/Protected/ItExpr.cs +++ b/src/Moq/Protected/ItExpr.cs @@ -113,7 +113,7 @@ public static Expression IsAny() /// public static Expression Is(Expression> match) { - Expression> expr = () => It.Is((Expression>)null); + Expression> expr = () => It.Is((Expression>)null!); return Expression.Call(((MethodCallExpression)expr.Body).Method, match); } From 320f9cffbe839fc9a311856f0d647a1225e205e4 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:00:17 +0000 Subject: [PATCH 81/93] Tidy nullability in MockFactory.cs. --- src/Moq/Obsolete/MockFactory.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/Obsolete/MockFactory.cs b/src/Moq/Obsolete/MockFactory.cs index eb48d3892..023aeb4d1 100644 --- a/src/Moq/Obsolete/MockFactory.cs +++ b/src/Moq/Obsolete/MockFactory.cs @@ -222,7 +222,7 @@ public Mock Create() /// factory.Verify(); /// /// - public Mock Create(params object[] args) + public Mock Create(params object[]? args) where T : class { // "fix" compiler picking this overload instead of @@ -314,7 +314,7 @@ public Mock Create(Expression> newExpression, MockBehavior behavio /// Type to mock. /// The behavior for the new mock. /// Optional arguments for the construction of the mock. - protected virtual Mock CreateMock(MockBehavior behavior, object[] args) + protected virtual Mock CreateMock(MockBehavior behavior, object[]? args) where T : class { var mock = new Mock(behavior, args); From 5befbbd7d8c18e7c192fec8ed1ab392fcdfba9f2 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:01:27 +0000 Subject: [PATCH 82/93] Tidy AsInterface.cs. --- src/Moq/AsInterface.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/AsInterface.cs b/src/Moq/AsInterface.cs index dd4ae509f..d58936758 100644 --- a/src/Moq/AsInterface.cs +++ b/src/Moq/AsInterface.cs @@ -19,9 +19,9 @@ public AsInterface(Mock owner) internal override List AdditionalInterfaces => this.owner.AdditionalInterfaces; - internal override Dictionary ConfiguredDefaultValues => this.owner.ConfiguredDefaultValues; + internal override Dictionary ConfiguredDefaultValues => this.owner.ConfiguredDefaultValues; - internal override object[] ConstructorArguments => this.owner.ConstructorArguments; + internal override object?[] ConstructorArguments => this.owner.ConstructorArguments; internal override InvocationCollection MutableInvocations => this.owner.MutableInvocations; From f01aee1fe42b23acf0aea38664dd04844d1b3788 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:02:06 +0000 Subject: [PATCH 83/93] Tidy ConstructorCallVisitor.cs. Fields `constructor` and `arguments` still need dealing with. --- .../Visitors/ConstructorCallVisitor.cs | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Moq/Expressions/Visitors/ConstructorCallVisitor.cs b/src/Moq/Expressions/Visitors/ConstructorCallVisitor.cs index 1a4917138..627792521 100644 --- a/src/Moq/Expressions/Visitors/ConstructorCallVisitor.cs +++ b/src/Moq/Expressions/Visitors/ConstructorCallVisitor.cs @@ -2,6 +2,7 @@ // All rights reserved. Licensed under the BSD 3-Clause License; see License.txt. using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Linq.Expressions; @@ -36,13 +37,18 @@ public static object[] ExtractArgumentValues(LambdaExpression newExpression) return visitor.arguments; } - ConstructorInfo constructor; + ConstructorInfo? constructor; object[] arguments; - public override Expression Visit(Expression node) +#if NULLABLE_REFERENCE_TYPES + [return: NotNullIfNotNull("node")] +#endif + public override Expression? Visit(Expression? node) { - switch (node.NodeType) + switch (node?.NodeType) { + case null: + return null; case ExpressionType.Lambda: case ExpressionType.New: case ExpressionType.Quote: @@ -58,18 +64,15 @@ public override Expression Visit(Expression node) protected override Expression VisitNew(NewExpression node) { - if (node != null) - { - constructor = node.Constructor; + constructor = node.Constructor; - // Creates a lambda which uses the same argument expressions as the - // arguments contained in the NewExpression - var argumentExtractor = Expression.Lambda>( - Expression.NewArrayInit( - typeof(object), - node.Arguments.Select(a => Expression.Convert(a, typeof(object))))); - arguments = ExpressionCompiler.Instance.Compile(argumentExtractor).Invoke(); - } + // Creates a lambda which uses the same argument expressions as the + // arguments contained in the NewExpression + var argumentExtractor = Expression.Lambda>( + Expression.NewArrayInit( + typeof(object), + node.Arguments.Select(a => Expression.Convert(a, typeof(object))))); + arguments = ExpressionCompiler.Instance.Compile(argumentExtractor).Invoke(); return node; } } From dc60cd2bcd3b3b995d20901ebfdefbadf4e3eb92 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:02:51 +0000 Subject: [PATCH 84/93] Tidy ReturnsExtensions.cs nullability. --- src/Moq/ReturnsExtensions.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Moq/ReturnsExtensions.cs b/src/Moq/ReturnsExtensions.cs index 5247edcb5..dd61fd754 100644 --- a/src/Moq/ReturnsExtensions.cs +++ b/src/Moq/ReturnsExtensions.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Moq.Language; @@ -48,11 +49,11 @@ public static IReturnsResult ReturnsAsync(this IReturnsType of the return value. /// Returns verb which represents the mocked type and the task of return type /// The function that will calculate the return value. - public static IReturnsResult ReturnsAsync(this IReturns> mock, Func valueFunction) where TMock : class + public static IReturnsResult ReturnsAsync(this IReturns> mock, Func? valueFunction) where TMock : class { if (IsNullResult(valueFunction, typeof(TResult))) { - return mock.ReturnsAsync(() => default); + return mock.ReturnsAsync(() => default!); } return mock.Returns(() => Task.FromResult(valueFunction())); @@ -69,7 +70,7 @@ public static IReturnsResult ReturnsAsync(this IReturns default); + return mock.ReturnsAsync(() => default!); } return mock.Returns(() => new ValueTask(valueFunction())); @@ -283,7 +284,11 @@ public static IReturnsResult ThrowsAsync(this IReturns Date: Wed, 26 Feb 2025 01:03:41 +0000 Subject: [PATCH 85/93] Tidy nullability in Mock`1.cs. --- src/Moq/Mock`1.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Moq/Mock`1.cs b/src/Moq/Mock`1.cs index 54a5f295c..2279f986e 100644 --- a/src/Moq/Mock`1.cs +++ b/src/Moq/Mock`1.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Text; @@ -84,10 +85,10 @@ static Mock() serialNumberCounter = 0; } - T instance; + T? instance; List additionalInterfaces; - Dictionary configuredDefaultValues; - object[] constructorArguments; + Dictionary configuredDefaultValues; + object?[] constructorArguments; DefaultValueProvider defaultValueProvider; EventHandlerCollection eventHandlers; InvocationCollection invocations; @@ -110,6 +111,7 @@ internal Mock(bool skipInitialize) // The skipInitialize parameter is not used at all, and it's // just to differentiate this ctor that should do nothing // from the regular ones which initializes the proxy, etc. + // TODO: How should nullable references be handled here? } /// @@ -176,7 +178,7 @@ public Mock(MockBehavior behavior, params object?[]? args) this.additionalInterfaces = new List(); this.behavior = behavior; - this.configuredDefaultValues = new Dictionary(); + this.configuredDefaultValues = new Dictionary(); this.constructorArguments = args; this.defaultValueProvider = DefaultValueProvider.Empty; this.eventHandlers = new EventHandlerCollection(); @@ -248,9 +250,9 @@ public override bool CallBase } } - internal override object[] ConstructorArguments => this.constructorArguments; + internal override object?[] ConstructorArguments => this.constructorArguments; - internal override Dictionary ConfiguredDefaultValues => this.configuredDefaultValues; + internal override Dictionary ConfiguredDefaultValues => this.configuredDefaultValues; /// /// Gets or sets the instance that will be used @@ -295,6 +297,9 @@ public override string ToString() return this.Name; } +#if NET + [MemberNotNull(nameof(instance))] +#endif void InitializeInstance() { // Determine the set of interfaces that the proxy object should additionally implement. @@ -623,7 +628,7 @@ public Mock SetupProperty(Expression> property) /// Assert.Equal(6, v.Value); /// /// - public Mock SetupProperty(Expression> property, TProperty initialValue) + public Mock SetupProperty(Expression> property, TProperty? initialValue) { Mock.SetupProperty(this, property, initialValue); return this; From 289fad3e874d9478ae8fe5cdb4e81e9d1556c819 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:03:56 +0000 Subject: [PATCH 86/93] Tidy nullability in MockException.cs. --- src/Moq/MockException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Moq/MockException.cs b/src/Moq/MockException.cs index ead78710c..ebd608bc3 100644 --- a/src/Moq/MockException.cs +++ b/src/Moq/MockException.cs @@ -248,7 +248,7 @@ protected MockException( System.Runtime.Serialization.StreamingContext context) : base(info, context) { - this.reasons = (MockExceptionReasons)info.GetValue(nameof(this.reasons), typeof(MockExceptionReasons)); + this.reasons = (MockExceptionReasons)info.GetValue(nameof(this.reasons), typeof(MockExceptionReasons))!; } /// From e88a34d9b693fd853cca7a92abe7824dc507842f Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:05:27 +0000 Subject: [PATCH 87/93] Partially fix up nullability in ProtectedMock.cs. --- src/Moq/Protected/ProtectedMock.cs | 35 +++++++++++++++++++----------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/Moq/Protected/ProtectedMock.cs b/src/Moq/Protected/ProtectedMock.cs index 9291421ab..2007312f6 100644 --- a/src/Moq/Protected/ProtectedMock.cs +++ b/src/Moq/Protected/ProtectedMock.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Linq.Expressions; @@ -51,7 +52,7 @@ public ISetup Setup(string methodName, Type[] genericTypeArguments, bool exac return this.InternalSetup(methodName, genericTypeArguments, exactParameterMatch, args); } - ISetup InternalSetup(string methodName, Type[] genericTypeArguments, bool exactParameterMatch, params object[] args) + ISetup InternalSetup(string methodName, Type[]? genericTypeArguments, bool exactParameterMatch, params object[] args) { Guard.NotNull(methodName, nameof(methodName)); @@ -80,7 +81,7 @@ public ISetup Setup(string methodName, Type[] genericTypeAr return this.InternalSetup(methodName, genericTypeArguments, exactParameterMatch, args); } - ISetup InternalSetup(string methodName, Type[] genericTypeArguments, + ISetup InternalSetup(string methodName, Type[]? genericTypeArguments, bool exactParameterMatch, params object[] args) { Guard.NotNullOrEmpty(methodName, nameof(methodName)); @@ -147,7 +148,7 @@ public ISetupSequentialAction SetupSequence(string methodOrPropertyName, Type[] return this.InternalSetupSequence(methodOrPropertyName, genericTypeArguments, exactParameterMatch, args); } - ISetupSequentialAction InternalSetupSequence(string methodOrPropertyName, Type[] genericTypeArguments, bool exactParameterMatch, params object[] args) + ISetupSequentialAction InternalSetupSequence(string methodOrPropertyName, Type[]? genericTypeArguments, bool exactParameterMatch, params object[] args) { Guard.NotNullOrEmpty(methodOrPropertyName, nameof(methodOrPropertyName)); @@ -175,7 +176,7 @@ public ISetupSequentialResult SetupSequence(string methodOrPro return this.InternalSetupSequence(methodOrPropertyName, genericTypeArguments, exactParameterMatch, args); } - ISetupSequentialResult InternalSetupSequence(string methodOrPropertyName, Type[] genericTypeArguments, bool exactParameterMatch, params object[] args) + ISetupSequentialResult InternalSetupSequence(string methodOrPropertyName, Type[]? genericTypeArguments, bool exactParameterMatch, params object[] args) { Guard.NotNullOrEmpty(methodOrPropertyName, nameof(methodOrPropertyName)); @@ -223,7 +224,7 @@ public void Verify(string methodName, Type[] genericTypeArguments, Times times, this.InternalVerify(methodName, genericTypeArguments, times, exactParameterMatch, args); } - void InternalVerify(string methodName, Type[] genericTypeArguments, Times times, bool exactParameterMatch, params object[] args) + void InternalVerify(string methodName, Type[]? genericTypeArguments, Times times, bool exactParameterMatch, params object[] args) { Guard.NotNullOrEmpty(methodName, nameof(methodName)); @@ -256,7 +257,7 @@ public void Verify(string methodName, Type[] genericTypeArguments, Time this.InternalVerify(methodName, genericTypeArguments, times, exactParameterMatch, args); } - void InternalVerify(string methodName, Type[] genericTypeArguments, Times times, bool exactParameterMatch, params object[] args) + void InternalVerify(string methodName, Type[]? genericTypeArguments, Times times, bool exactParameterMatch, params object[] args) { Guard.NotNullOrEmpty(methodName, nameof(methodName)); @@ -359,7 +360,11 @@ static Expression> GetSetterExpression(PropertyInfo property, Expressi param); } - static void ThrowIfMemberMissing(string memberName, MemberInfo member) +#if NULLABLE_REFERENCE_TYPES + static void ThrowIfMemberMissing(string memberName, [NotNull] MemberInfo? member) +#else + static void ThrowIfMemberMissing(string memberName, MemberInfo? member) +#endif { if (member == null) { @@ -371,7 +376,11 @@ static void ThrowIfMemberMissing(string memberName, MemberInfo member) } } - static void ThrowIfMethodMissing(string methodName, MethodInfo method, object[] args) +#if NULLABLE_REFERENCE_TYPES + static void ThrowIfMethodMissing(string methodName, [NotNull] MethodInfo? method, object[] args) +#else + static void ThrowIfMethodMissing(string methodName, MethodInfo? method, object[] args) +#endif { if (method == null) { @@ -443,14 +452,14 @@ static void ThrowIfVoidMethod(MethodInfo method) } } - static Type[] ToArgTypes(object[] args) + static Type?[] ToArgTypes(object[] args) { if (args == null) { throw new ArgumentException(Resources.UseItExprIsNullRatherThanNullArgumentValue); } - var types = new Type[args.Length]; + var types = new Type?[args.Length]; for (int index = 0; index < args.Length; index++) { if (args[index] == null) @@ -503,9 +512,9 @@ static bool IsItRefAny(Expression expression) return ItRefAnyField(expression) != null; } - static FieldInfo ItRefAnyField(Expression expr) + static FieldInfo? ItRefAnyField(Expression expr) { - FieldInfo itRefAnyField = null; + FieldInfo? itRefAnyField = null; if (expr.NodeType == ExpressionType.MemberAccess) { @@ -514,7 +523,7 @@ static FieldInfo ItRefAnyField(Expression expr) { if (field.Name == nameof(It.Ref.IsAny)) { - var fieldDeclaringType = field.DeclaringType; + var fieldDeclaringType = field.DeclaringType!; if (fieldDeclaringType.IsGenericType) { var fieldDeclaringTypeDefinition = fieldDeclaringType.GetGenericTypeDefinition(); From bc0d5f9a327ee4e0c3aaa7ed5e1fa7c22c0f9ff2 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:06:55 +0000 Subject: [PATCH 88/93] Tidy nullability in Mock.cs. --- src/Moq/Mock.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index 41a5a9060..b9a4422e4 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -151,7 +151,7 @@ public static void VerifyAll(params Mock[] mocks) /// public abstract bool CallBase { get; set; } - internal abstract object[] ConstructorArguments { get; } + internal abstract object?[] ConstructorArguments { get; } /// /// Specifies the behavior to use when returning default values for unexpected invocations on loose mocks. @@ -397,7 +397,7 @@ static void VerifyNoOtherCalls(Mock mock, HashSet verifiedMocks) // In order for an invocation to be "transitive", its return value has to be a // sub-object (inner mock); and that sub-object has to have received at least // one call: - var wasTransitiveInvocation = mock.MutableSetups.FindLastInnerMock(setup => setup.Matches(unverifiedInvocations[i])) is Mock innerMock + var wasTransitiveInvocation = mock.MutableSetups.FindLastInnerMock(setup => setup.Matches(unverifiedInvocations[i]!)) is Mock innerMock && innerMock.MutableInvocations.Any(); if (wasTransitiveInvocation) { @@ -518,7 +518,7 @@ internal static MethodCall SetupGet(Mock mock, LambdaExpression expression, Cond return Mock.Setup(mock, expression, condition); } - internal static MethodCall SetupSet(Mock mock, LambdaExpression expression, Condition condition) + internal static MethodCall SetupSet(Mock mock, LambdaExpression expression, Condition? condition) { Guard.NotNull(expression, nameof(expression)); Guard.IsAssignmentToPropertyOrIndexer(expression, nameof(expression)); @@ -583,7 +583,7 @@ internal static bool SetupReturns(Mock mock, LambdaExpression expression, object return true; } - internal static MethodCall SetupAdd(Mock mock, LambdaExpression expression, Condition condition) + internal static MethodCall SetupAdd(Mock mock, LambdaExpression expression, Condition? condition) { Guard.NotNull(expression, nameof(expression)); Guard.IsEventAdd(expression, nameof(expression)); @@ -591,7 +591,7 @@ internal static MethodCall SetupAdd(Mock mock, LambdaExpression expression, Cond return Mock.Setup(mock, expression, condition); } - internal static MethodCall SetupRemove(Mock mock, LambdaExpression expression, Condition condition) + internal static MethodCall SetupRemove(Mock mock, LambdaExpression expression, Condition? condition) { Guard.NotNull(expression, nameof(expression)); Guard.IsEventRemove(expression, nameof(expression)); @@ -611,7 +611,7 @@ internal static SequenceSetup SetupSequence(Mock mock, LambdaExpression expressi }); } - internal static StubbedPropertySetup SetupProperty(Mock mock, LambdaExpression expression, object initialValue) + internal static StubbedPropertySetup SetupProperty(Mock mock, LambdaExpression expression, object? initialValue) { Guard.NotNull(expression, nameof(expression)); From b4e4d5ebf645361b097ccaeb0feffef2419e2748 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:09:04 +0000 Subject: [PATCH 89/93] Some nullability improvements to ExpressionExtensions.cs. It seems like nullability is a little dodgy here. --- src/Moq/ExpressionExtensions.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Moq/ExpressionExtensions.cs b/src/Moq/ExpressionExtensions.cs index 1b2b324af..805a39272 100644 --- a/src/Moq/ExpressionExtensions.cs +++ b/src/Moq/ExpressionExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Linq.Expressions; @@ -64,7 +65,11 @@ internal static TDelegate CompileUsingExpressionCompiler(this Express return ExpressionCompiler.Instance.Compile(expression); } - public static bool IsMatch(this Expression expression, out Match match) +#if NULLABLE_REFERENCE_TYPES + public static bool IsMatch(this Expression expression, [NotNullWhen(true)] out Match? match) +#else + public static bool IsMatch(this Expression expression, out Match? match) +#endif { if (expression is MatchExpression matchExpression) { @@ -262,6 +267,7 @@ void Split(Expression e, out Expression r /* remainder */, out MethodExpectation } else // This should be unreachable. { + // TODO: Should we throw here? method = null; } p = new MethodExpectation( @@ -286,7 +292,7 @@ void Split(Expression e, out Expression r /* remainder */, out MethodExpectation expression: Expression.Lambda( Expression.Invoke(parameter, arguments), parameter), - method: r.Type.GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance), + method: r.Type.GetMethod("Invoke", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)!, arguments); return; } @@ -321,7 +327,8 @@ void Split(Expression e, out Expression r /* remainder */, out MethodExpectation } else // This should be unreachable. { - method = null; + // TODO: Should we throw here? + method = null!; } p = new MethodExpectation( expression: Expression.Lambda( @@ -341,7 +348,7 @@ void Split(Expression e, out Expression r /* remainder */, out MethodExpectation bool IsResult(MemberInfo member, out IAwaitableFactory? awaitableFactory) { - var instanceType = member.DeclaringType; + var instanceType = member.DeclaringType!; awaitableFactory = AwaitableFactory.TryGet(instanceType); var returnType = member switch { @@ -363,7 +370,7 @@ internal static PropertyInfo GetReboundProperty(this MemberExpression expression // the expression. we attempt to correct this here by checking whether the type of the accessed object // has a property by the same name whose base definition equals the property in the expression; if so, // we "upgrade" to the derived property. - if (property.DeclaringType != expression.Expression.Type) + if (property.DeclaringType != expression.Expression!.Type) { var parameterTypes = new ParameterTypes(property.GetIndexParameters()); var derivedProperty = expression.Expression.Type From a92acc6a3094d4438c4b9957c74a4d55fc7c4ca1 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:09:55 +0000 Subject: [PATCH 90/93] Tidy nullability in It.cs. --- src/Moq/It.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Moq/It.cs b/src/Moq/It.cs index d6c81f237..1bfc9d996 100644 --- a/src/Moq/It.cs +++ b/src/Moq/It.cs @@ -32,7 +32,7 @@ public static class Ref /// /// Matches any value that is assignment-compatible with type . /// - public static TValue IsAny; + public static TValue IsAny = default(TValue)!; } /// @@ -64,7 +64,7 @@ public static TValue IsAny() } } - static readonly MethodInfo isAnyMethod = typeof(It).GetMethod(nameof(It.IsAny), BindingFlags.Public | BindingFlags.Static); + static readonly MethodInfo isAnyMethod = typeof(It).GetMethod(nameof(It.IsAny), BindingFlags.Public | BindingFlags.Static)!; internal static MethodCallExpression IsAny(Type genericArgument) { @@ -143,7 +143,7 @@ public static TValue Is(Expression> match) throw new ArgumentException(Resources.UseItIsOtherOverload, nameof(match)); } - var thisMethod = (MethodInfo)MethodBase.GetCurrentMethod(); + var thisMethod = (MethodInfo)MethodBase.GetCurrentMethod()!; var compiledMatchMethod = match.CompileUsingExpressionCompiler(); return Match.Create( @@ -165,9 +165,9 @@ public static TValue Is(Expression> match) /// Allows the specification of a predicate to perform matching of method call arguments. /// [EditorBrowsable(EditorBrowsableState.Advanced)] - public static TValue Is(Expression> match) + public static TValue Is(Expression> match) { - var thisMethod = (MethodInfo)MethodBase.GetCurrentMethod(); + var thisMethod = (MethodInfo)MethodBase.GetCurrentMethod()!; var compiledMatchMethod = match.CompileUsingExpressionCompiler(); return Match.Create( From 13d0344fed98889acd56c19cfd981ae18a64a33c Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Wed, 26 Feb 2025 01:18:34 +0000 Subject: [PATCH 91/93] Revert change to Recorder.invocationTimestamp. --- src/Moq/ActionObserver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Moq/ActionObserver.cs b/src/Moq/ActionObserver.cs index aa685924e..6b8e9bb19 100644 --- a/src/Moq/ActionObserver.cs +++ b/src/Moq/ActionObserver.cs @@ -242,7 +242,7 @@ sealed class Recorder : IInterceptor readonly MatcherObserver matcherObserver; int creationTimestamp; Invocation? invocation; - int? invocationTimestamp; + int invocationTimestamp; object? returnValue; public Recorder(MatcherObserver matcherObserver) @@ -260,7 +260,7 @@ public IEnumerable Matches get { Debug.Assert(this.invocationTimestamp != default); - return this.matcherObserver.GetMatchesBetween(this.creationTimestamp, this.invocationTimestamp!.Value); + return this.matcherObserver.GetMatchesBetween(this.creationTimestamp, this.invocationTimestamp); } } From b14b04a1658b496f9c26927d8656edea459369a3 Mon Sep 17 00:00:00 2001 From: Andrew McClement Date: Wed, 28 May 2025 14:12:52 +0100 Subject: [PATCH 92/93] Dotnet format. --- src/Moq/AsInterface.cs | 2 +- src/Moq/Extensions.cs | 2 +- src/Moq/Match.cs | 2 +- src/Moq/Mock.cs | 2 +- src/Moq/StringBuilderExtensions.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Moq/AsInterface.cs b/src/Moq/AsInterface.cs index d58936758..7ba906e03 100644 --- a/src/Moq/AsInterface.cs +++ b/src/Moq/AsInterface.cs @@ -49,7 +49,7 @@ public override DefaultValueProvider DefaultValueProvider public override TInterface Object { - get { return (TInterface) this.owner.Object; } + get { return (TInterface)this.owner.Object; } } internal override SetupCollection MutableSetups => this.owner.MutableSetups; diff --git a/src/Moq/Extensions.cs b/src/Moq/Extensions.cs index d40e98b12..a33b61541 100644 --- a/src/Moq/Extensions.cs +++ b/src/Moq/Extensions.cs @@ -20,7 +20,7 @@ public static bool CanCreateInstance(this Type type) { return type.IsValueType || type.GetConstructor(Type.EmptyTypes) != null; } - + #if NULLABLE_REFERENCE_TYPES public static bool CanRead(this PropertyInfo property, [NotNullWhen(true)] out MethodInfo? getter) #else diff --git a/src/Moq/Match.cs b/src/Moq/Match.cs index b8d09475c..bcb089287 100644 --- a/src/Moq/Match.cs +++ b/src/Moq/Match.cs @@ -78,7 +78,7 @@ internal static TValue Matcher() bool IMatcher.Matches(object? argument, Type parameterType) => this.Matches(argument, parameterType); void IMatcher.SetupEvaluatedSuccessfully(object? value, Type parameterType) => this.SetupEvaluatedSuccessfully(value, parameterType); - + // TODO: Consider making the constructor set this to avoid the nullable reference warning. internal Expression RenderExpression { get; set; } diff --git a/src/Moq/Mock.cs b/src/Moq/Mock.cs index b9a4422e4..e4cea94a4 100644 --- a/src/Moq/Mock.cs +++ b/src/Moq/Mock.cs @@ -703,7 +703,7 @@ internal static Task RaiseEventAsync(Mock mock, Action action, object?[] a var expression = ExpressionReconstructor.Instance.ReconstructExpression(action, mock.ConstructorArguments); var parts = expression.Split(); - + // TODO: Will this code never return null? return (Task)Mock.RaiseEvent(mock, expression, parts, arguments); } diff --git a/src/Moq/StringBuilderExtensions.cs b/src/Moq/StringBuilderExtensions.cs index 064745d67..e07d25cd4 100644 --- a/src/Moq/StringBuilderExtensions.cs +++ b/src/Moq/StringBuilderExtensions.cs @@ -148,7 +148,7 @@ public static StringBuilder AppendValueOf(this StringBuilder stringBuilder, obje stringBuilder.AppendValueOf(enumerator.Current); } - + (enumerator as IDisposable)?.Dispose(); stringBuilder.Append(']'); } From 111b277d78016b05cdc0c535728924aa0c12d4b4 Mon Sep 17 00:00:00 2001 From: Andrew I McClement Date: Sun, 20 Jul 2025 17:07:22 +0100 Subject: [PATCH 93/93] Simplify StringBuilder.AppendValueOf handling of collections by testing for `IReadOnlyList`. This does handle more types than before (previously only T[] and List were handled) but I do not think the extension is problematic. This also avoids the theoretical issue of the enumerator not being disposed if an exception is thrown during iteration, since we are using the indexer directly. --- src/Moq/StringBuilderExtensions.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Moq/StringBuilderExtensions.cs b/src/Moq/StringBuilderExtensions.cs index e07d25cd4..939d0bd53 100644 --- a/src/Moq/StringBuilderExtensions.cs +++ b/src/Moq/StringBuilderExtensions.cs @@ -128,12 +128,11 @@ public static StringBuilder AppendValueOf(this StringBuilder stringBuilder, obje { stringBuilder.AppendNameOf(obj.GetType()).Append('.').Append(obj); } - else if (obj.GetType().IsArray || (obj.GetType().IsConstructedGenericType && obj.GetType().GetGenericTypeDefinition() == typeof(List<>))) + else if (obj is IReadOnlyList list) { stringBuilder.Append('['); const int maxCount = 10; - var enumerator = ((IEnumerable)obj).GetEnumerator(); - for (int i = 0; enumerator.MoveNext() && i < maxCount + 1; ++i) + for (var i = 0; i < list.Count; i++) { if (i > 0) { @@ -146,10 +145,9 @@ public static StringBuilder AppendValueOf(this StringBuilder stringBuilder, obje break; } - stringBuilder.AppendValueOf(enumerator.Current); + stringBuilder.AppendValueOf(list[i]); } - (enumerator as IDisposable)?.Dispose(); stringBuilder.Append(']'); } else