From 6fa75cd715b472023d7593082ba0da4e33827720 Mon Sep 17 00:00:00 2001 From: Alex Povar Date: Tue, 8 Dec 2020 18:59:50 +0100 Subject: [PATCH 1/3] Maintain project - Update .NET SDK on CI - Update Ruby on CI - Embed icon to the NuGet package - Trim trailing whitespace --- .editorconfig | 3 +++ .travis.yml | 4 ++-- appveyor.yml | 4 ++-- src/NSubstitute/NSubstitute.csproj | 7 +++++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.editorconfig b/.editorconfig index f48f9ef30..5da74dadc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,8 @@ root = true +[*] +trim_trailing_whitespace = true + [*.{cs,fs,fsx}] indent_size = 4 indent_style = space diff --git a/.travis.yml b/.travis.yml index 00d82dd56..7aadd0ebd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: csharp dist: xenial -dotnet: 2.1.502 +dotnet: 5.0.100 mono: none -env: CONFIGURATION=Release FRAMEWORK=netcoreapp2.1 +env: CONFIGURATION=Release FRAMEWORK=net5.0 before_script: - dotnet --info diff --git a/appveyor.yml b/appveyor.yml index 84cb9aa9f..4f039068e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,11 +1,11 @@ -os: Visual Studio 2017 +os: Visual Studio 2019 build: off environment: CONFIGURATION: Release install: - - set PATH=C:\Ruby22\bin;%PATH% + - set PATH=C:\Ruby25\bin;%PATH% - bundle install before_test: diff --git a/src/NSubstitute/NSubstitute.csproj b/src/NSubstitute/NSubstitute.csproj index f04868872..701da1250 100644 --- a/src/NSubstitute/NSubstitute.csproj +++ b/src/NSubstitute/NSubstitute.csproj @@ -3,14 +3,17 @@ NSubstitute is a friendly substitute for .NET mocking libraries. It has a simple, succinct syntax to help developers write clearer tests. NSubstitute is designed for Arrange-Act-Assert (AAA) testing and with Test Driven Development (TDD) in mind. 3.0.0 - Anthony Egerton;David Tchepak;Alexandr Nikitin;Alex Povar + Anthony Egerton;David Tchepak;Alexandr Nikitin;Oleksandr Povar NSubstitute NSubstitute mocking;mocks;testing;unit-testing;TDD;AAA - https://nsubstitute.github.io/images/nsubstitute-100x100.png + icon.png https://nsubstitute.github.io/ BSD-3-Clause + + + netstandard1.3;netstandard2.0;net45;net46 From a3ad944cd6aedfe437babbb30df566c6d0046989 Mon Sep 17 00:00:00 2001 From: Alex Povar Date: Tue, 8 Dec 2020 18:44:10 +0100 Subject: [PATCH 2/3] Add .NET 5 target framework --- CHANGELOG.md | 1 + Directory.Build.props | 5 +++-- src/NSubstitute/NSubstitute.csproj | 10 +++------- .../NSubstitute.Acceptance.Specs.csproj | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 510693ea5..0d5518173 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### unreleased +* [NEW] Add .NET 5 support ### 4.2.2 (Jun 2020) diff --git a/Directory.Build.props b/Directory.Build.props index 64206bf8f..ad3c08ee0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,11 +1,12 @@ - + true + true latest diff --git a/src/NSubstitute/NSubstitute.csproj b/src/NSubstitute/NSubstitute.csproj index 701da1250..1184b4f14 100644 --- a/src/NSubstitute/NSubstitute.csproj +++ b/src/NSubstitute/NSubstitute.csproj @@ -16,7 +16,7 @@ - netstandard1.3;netstandard2.0;net45;net46 + netstandard1.3;netstandard2.0;net45;net46;net5.0 @@ -35,7 +35,8 @@ - + @@ -43,11 +44,6 @@ - - - - - $(DefineConstants);SYSTEM_REFLECTION_CUSTOMATTRIBUTES_IS_ARRAY diff --git a/tests/NSubstitute.Acceptance.Specs/NSubstitute.Acceptance.Specs.csproj b/tests/NSubstitute.Acceptance.Specs/NSubstitute.Acceptance.Specs.csproj index 3f87626d6..6147cab8b 100644 --- a/tests/NSubstitute.Acceptance.Specs/NSubstitute.Acceptance.Specs.csproj +++ b/tests/NSubstitute.Acceptance.Specs/NSubstitute.Acceptance.Specs.csproj @@ -1,7 +1,7 @@  - netcoreapp2.0;netcoreapp1.1;net46;net45 + netcoreapp2.0;netcoreapp1.1;net46;net45;net5.0 From 4c488c3eb383c13570644ab53936137a374be4c0 Mon Sep 17 00:00:00 2001 From: Alex Povar Date: Tue, 8 Dec 2020 18:45:35 +0100 Subject: [PATCH 3/3] Arrange nullability attributes and adjust code --- src/NSubstitute/Arg.cs | 5 +- src/NSubstitute/Callback.cs | 9 +- .../Callbacks/ConfiguredCallback.cs | 3 + src/NSubstitute/Compatibility/CompatArg.cs | 3 + .../DiagnosticsNullabilityAttributes.cs | 142 ++++++++++++++++++ src/NSubstitute/Core/Argument.cs | 20 +-- .../Core/Arguments/AnyArgumentMatcher.cs | 2 +- .../Core/Arguments/ArgumentFormatter.cs | 8 +- .../Core/Arguments/ArgumentMatchInfo.cs | 10 +- .../Core/Arguments/ArgumentMatcher.cs | 15 +- .../Core/Arguments/ArgumentSpecification.cs | 22 +-- ...rgumentSpecificationCompatibilityTester.cs | 4 +- .../Arguments/ArgumentSpecificationFactory.cs | 10 +- .../ArgumentSpecificationsFactory.cs | 2 +- .../Arguments/ArrayContentsArgumentMatcher.cs | 7 +- .../Core/Arguments/DefaultChecker.cs | 2 +- .../Core/Arguments/EqualsArgumentMatcher.cs | 6 +- .../Arguments/ExpressionArgumentMatcher.cs | 4 +- .../Core/Arguments/IArgumentFormatter.cs | 2 +- .../Core/Arguments/IArgumentMatcher.cs | 4 +- .../Core/Arguments/IArgumentSpecification.cs | 8 +- ...rgumentSpecificationCompatibilityTester.cs | 2 +- .../IArgumentSpecificationFactory.cs | 2 +- .../IArgumentSpecificationsFactory.cs | 2 +- .../Core/Arguments/IDefaultChecker.cs | 2 +- .../ISuppliedArgumentSpecifications.cs | 4 +- .../SuppliedArgumentSpecifications.cs | 4 +- src/NSubstitute/Core/Call.cs | 26 ++-- src/NSubstitute/Core/CallActions.cs | 2 +- src/NSubstitute/Core/CallCollection.cs | 9 +- src/NSubstitute/Core/CallFactory.cs | 4 +- src/NSubstitute/Core/CallInfo.cs | 16 +- src/NSubstitute/Core/CallResults.cs | 4 +- src/NSubstitute/Core/CallRouter.cs | 4 +- src/NSubstitute/Core/CallSpecification.cs | 6 +- src/NSubstitute/Core/DefaultForType.cs | 4 +- .../DependencyInjection/INSubContainer.cs | 15 +- .../Core/DependencyInjection/NSubContainer.cs | 23 +-- src/NSubstitute/Core/EventCallFormatter.cs | 4 +- .../Core/Events/DelegateEventWrapper.cs | 17 ++- .../Core/Events/EventHandlerWrapper.cs | 11 +- .../Core/Events/RaiseEventWrapper.cs | 4 +- src/NSubstitute/Core/Extensions.cs | 11 +- src/NSubstitute/Core/ICall.cs | 6 +- src/NSubstitute/Core/ICallFactory.cs | 4 +- src/NSubstitute/Core/ICallResults.cs | 2 +- src/NSubstitute/Core/ICallRouter.cs | 2 +- src/NSubstitute/Core/IDefaultForType.cs | 2 +- src/NSubstitute/Core/IDescribeNonMatches.cs | 2 +- src/NSubstitute/Core/IPendingSpecification.cs | 2 +- src/NSubstitute/Core/IProxyFactory.cs | 2 +- src/NSubstitute/Core/IResultsForType.cs | 2 +- src/NSubstitute/Core/IReturn.cs | 61 ++++---- src/NSubstitute/Core/ISubstitutionContext.cs | 4 +- src/NSubstitute/Core/IThreadLocalContext.cs | 6 +- src/NSubstitute/Core/Maybe.cs | 5 +- .../Core/PendingSpecificationInfo.cs | 14 +- src/NSubstitute/Core/PropertyCallFormatter.cs | 4 +- src/NSubstitute/Core/PropertyHelper.cs | 12 +- src/NSubstitute/Core/Query.cs | 4 +- src/NSubstitute/Core/ReflectionExtensions.cs | 10 +- src/NSubstitute/Core/ResultsForType.cs | 2 +- src/NSubstitute/Core/ReturnObservable.cs | 11 +- src/NSubstitute/Core/RobustThreadLocal.cs | 11 +- src/NSubstitute/Core/RouteAction.cs | 8 +- .../Core/RouteFactoryCacheWrapper.cs | 2 +- .../Core/SequenceChecking/InstanceTracker.cs | 2 +- .../SequenceChecking/SequenceFormatter.cs | 42 +++--- src/NSubstitute/Core/SubstituteFactory.cs | 6 +- src/NSubstitute/Core/SubstitutionContext.cs | 10 +- src/NSubstitute/Core/ThreadLocalContext.cs | 34 ++--- src/NSubstitute/Core/WhenCalled.cs | 2 +- .../Exceptions/AmbiguousArgumentsException.cs | 20 +-- .../Exceptions/CouldNotSetReturnException.cs | 10 +- .../NullSubstituteReferenceException.cs | 5 +- .../Exceptions/SubstituteException.cs | 2 +- .../Exceptions/SubstituteInternalException.cs | 2 +- src/NSubstitute/Extensions/ClearExtensions.cs | 8 +- .../Extensions/ConfigurationExtensions.cs | 11 +- .../Extensions/ExceptionExtensions.cs | 7 +- .../Extensions/ReceivedExtensions.cs | 8 + .../Extensions/ReturnsExtensions.cs | 3 + .../Extensions/ReturnsForAllExtensions.cs | 12 +- src/NSubstitute/NSubstitute.csproj | 15 +- .../CastleDynamicProxyFactory.cs | 21 +-- .../CastleInvocationMapper.cs | 2 +- .../CastleDynamicProxy/ProxyIdInterceptor.cs | 2 +- .../DelegateProxy/DelegateProxyFactory.cs | 2 +- src/NSubstitute/Proxies/ProxyFactory.cs | 2 +- src/NSubstitute/Raise.cs | 3 + src/NSubstitute/Received.cs | 3 + .../Routing/AutoValues/AutoArrayProvider.cs | 4 +- .../AutoValues/AutoObservableProvider.cs | 6 +- .../AutoValues/AutoQueryableProvider.cs | 2 +- .../Routing/AutoValues/AutoTaskProvider.cs | 8 +- .../AutoValues/AutoValueProvidersFactory.cs | 2 +- .../Routing/AutoValues/IAutoValueProvider.cs | 2 +- .../Handlers/EventSubscriptionHandler.cs | 4 +- .../Routing/Handlers/RaiseEventHandler.cs | 10 +- .../Routing/Handlers/ReturnAutoValue.cs | 2 +- .../ReturnFromAndConfigureDynamicCall.cs | 8 +- src/NSubstitute/Routing/IRoute.cs | 2 +- src/NSubstitute/Routing/IRouteFactory.cs | 2 +- src/NSubstitute/Routing/Route.cs | 2 +- src/NSubstitute/Routing/RouteFactory.cs | 2 +- src/NSubstitute/Substitute.cs | 3 + src/NSubstitute/SubstituteExtensions.cs | 37 ++++- 107 files changed, 622 insertions(+), 350 deletions(-) create mode 100644 src/NSubstitute/Compatibility/DiagnosticsNullabilityAttributes.cs diff --git a/src/NSubstitute/Arg.cs b/src/NSubstitute/Arg.cs index 319a6b1f4..3a41a4a70 100644 --- a/src/NSubstitute/Arg.cs +++ b/src/NSubstitute/Arg.cs @@ -2,6 +2,9 @@ using System.Linq.Expressions; using NSubstitute.Core.Arguments; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute { /// @@ -89,7 +92,7 @@ public static ref TDelegate InvokeDelegate(params object[] arguments) /// public static ref T Do(Action useArgument) { - return ref ArgumentMatcher.Enqueue(new AnyArgumentMatcher(typeof(T)), x => useArgument((T) x)); + return ref ArgumentMatcher.Enqueue(new AnyArgumentMatcher(typeof(T)), x => useArgument((T) x!)); } /// diff --git a/src/NSubstitute/Callback.cs b/src/NSubstitute/Callback.cs index 840f15837..25ce38556 100644 --- a/src/NSubstitute/Callback.cs +++ b/src/NSubstitute/Callback.cs @@ -3,6 +3,9 @@ using NSubstitute.Callbacks; using NSubstitute.Core; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute { /// @@ -72,7 +75,8 @@ public static Callback AlwaysThrow(TException exception) where TExce return AlwaysThrow(info => exception); } - protected static Action ToCallback(Func throwThis) where TException : Exception + protected static Action ToCallback(Func throwThis) + where TException : notnull, Exception { return ci => { if (throwThis != null) throw throwThis(ci); }; } @@ -111,8 +115,7 @@ public void Call(CallInfo callInfo) private void CallFromStack(CallInfo callInfo) { - Action callback; - if (callbackQueue.TryDequeue(out callback)) + if (callbackQueue.TryDequeue(out var callback)) { callback(callInfo); } diff --git a/src/NSubstitute/Callbacks/ConfiguredCallback.cs b/src/NSubstitute/Callbacks/ConfiguredCallback.cs index 7d9c4f908..8e03ceeb4 100644 --- a/src/NSubstitute/Callbacks/ConfiguredCallback.cs +++ b/src/NSubstitute/Callbacks/ConfiguredCallback.cs @@ -1,6 +1,9 @@ using System; using NSubstitute.Core; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute.Callbacks { public class ConfiguredCallback : EndCallbackChain diff --git a/src/NSubstitute/Compatibility/CompatArg.cs b/src/NSubstitute/Compatibility/CompatArg.cs index 254e01d07..f3a9624d1 100644 --- a/src/NSubstitute/Compatibility/CompatArg.cs +++ b/src/NSubstitute/Compatibility/CompatArg.cs @@ -1,6 +1,9 @@ using System; using System.Linq.Expressions; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute.Compatibility { /// diff --git a/src/NSubstitute/Compatibility/DiagnosticsNullabilityAttributes.cs b/src/NSubstitute/Compatibility/DiagnosticsNullabilityAttributes.cs new file mode 100644 index 000000000..fbe8ff36a --- /dev/null +++ b/src/NSubstitute/Compatibility/DiagnosticsNullabilityAttributes.cs @@ -0,0 +1,142 @@ +#if !SYSTEM_DIAGNOSTICS_CODEANALYSIS_NULLABILITY + +// This was copied from https://github.com/dotnet/runtime/blob/39b9607807f29e48cae4652cd74735182b31182e/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs +// and updated to have the scope of the attributes be internal. +namespace System.Diagnostics.CodeAnalysis +{ + + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute { } + + /// Specifies that an output will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } +} +#endif diff --git a/src/NSubstitute/Core/Argument.cs b/src/NSubstitute/Core/Argument.cs index 4a4c74ea6..1f06a491e 100644 --- a/src/NSubstitute/Core/Argument.cs +++ b/src/NSubstitute/Core/Argument.cs @@ -5,15 +5,15 @@ namespace NSubstitute.Core { public class Argument { - private readonly ICall _call; + private readonly ICall? _call; private readonly int _argIndex; - private readonly Type _declaredType; - private readonly Func _getValue; - private readonly Action _setValue; + private readonly Type? _declaredType; + private readonly Func? _getValue; + private readonly Action? _setValue; [Obsolete("This constructor overload is deprecated and will be removed in the next version.")] - public Argument(Type declaredType, Func getValue, Action setValue) + public Argument(Type declaredType, Func getValue, Action setValue) { _declaredType = declaredType; _getValue = getValue; @@ -26,9 +26,9 @@ public Argument(ICall call, int argIndex) _argIndex = argIndex; } - public object Value + public object? Value { - get => _getValue != null ? _getValue() : _call.GetArguments()[_argIndex]; + get => _getValue != null ? _getValue() : _call!.GetArguments()[_argIndex]; set { if (_setValue != null) @@ -37,14 +37,14 @@ public object Value } else { - _call.GetArguments()[_argIndex] = value; + _call!.GetArguments()[_argIndex] = value; } } } public bool IsByRef => DeclaredType.IsByRef; - public Type DeclaredType => _declaredType ?? _call.GetParameterInfos()[_argIndex].ParameterType; + public Type DeclaredType => _declaredType ?? _call!.GetParameterInfos()[_argIndex].ParameterType; public Type ActualType => Value == null ? DeclaredType : Value.GetType(); @@ -65,7 +65,7 @@ public bool CanSetValueWithInstanceOf(Type type) private static Type AsNonByRefType(Type type) { - return type.IsByRef ? type.GetElementType() : type; + return type.IsByRef ? type.GetElementType()! : type; } } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/AnyArgumentMatcher.cs b/src/NSubstitute/Core/Arguments/AnyArgumentMatcher.cs index cb449e21d..8e547a15a 100644 --- a/src/NSubstitute/Core/Arguments/AnyArgumentMatcher.cs +++ b/src/NSubstitute/Core/Arguments/AnyArgumentMatcher.cs @@ -13,6 +13,6 @@ public AnyArgumentMatcher(Type typeArgMustBeCompatibleWith) public override string ToString() => "any " + _typeArgMustBeCompatibleWith.GetNonMangledTypeName(); - public bool IsSatisfiedBy(object argument) => argument.IsCompatibleWith(_typeArgMustBeCompatibleWith); + public bool IsSatisfiedBy(object? argument) => argument.IsCompatibleWith(_typeArgMustBeCompatibleWith); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/ArgumentFormatter.cs b/src/NSubstitute/Core/Arguments/ArgumentFormatter.cs index 20207abf5..d0f4e2464 100644 --- a/src/NSubstitute/Core/Arguments/ArgumentFormatter.cs +++ b/src/NSubstitute/Core/Arguments/ArgumentFormatter.cs @@ -8,13 +8,13 @@ public class ArgumentFormatter : IArgumentFormatter { internal static IArgumentFormatter Default { get; } = new ArgumentFormatter(); - public string Format(object argument, bool highlight) + public string Format(object? argument, bool highlight) { var formatted = Format(argument); return highlight ? "*" + formatted + "*" : formatted; } - private string Format(object arg) + private string Format(object? arg) { switch (arg) { @@ -24,11 +24,11 @@ private string Format(object arg) case string str: return string.Format(CultureInfo.InvariantCulture, "\"{0}\"", str); - case object obj when obj.GetType().GetMethod(nameof(ToString), Type.EmptyTypes).DeclaringType == typeof(object): + case object obj when obj.GetType().GetMethod(nameof(ToString), Type.EmptyTypes)!.DeclaringType == typeof(object): return arg.GetType().GetNonMangledTypeName(); default: - return arg.ToString(); + return arg.ToString() ?? string.Empty; } } } diff --git a/src/NSubstitute/Core/Arguments/ArgumentMatchInfo.cs b/src/NSubstitute/Core/Arguments/ArgumentMatchInfo.cs index 4ee137dd8..d43b6f806 100644 --- a/src/NSubstitute/Core/Arguments/ArgumentMatchInfo.cs +++ b/src/NSubstitute/Core/Arguments/ArgumentMatchInfo.cs @@ -2,14 +2,14 @@ { public class ArgumentMatchInfo { - public ArgumentMatchInfo(int index, object argument, IArgumentSpecification specification) + public ArgumentMatchInfo(int index, object? argument, IArgumentSpecification specification) { Index = index; _argument = argument; _specification = specification; } - private readonly object _argument; + private readonly object? _argument; private readonly IArgumentSpecification _specification; public int Index { get; private set; } @@ -23,14 +23,14 @@ public string DescribeNonMatch() return string.Format("{0}{1}", argIndexPrefix, describeNonMatch.Replace("\n", "\n".PadRight(argIndexPrefix.Length + 1))); } - public bool Equals(ArgumentMatchInfo other) + public bool Equals(ArgumentMatchInfo? other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return other.Index == Index && Equals(other._argument, _argument) && Equals(other._specification, _specification); } - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; @@ -44,7 +44,7 @@ public override int GetHashCode() { int result = Index; result = (result*397) ^ (_argument != null ? _argument.GetHashCode() : 0); - result = (result*397) ^ (_specification != null ? _specification.GetHashCode() : 0); + result = (result*397) ^ _specification.GetHashCode(); return result; } } diff --git a/src/NSubstitute/Core/Arguments/ArgumentMatcher.cs b/src/NSubstitute/Core/Arguments/ArgumentMatcher.cs index 3c7f0c728..c7d45e454 100644 --- a/src/NSubstitute/Core/Arguments/ArgumentMatcher.cs +++ b/src/NSubstitute/Core/Arguments/ArgumentMatcher.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace NSubstitute.Core.Arguments { @@ -8,7 +9,7 @@ public static class ArgumentMatcher /// Enqueues a matcher for the method argument in current position and returns the value which should be /// passed back to the method you invoke. /// - public static ref T Enqueue(IArgumentMatcher argumentMatcher) + public static ref T? Enqueue(IArgumentMatcher argumentMatcher) { if (argumentMatcher == null) throw new ArgumentNullException(nameof(argumentMatcher)); @@ -25,17 +26,17 @@ public static ref T Enqueue(IArgumentMatcher argumentMatcher) return ref EnqueueArgSpecification(new ArgumentSpecification(typeof(T), nonGenericMatcher)); } - internal static ref T Enqueue(IArgumentMatcher argumentMatcher) + internal static ref T? Enqueue(IArgumentMatcher argumentMatcher) { return ref EnqueueArgSpecification(new ArgumentSpecification(typeof(T), argumentMatcher)); } - internal static ref T Enqueue(IArgumentMatcher argumentMatcher, Action action) + internal static ref T? Enqueue(IArgumentMatcher argumentMatcher, Action action) { return ref EnqueueArgSpecification(new ArgumentSpecification(typeof(T), argumentMatcher, action)); } - private static ref T EnqueueArgSpecification(IArgumentSpecification specification) + private static ref T? EnqueueArgSpecification(IArgumentSpecification specification) { SubstitutionContext.Current.ThreadContext.EnqueueArgumentSpecification(specification); return ref new DefaultValueContainer().Value; @@ -50,7 +51,7 @@ public GenericToNonGenericMatcherProxy(IArgumentMatcher matcher) _matcher = matcher; } - public bool IsSatisfiedBy(object argument) => _matcher.IsSatisfiedBy((T) argument); + public bool IsSatisfiedBy(object? argument) => _matcher.IsSatisfiedBy((T) argument!); } private class GenericToNonGenericMatcherProxyWithDescribe : GenericToNonGenericMatcherProxy, IDescribeNonMatches @@ -60,12 +61,12 @@ public GenericToNonGenericMatcherProxyWithDescribe(IArgumentMatcher matcher) if (matcher as IDescribeNonMatches == null) throw new ArgumentException("Should implement IDescribeNonMatches type."); } - public string DescribeFor(object argument) => ((IDescribeNonMatches) _matcher).DescribeFor(argument); + public string DescribeFor(object? argument) => ((IDescribeNonMatches) _matcher).DescribeFor(argument); } private class DefaultValueContainer { - public T Value; + public T? Value; } } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/ArgumentSpecification.cs b/src/NSubstitute/Core/Arguments/ArgumentSpecification.cs index 04a88eb44..7e56f8fa5 100644 --- a/src/NSubstitute/Core/Arguments/ArgumentSpecification.cs +++ b/src/NSubstitute/Core/Arguments/ArgumentSpecification.cs @@ -4,30 +4,30 @@ namespace NSubstitute.Core.Arguments { public class ArgumentSpecification : IArgumentSpecification { - private static readonly Action NoOpAction = x => { }; + private static readonly Action NoOpAction = x => { }; private readonly IArgumentMatcher _matcher; - private readonly Action _action; + private readonly Action _action; public Type ForType { get; } public bool HasAction => _action != NoOpAction; public ArgumentSpecification(Type forType, IArgumentMatcher matcher) : this(forType, matcher, NoOpAction) { } - public ArgumentSpecification(Type forType, IArgumentMatcher matcher, Action action) + public ArgumentSpecification(Type forType, IArgumentMatcher matcher, Action action) { ForType = forType; _matcher = matcher; _action = action; } - public bool IsSatisfiedBy(object argument) + public bool IsSatisfiedBy(object? argument) { if (!IsCompatibleWith(argument)) return false; try { return _matcher.IsSatisfiedBy(argument); } catch { return false; } } - public string DescribeNonMatch(object argument) + public string DescribeNonMatch(object? argument) { var describable = _matcher as IDescribeNonMatches; if (describable == null) return string.Empty; @@ -35,7 +35,7 @@ public string DescribeNonMatch(object argument) return IsCompatibleWith(argument) ? describable.DescribeFor(argument) : GetIncompatibleTypeMessage(argument); } - public string FormatArgument(object argument) + public string FormatArgument(object? argument) { var isSatisfiedByArg = IsSatisfiedBy(argument); var matcherFormatter = _matcher as IArgumentFormatter; @@ -44,7 +44,7 @@ public string FormatArgument(object argument) : matcherFormatter.Format(argument, isSatisfiedByArg); } - public override string ToString() => _matcher.ToString(); + public override string ToString() => _matcher.ToString() ?? string.Empty; public IArgumentSpecification CreateCopyMatchingAnyArgOfType(Type requiredType) { @@ -56,23 +56,23 @@ public IArgumentSpecification CreateCopyMatchingAnyArgOfType(Type requiredType) _action == NoOpAction ? NoOpAction : RunActionIfTypeIsCompatible); } - public void RunAction(object argument) + public void RunAction(object? argument) { _action(argument); } - private void RunActionIfTypeIsCompatible(object argument) + private void RunActionIfTypeIsCompatible(object? argument) { if (!argument.IsCompatibleWith(ForType)) return; _action(argument); } - private bool IsCompatibleWith(object argument) + private bool IsCompatibleWith(object? argument) { return argument.IsCompatibleWith(ForType); } - private string GetIncompatibleTypeMessage(object argument) + private string GetIncompatibleTypeMessage(object? argument) { var argumentType = argument == null ? typeof(object) : argument.GetType(); return string.Format("Expected an argument compatible with type {0}. Actual type was {1}.", ForType, argumentType); diff --git a/src/NSubstitute/Core/Arguments/ArgumentSpecificationCompatibilityTester.cs b/src/NSubstitute/Core/Arguments/ArgumentSpecificationCompatibilityTester.cs index 05f0963c2..334b39902 100644 --- a/src/NSubstitute/Core/Arguments/ArgumentSpecificationCompatibilityTester.cs +++ b/src/NSubstitute/Core/Arguments/ArgumentSpecificationCompatibilityTester.cs @@ -12,14 +12,14 @@ public ArgumentSpecificationCompatibilityTester(IDefaultChecker defaultChecker) _defaultChecker = defaultChecker ?? throw new ArgumentNullException(nameof(defaultChecker)); } - public bool IsSpecificationCompatible(IArgumentSpecification specification, object argumentValue, Type argumentType) + public bool IsSpecificationCompatible(IArgumentSpecification specification, object? argumentValue, Type argumentType) { var typeArgSpecIsFor = specification.ForType; return AreTypesCompatible(argumentType, typeArgSpecIsFor) && IsProvidedArgumentTheOneWeWouldGetUsingAnArgSpecForThisType(argumentValue, typeArgSpecIsFor); } - private bool IsProvidedArgumentTheOneWeWouldGetUsingAnArgSpecForThisType(object argument, Type typeArgSpecIsFor) + private bool IsProvidedArgumentTheOneWeWouldGetUsingAnArgSpecForThisType(object? argument, Type typeArgSpecIsFor) { return _defaultChecker.IsDefault(argument, typeArgSpecIsFor); } diff --git a/src/NSubstitute/Core/Arguments/ArgumentSpecificationFactory.cs b/src/NSubstitute/Core/Arguments/ArgumentSpecificationFactory.cs index 144d87fc3..157cdf556 100644 --- a/src/NSubstitute/Core/Arguments/ArgumentSpecificationFactory.cs +++ b/src/NSubstitute/Core/Arguments/ArgumentSpecificationFactory.cs @@ -8,7 +8,7 @@ namespace NSubstitute.Core.Arguments { public class ArgumentSpecificationFactory : IArgumentSpecificationFactory { - public IArgumentSpecification Create(object argument, IParameterInfo parameterInfo, + public IArgumentSpecification Create(object? argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications) { return parameterInfo.IsParams @@ -16,7 +16,7 @@ public IArgumentSpecification Create(object argument, IParameterInfo parameterIn : CreateSpecFromNonParamsArg(argument, parameterInfo, suppliedArgumentSpecifications); } - private IArgumentSpecification CreateSpecFromNonParamsArg(object argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications) + private IArgumentSpecification CreateSpecFromNonParamsArg(object? argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications) { if (suppliedArgumentSpecifications.IsNextFor(argument, parameterInfo.ParameterType)) { @@ -32,7 +32,7 @@ private IArgumentSpecification CreateSpecFromNonParamsArg(object argument, IPara throw new AmbiguousArgumentsException(); } - private IArgumentSpecification CreateSpecFromParamsArg(object argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications) + private IArgumentSpecification CreateSpecFromParamsArg(object? argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications) { // Next specification is for the whole params array. if (suppliedArgumentSpecifications.IsNextFor(argument, parameterInfo.ParameterType)) @@ -60,11 +60,11 @@ private IArgumentSpecification CreateSpecFromParamsArg(object argument, IParamet throw new SubstituteInternalException($"Expected to get array argument, but got argument of '{argument.GetType().FullName}' type."); } - var arrayArgumentSpecifications = UnwrapParamsArguments(arrayArg.Cast(), parameterInfo.ParameterType.GetElementType(), suppliedArgumentSpecifications); + var arrayArgumentSpecifications = UnwrapParamsArguments(arrayArg.Cast(), parameterInfo.ParameterType.GetElementType()!, suppliedArgumentSpecifications); return new ArgumentSpecification(parameterInfo.ParameterType, new ArrayContentsArgumentMatcher(arrayArgumentSpecifications)); } - private IEnumerable UnwrapParamsArguments(IEnumerable args, Type paramsElementType, ISuppliedArgumentSpecifications suppliedArgumentSpecifications) + private IEnumerable UnwrapParamsArguments(IEnumerable args, Type paramsElementType, ISuppliedArgumentSpecifications suppliedArgumentSpecifications) { var fakeParameterInfo = new ParameterInfoFromType(paramsElementType); var result = new List(); diff --git a/src/NSubstitute/Core/Arguments/ArgumentSpecificationsFactory.cs b/src/NSubstitute/Core/Arguments/ArgumentSpecificationsFactory.cs index aa84271c9..3ad98ead3 100644 --- a/src/NSubstitute/Core/Arguments/ArgumentSpecificationsFactory.cs +++ b/src/NSubstitute/Core/Arguments/ArgumentSpecificationsFactory.cs @@ -16,7 +16,7 @@ public ArgumentSpecificationsFactory(IArgumentSpecificationFactory argumentSpeci _suppliedArgumentSpecificationsFactory = suppliedArgumentSpecificationsFactory; } - public IEnumerable Create(IList argumentSpecs, object[] arguments, IParameterInfo[] parameterInfos, MethodInfo methodInfo, MatchArgs matchArgs) + public IEnumerable Create(IList argumentSpecs, object?[] arguments, IParameterInfo[] parameterInfos, MethodInfo methodInfo, MatchArgs matchArgs) { var suppliedArgumentSpecifications = _suppliedArgumentSpecificationsFactory.Create(argumentSpecs); diff --git a/src/NSubstitute/Core/Arguments/ArrayContentsArgumentMatcher.cs b/src/NSubstitute/Core/Arguments/ArrayContentsArgumentMatcher.cs index 718abf911..dbf17c5a1 100644 --- a/src/NSubstitute/Core/Arguments/ArrayContentsArgumentMatcher.cs +++ b/src/NSubstitute/Core/Arguments/ArrayContentsArgumentMatcher.cs @@ -13,7 +13,7 @@ public ArrayContentsArgumentMatcher(IEnumerable argument _argumentSpecifications = argumentSpecifications.ToArray(); } - public bool IsSatisfiedBy(object argument) + public bool IsSatisfiedBy(object? argument) { if (argument != null) { @@ -33,10 +33,9 @@ public override string ToString() return string.Join(", ", _argumentSpecifications.Select(x => x.ToString())); } - public string Format(object argument, bool highlight) + public string Format(object? argument, bool highlight) { - var enumerableArgs = argument as IEnumerable; - var argArray = enumerableArgs != null ? enumerableArgs.Cast().ToArray() : new object[0]; + var argArray = argument is IEnumerable enumerableArgs ? enumerableArgs.Cast().ToArray() : new object[0]; return Format(argArray, _argumentSpecifications).Join(", "); } diff --git a/src/NSubstitute/Core/Arguments/DefaultChecker.cs b/src/NSubstitute/Core/Arguments/DefaultChecker.cs index 3cd5f6ab6..05c6c77ba 100644 --- a/src/NSubstitute/Core/Arguments/DefaultChecker.cs +++ b/src/NSubstitute/Core/Arguments/DefaultChecker.cs @@ -12,7 +12,7 @@ public DefaultChecker(IDefaultForType defaultForType) _defaultForType = defaultForType; } - public bool IsDefault(object value, Type forType) + public bool IsDefault(object? value, Type forType) { return EqualityComparer.Default.Equals(value, _defaultForType.GetDefaultFor(forType)); } diff --git a/src/NSubstitute/Core/Arguments/EqualsArgumentMatcher.cs b/src/NSubstitute/Core/Arguments/EqualsArgumentMatcher.cs index 4ea68292d..9f55272a3 100644 --- a/src/NSubstitute/Core/Arguments/EqualsArgumentMatcher.cs +++ b/src/NSubstitute/Core/Arguments/EqualsArgumentMatcher.cs @@ -4,15 +4,15 @@ namespace NSubstitute.Core.Arguments { public class EqualsArgumentMatcher : IArgumentMatcher { - private readonly object _value; + private readonly object? _value; - public EqualsArgumentMatcher(object value) + public EqualsArgumentMatcher(object? value) { _value = value; } public override string ToString() => ArgumentFormatter.Default.Format(_value, false); - public bool IsSatisfiedBy(object argument) => EqualityComparer.Default.Equals(_value, argument); + public bool IsSatisfiedBy(object? argument) => EqualityComparer.Default.Equals(_value, argument); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/ExpressionArgumentMatcher.cs b/src/NSubstitute/Core/Arguments/ExpressionArgumentMatcher.cs index 12a5f4ba0..6b4274ea0 100644 --- a/src/NSubstitute/Core/Arguments/ExpressionArgumentMatcher.cs +++ b/src/NSubstitute/Core/Arguments/ExpressionArgumentMatcher.cs @@ -14,9 +14,9 @@ public ExpressionArgumentMatcher(Expression> predicate) _predicateDescription = predicate.ToString(); } - public bool IsSatisfiedBy(object argument) + public bool IsSatisfiedBy(object? argument) { - return _predicate((T)argument); + return _predicate((T)argument!); } public override string ToString() { return _predicateDescription; } diff --git a/src/NSubstitute/Core/Arguments/IArgumentFormatter.cs b/src/NSubstitute/Core/Arguments/IArgumentFormatter.cs index 2f682ecc3..47f1e6d40 100644 --- a/src/NSubstitute/Core/Arguments/IArgumentFormatter.cs +++ b/src/NSubstitute/Core/Arguments/IArgumentFormatter.cs @@ -2,6 +2,6 @@ namespace NSubstitute.Core.Arguments { public interface IArgumentFormatter { - string Format(object arg, bool highlight); + string Format(object? arg, bool highlight); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/IArgumentMatcher.cs b/src/NSubstitute/Core/Arguments/IArgumentMatcher.cs index 693949fa7..01d5c9ea2 100644 --- a/src/NSubstitute/Core/Arguments/IArgumentMatcher.cs +++ b/src/NSubstitute/Core/Arguments/IArgumentMatcher.cs @@ -10,7 +10,7 @@ public interface IArgumentMatcher /// Checks whether the satisfies the condition of the matcher. /// If this throws an exception the argument will be treated as non-matching. /// - bool IsSatisfiedBy(object argument); + bool IsSatisfiedBy(object? argument); } /// @@ -24,6 +24,6 @@ public interface IArgumentMatcher /// Checks whether the satisfies the condition of the matcher. /// If this throws an exception the argument will be treated as non-matching. /// - bool IsSatisfiedBy(T argument); + bool IsSatisfiedBy(T? argument); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/IArgumentSpecification.cs b/src/NSubstitute/Core/Arguments/IArgumentSpecification.cs index 154bfd4c9..dffa0a59d 100644 --- a/src/NSubstitute/Core/Arguments/IArgumentSpecification.cs +++ b/src/NSubstitute/Core/Arguments/IArgumentSpecification.cs @@ -4,12 +4,12 @@ namespace NSubstitute.Core.Arguments { public interface IArgumentSpecification { - bool IsSatisfiedBy(object argument); + bool IsSatisfiedBy(object? argument); Type ForType { get; } IArgumentSpecification CreateCopyMatchingAnyArgOfType(Type requiredType); bool HasAction { get; } - void RunAction(object argument); - string DescribeNonMatch(object argument); - string FormatArgument(object argument); + void RunAction(object? argument); + string DescribeNonMatch(object? argument); + string FormatArgument(object? argument); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/IArgumentSpecificationCompatibilityTester.cs b/src/NSubstitute/Core/Arguments/IArgumentSpecificationCompatibilityTester.cs index e4d0f5010..cf4f517cf 100644 --- a/src/NSubstitute/Core/Arguments/IArgumentSpecificationCompatibilityTester.cs +++ b/src/NSubstitute/Core/Arguments/IArgumentSpecificationCompatibilityTester.cs @@ -4,6 +4,6 @@ namespace NSubstitute.Core.Arguments { public interface IArgumentSpecificationCompatibilityTester { - bool IsSpecificationCompatible(IArgumentSpecification specification, object argumentValue, Type argumentType); + bool IsSpecificationCompatible(IArgumentSpecification specification, object? argumentValue, Type argumentType); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/IArgumentSpecificationFactory.cs b/src/NSubstitute/Core/Arguments/IArgumentSpecificationFactory.cs index 05ecd958c..9800095f9 100644 --- a/src/NSubstitute/Core/Arguments/IArgumentSpecificationFactory.cs +++ b/src/NSubstitute/Core/Arguments/IArgumentSpecificationFactory.cs @@ -2,6 +2,6 @@ namespace NSubstitute.Core.Arguments { public interface IArgumentSpecificationFactory { - IArgumentSpecification Create(object argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications); + IArgumentSpecification Create(object? argument, IParameterInfo parameterInfo, ISuppliedArgumentSpecifications suppliedArgumentSpecifications); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/IArgumentSpecificationsFactory.cs b/src/NSubstitute/Core/Arguments/IArgumentSpecificationsFactory.cs index b7998d058..b846ad875 100644 --- a/src/NSubstitute/Core/Arguments/IArgumentSpecificationsFactory.cs +++ b/src/NSubstitute/Core/Arguments/IArgumentSpecificationsFactory.cs @@ -5,6 +5,6 @@ namespace NSubstitute.Core.Arguments { public interface IArgumentSpecificationsFactory { - IEnumerable Create(IList argumentSpecs, object[] arguments, IParameterInfo[] parameterInfos, MethodInfo methodInfo, MatchArgs matchArgs); + IEnumerable Create(IList argumentSpecs, object?[] arguments, IParameterInfo[] parameterInfos, MethodInfo methodInfo, MatchArgs matchArgs); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/IDefaultChecker.cs b/src/NSubstitute/Core/Arguments/IDefaultChecker.cs index 665073a91..b250d6e80 100644 --- a/src/NSubstitute/Core/Arguments/IDefaultChecker.cs +++ b/src/NSubstitute/Core/Arguments/IDefaultChecker.cs @@ -4,6 +4,6 @@ namespace NSubstitute.Core.Arguments { public interface IDefaultChecker { - bool IsDefault(object value, Type forType); + bool IsDefault(object? value, Type forType); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/Arguments/ISuppliedArgumentSpecifications.cs b/src/NSubstitute/Core/Arguments/ISuppliedArgumentSpecifications.cs index b0ed08939..56e510962 100644 --- a/src/NSubstitute/Core/Arguments/ISuppliedArgumentSpecifications.cs +++ b/src/NSubstitute/Core/Arguments/ISuppliedArgumentSpecifications.cs @@ -5,8 +5,8 @@ namespace NSubstitute.Core.Arguments { public interface ISuppliedArgumentSpecifications { - bool AnyFor(object argument, Type argumentType); - bool IsNextFor(object argument, Type argumentType); + bool AnyFor(object? argument, Type argumentType); + bool IsNextFor(object? argument, Type argumentType); IArgumentSpecification Dequeue(); IEnumerable DequeueRemaining(); } diff --git a/src/NSubstitute/Core/Arguments/SuppliedArgumentSpecifications.cs b/src/NSubstitute/Core/Arguments/SuppliedArgumentSpecifications.cs index 53df85e12..69f3f917e 100644 --- a/src/NSubstitute/Core/Arguments/SuppliedArgumentSpecifications.cs +++ b/src/NSubstitute/Core/Arguments/SuppliedArgumentSpecifications.cs @@ -18,12 +18,12 @@ public SuppliedArgumentSpecifications(IArgumentSpecificationCompatibilityTester } - public bool AnyFor(object argument, Type argumentType) + public bool AnyFor(object? argument, Type argumentType) { return AllSpecifications.Any(x => _argSpecCompatibilityTester.IsSpecificationCompatible(x, argument, argumentType)); } - public bool IsNextFor(object argument, Type argumentType) + public bool IsNextFor(object? argument, Type argumentType) { if (_queue.Count <= 0) { return false; } var nextArgSpec = _queue.Peek(); diff --git a/src/NSubstitute/Core/Call.cs b/src/NSubstitute/Core/Call.cs index e4372cc67..cf29918e6 100644 --- a/src/NSubstitute/Core/Call.cs +++ b/src/NSubstitute/Core/Call.cs @@ -11,17 +11,17 @@ namespace NSubstitute.Core public class Call : ICall, /* Performance optimization */ CallCollection.IReceivedCallEntry { private readonly MethodInfo _methodInfo; - private readonly object[] _arguments; - private object[] _originalArguments; + private readonly object?[] _arguments; + private object?[] _originalArguments; private readonly object _target; private readonly IList _argumentSpecifications; - private IParameterInfo[] _parameterInfosCached; + private IParameterInfo[]? _parameterInfosCached; private long? _sequenceNumber; - private readonly Func _baseMethod; + private readonly Func? _baseMethod; [Obsolete("This constructor is deprecated and will be removed in future version of product.")] public Call(MethodInfo methodInfo, - object[] arguments, + object?[] arguments, object target, IList argumentSpecifications, IParameterInfo[] parameterInfos, @@ -32,10 +32,10 @@ public Call(MethodInfo methodInfo, } public Call(MethodInfo methodInfo, - object[] arguments, + object?[] arguments, object target, IList argumentSpecifications, - Func baseMethod) + Func? baseMethod) { _methodInfo = methodInfo ?? throw new ArgumentNullException(nameof(methodInfo)); _arguments = arguments ?? throw new ArgumentNullException(nameof(arguments)); @@ -83,24 +83,24 @@ public long GetSequenceNumber() public bool CanCallBase => _baseMethod != null; - public Maybe TryCallBase() + public Maybe TryCallBase() { - return _baseMethod == null ? Maybe.Nothing() : Maybe.Just(_baseMethod()); + return _baseMethod == null ? Maybe.Nothing() : Maybe.Just(_baseMethod()); } public Type GetReturnType() => _methodInfo.ReturnType; public MethodInfo GetMethodInfo() => _methodInfo; - public object[] GetArguments() + public object?[] GetArguments() { // This method assumes that result might be mutated. // Therefore, we should guard our array with original values to ensure it's unmodified. // Also if array is empty - no sense to make a copy. - object[] originalArray = _originalArguments; + object?[] originalArray = _originalArguments; if (originalArray == _arguments && originalArray.Length > 0) { - object[] copy = originalArray.ToArray(); + object?[] copy = originalArray.ToArray(); // If it happens that _originalArguments doesn't point to the `_arguments` anymore - // it might happen that other thread already created a copy and mutated the original `_arguments` array. // In this case it's unsafe to replace it with a copy. @@ -110,7 +110,7 @@ public object[] GetArguments() return _arguments; } - public object[] GetOriginalArguments() + public object?[] GetOriginalArguments() { return _originalArguments; } diff --git a/src/NSubstitute/Core/CallActions.cs b/src/NSubstitute/Core/CallActions.cs index 916d31489..14a862500 100644 --- a/src/NSubstitute/Core/CallActions.cs +++ b/src/NSubstitute/Core/CallActions.cs @@ -55,7 +55,7 @@ public void InvokeMatchingActions(ICall call) return; } - CallInfo callInfo = null; + CallInfo? callInfo = null; foreach (var action in _actions) { if (!action.IsSatisfiedBy(call)) diff --git a/src/NSubstitute/Core/CallCollection.cs b/src/NSubstitute/Core/CallCollection.cs index f1b8cb9ab..ae7f2f929 100644 --- a/src/NSubstitute/Core/CallCollection.cs +++ b/src/NSubstitute/Core/CallCollection.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using NSubstitute.Exceptions; @@ -44,7 +45,7 @@ public IEnumerable AllCalls() { return _callEntries .Where(e => !e.IsSkipped) - .Select(e => e.Call) + .Select(e => e.Call!) .ToArray(); } @@ -60,7 +61,8 @@ public void Clear() /// internal interface IReceivedCallEntry { - ICall Call { get; } + ICall? Call { get; } + [MemberNotNullWhen(false, nameof(Call))] bool IsSkipped { get; } void Skip(); bool TryTakeEntryOwnership(); @@ -73,8 +75,9 @@ internal interface IReceivedCallEntry /// private class ReceivedCallEntry : IReceivedCallEntry { - public ICall Call { get; private set; } + public ICall? Call { get; private set; } + [MemberNotNullWhen(false, nameof(Call))] public bool IsSkipped => Call == null; public void Skip() => Call = null; // Null the hold value to reclaim a bit of memory. diff --git a/src/NSubstitute/Core/CallFactory.cs b/src/NSubstitute/Core/CallFactory.cs index d8e72768d..75305c09c 100644 --- a/src/NSubstitute/Core/CallFactory.cs +++ b/src/NSubstitute/Core/CallFactory.cs @@ -8,12 +8,12 @@ namespace NSubstitute.Core { public class CallFactory : ICallFactory { - public ICall Create(MethodInfo methodInfo, object[] arguments, object target, IList argumentSpecifications, Func baseMethod) + public ICall Create(MethodInfo methodInfo, object?[] arguments, object target, IList argumentSpecifications, Func? baseMethod) { return new Call(methodInfo, arguments, target, argumentSpecifications , baseMethod); } - public ICall Create(MethodInfo methodInfo, object[] arguments, object target, IList argumentSpecifications) + public ICall Create(MethodInfo methodInfo, object?[] arguments, object target, IList argumentSpecifications) { return new Call(methodInfo, arguments, target, argumentSpecifications, baseMethod: null); } diff --git a/src/NSubstitute/Core/CallInfo.cs b/src/NSubstitute/Core/CallInfo.cs index 20e56eedb..f15d5c5c7 100644 --- a/src/NSubstitute/Core/CallInfo.cs +++ b/src/NSubstitute/Core/CallInfo.cs @@ -1,8 +1,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using NSubstitute.Exceptions; +// Disable nullability for entry-point API +#nullable disable annotations + namespace NSubstitute.Core { public class CallInfo @@ -75,15 +79,15 @@ public T Arg() throw new ArgumentNotFoundException("Can not find an argument of type " + typeof(T).FullName + " to this call."); } - private bool TryGetArg(Func condition, out T value) + private bool TryGetArg(Func condition, [MaybeNull] out T value) { - value = default(T); + value = default; var matchingArgs = _callArguments.Where(condition); if (!matchingArgs.Any()) return false; ThrowIfMoreThanOne(matchingArgs); - value = (T)matchingArgs.First().Value; + value = (T)matchingArgs.First().Value!; return true; } @@ -107,24 +111,22 @@ private void ThrowIfMoreThanOne(IEnumerable arguments) /// The type of the argument to retrieve /// The zero-based position of the argument to retrieve /// The argument passed to the call, or throws if there is not exactly one argument of this type + [return: MaybeNull] public T ArgAt(int position) { - T arg; if (position >= _callArguments.Length) { throw new ArgumentOutOfRangeException("position", "There is no argument at position " + position); } try { - arg = (T) (_callArguments[position].Value); + return (T) _callArguments[position].Value!; } catch (InvalidCastException) { throw new InvalidCastException("Couldn't convert parameter at position" + position + " to type " + typeof(T).FullName); } - - return arg; } private static string DisplayTypes(IEnumerable types) diff --git a/src/NSubstitute/Core/CallResults.cs b/src/NSubstitute/Core/CallResults.cs index e5b7d38bf..a97e737a4 100644 --- a/src/NSubstitute/Core/CallResults.cs +++ b/src/NSubstitute/Core/CallResults.cs @@ -21,7 +21,7 @@ public void SetResult(ICallSpecification callSpecification, IReturn result) _results.Push(new ResultForCallSpec(callSpecification, result)); } - public bool TryGetResult(ICall call, out object result) + public bool TryGetResult(ICall call, out object? result) { result = default; if (ReturnsVoidFrom(call)) @@ -82,7 +82,7 @@ public ResultForCallSpec(ICallSpecification callSpecification, IReturn resultToR } public bool IsResultFor(ICall call) => _callSpecification.IsSatisfiedBy(call); - public object GetResult(ICall call, ICallInfoFactory callInfoFactory) + public object? GetResult(ICall call, ICallInfoFactory callInfoFactory) { if (_resultToReturn is ICallIndependentReturn callIndependentReturn) { diff --git a/src/NSubstitute/Core/CallRouter.cs b/src/NSubstitute/Core/CallRouter.cs index f19e42ece..355c48532 100644 --- a/src/NSubstitute/Core/CallRouter.cs +++ b/src/NSubstitute/Core/CallRouter.cs @@ -58,7 +58,7 @@ public IEnumerable ReceivedCalls() public void SetRoute(Func getRoute) => _threadContext.SetNextRoute(this, getRoute); - public object Route(ICall call) + public object? Route(ICall call) { _threadContext.SetLastCallRouter(this); @@ -71,7 +71,7 @@ public object Route(ICall call) return routeToUse.Handle(call); } - private IRoute ResolveCurrentRoute(ICall call, bool isQuerying, Func pendingRaisingEventArgs, Func queuedNextRouteFactory) + private IRoute ResolveCurrentRoute(ICall call, bool isQuerying, Func? pendingRaisingEventArgs, Func? queuedNextRouteFactory) { if (isQuerying) { diff --git a/src/NSubstitute/Core/CallSpecification.cs b/src/NSubstitute/Core/CallSpecification.cs index 0416ef6a6..f6e9ed0e2 100644 --- a/src/NSubstitute/Core/CallSpecification.cs +++ b/src/NSubstitute/Core/CallSpecification.cs @@ -92,7 +92,7 @@ private static bool AreEquivalentDefinitions(MethodInfo a, MethodInfo b) private bool IsMatchingArgumentSpecifications(ICall call) { - object[] arguments = call.GetOriginalArguments(); + object?[] arguments = call.GetOriginalArguments(); for (int i = 0; i < arguments.Length; i++) { if (!_argumentSpecifications[i].IsSatisfiedBy(arguments[i])) @@ -114,7 +114,7 @@ public IEnumerable NonMatchingArguments(ICall call) public override string ToString() { - var argSpecsAsStrings = _argumentSpecifications.Select(x => x.ToString()).ToArray(); + var argSpecsAsStrings = _argumentSpecifications.Select(x => x.ToString() ?? string.Empty).ToArray(); return CallFormatter.Default.Format(GetMethodInfo(), argSpecsAsStrings); } @@ -123,7 +123,7 @@ public string Format(ICall call) return CallFormatter.Default.Format(call.GetMethodInfo(), FormatArguments(call.GetOriginalArguments())); } - private IEnumerable FormatArguments(IEnumerable arguments) + private IEnumerable FormatArguments(IEnumerable arguments) { return arguments.Zip(_argumentSpecifications, (arg, spec) => spec.FormatArgument(arg)).ToArray(); } diff --git a/src/NSubstitute/Core/DefaultForType.cs b/src/NSubstitute/Core/DefaultForType.cs index 69d827515..783cdc33c 100644 --- a/src/NSubstitute/Core/DefaultForType.cs +++ b/src/NSubstitute/Core/DefaultForType.cs @@ -10,7 +10,7 @@ public class DefaultForType : IDefaultForType private static readonly object BoxedLong = default(long); private static readonly object BoxedDouble = default(double); - public object GetDefaultFor(Type type) + public object? GetDefaultFor(Type type) { if (IsVoid(type)) return null; if (type.GetTypeInfo().IsValueType) return DefaultInstanceOfValueType(type); @@ -42,7 +42,7 @@ private object DefaultInstanceOfValueType(Type returnType) return BoxedDouble; } - return Activator.CreateInstance(returnType); + return Activator.CreateInstance(returnType)!; } } } \ No newline at end of file diff --git a/src/NSubstitute/Core/DependencyInjection/INSubContainer.cs b/src/NSubstitute/Core/DependencyInjection/INSubContainer.cs index 0877b2e72..ea31db78a 100644 --- a/src/NSubstitute/Core/DependencyInjection/INSubContainer.cs +++ b/src/NSubstitute/Core/DependencyInjection/INSubContainer.cs @@ -4,7 +4,7 @@ namespace NSubstitute.Core.DependencyInjection { public interface INSubResolver { - T Resolve(); + T Resolve() where T : notnull; } public interface INSubContainer : INSubResolver @@ -24,32 +24,39 @@ public interface INSubContainer : INSubResolver public interface IConfigurableNSubContainer : INSubContainer { - IConfigurableNSubContainer Register(NSubLifetime lifetime) where TImpl : TKey; + IConfigurableNSubContainer Register(NSubLifetime lifetime) + where TKey : notnull + where TImpl : TKey; - IConfigurableNSubContainer Register(Func factory, NSubLifetime lifetime); + IConfigurableNSubContainer Register(Func factory, NSubLifetime lifetime) + where TKey : notnull; /// /// Decorates the original implementation with a custom decorator. /// The factory method is provided with an original implementation instance. /// The lifetime of decorated implementation is used. /// - IConfigurableNSubContainer Decorate(Func factory); + IConfigurableNSubContainer Decorate(Func factory) + where TKey : notnull; } public static class ConfigurableNSubContainerExtensions { public static IConfigurableNSubContainer RegisterPerScope(this IConfigurableNSubContainer container) + where TKey : notnull where TImpl : TKey { return container.Register(NSubLifetime.PerScope); } public static IConfigurableNSubContainer RegisterPerScope(this IConfigurableNSubContainer container, Func factory) + where TKey : notnull { return container.Register(factory, NSubLifetime.PerScope); } public static IConfigurableNSubContainer RegisterSingleton(this IConfigurableNSubContainer container) + where TKey : notnull where TImpl : TKey { return container.Register(NSubLifetime.Singleton); diff --git a/src/NSubstitute/Core/DependencyInjection/NSubContainer.cs b/src/NSubstitute/Core/DependencyInjection/NSubContainer.cs index de5a974b6..b9da62ae9 100644 --- a/src/NSubstitute/Core/DependencyInjection/NSubContainer.cs +++ b/src/NSubstitute/Core/DependencyInjection/NSubContainer.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -18,7 +19,7 @@ namespace NSubstitute.Core.DependencyInjection /// public class NSubContainer : IConfigurableNSubContainer { - private readonly NSubContainer _parentContainer; + private readonly NSubContainer? _parentContainer; private readonly object _syncRoot; private readonly Dictionary _registrations = new Dictionary(); @@ -36,9 +37,11 @@ private NSubContainer(NSubContainer parentContainer) _syncRoot = parentContainer._syncRoot; } - public T Resolve() => CreateScope().Resolve(); + public T Resolve() where T : notnull => CreateScope().Resolve(); - public IConfigurableNSubContainer Register(NSubLifetime lifetime) where TImpl : TKey + public IConfigurableNSubContainer Register(NSubLifetime lifetime) + where TKey : notnull + where TImpl : TKey { var constructors = typeof(TImpl).GetConstructors(); if (constructors.Length != 1) @@ -64,6 +67,7 @@ object Factory(Scope scope) } public IConfigurableNSubContainer Register(Func factory, NSubLifetime lifetime) + where TKey: notnull { object Factory(Scope scope) { @@ -76,8 +80,9 @@ object Factory(Scope scope) } public IConfigurableNSubContainer Decorate(Func factory) + where TKey : notnull { - Registration existingRegistration = TryFindRegistration(typeof(TKey)); + Registration? existingRegistration = TryFindRegistration(typeof(TKey)); if (existingRegistration == null) { throw new ArgumentException("Cannot decorate type " + typeof(TKey).FullName +" as implementation is not registered."); @@ -114,7 +119,7 @@ private void SetRegistration(Type type, Registration registration) } } - private Registration TryFindRegistration(Type type) + private Registration? TryFindRegistration(Type type) { // Both read and write accesses to dictionary should be synchronized. // The same lock object is shared among all the nested containers, @@ -139,7 +144,7 @@ private Registration TryFindRegistration(Type type) private class Registration { private readonly Func _factory; - private object _singletonValue; + private object? _singletonValue; public NSubLifetime Lifetime { get; } public Registration(Func factory, NSubLifetime lifetime) @@ -184,9 +189,9 @@ public Scope(NSubContainer mostNestedContainer) _mostNestedContainer = mostNestedContainer; } - public T Resolve() => (T) Resolve(typeof(T)); + public T Resolve() where T : notnull => (T) Resolve(typeof(T)); - public bool TryGetCached(Registration registration, out object result) + public bool TryGetCached(Registration registration, [MaybeNullWhen(false)] out object result) { return _cache.TryGetValue(registration, out result); } @@ -202,7 +207,7 @@ public object Resolve(Type type) // so we synchronize across the whole containers graph. lock (_mostNestedContainer._syncRoot) { - Registration registration = _mostNestedContainer.TryFindRegistration(type); + Registration? registration = _mostNestedContainer.TryFindRegistration(type); if (registration == null) throw new InvalidOperationException($"Type is not registered: {type.FullName}"); diff --git a/src/NSubstitute/Core/EventCallFormatter.cs b/src/NSubstitute/Core/EventCallFormatter.cs index db1a40827..32a414f10 100644 --- a/src/NSubstitute/Core/EventCallFormatter.cs +++ b/src/NSubstitute/Core/EventCallFormatter.cs @@ -21,12 +21,12 @@ public EventCallFormatter(Func> eventsToFormat) public bool CanFormat(MethodInfo methodInfo) { - return methodInfo.DeclaringType.GetEvents().Any(x => _eventsToFormat(methodInfo)(x)); + return methodInfo.DeclaringType!.GetEvents().Any(x => _eventsToFormat(methodInfo)(x)); } public string Format(MethodInfo methodInfo, IEnumerable arguments) { - var eventInfo = methodInfo.DeclaringType.GetEvents().First(x => _eventsToFormat(methodInfo)(x)); + var eventInfo = methodInfo.DeclaringType!.GetEvents().First(x => _eventsToFormat(methodInfo)(x)); return Format(eventInfo, _eventOperator, arguments); } diff --git a/src/NSubstitute/Core/Events/DelegateEventWrapper.cs b/src/NSubstitute/Core/Events/DelegateEventWrapper.cs index 93166d965..43bcbe459 100644 --- a/src/NSubstitute/Core/Events/DelegateEventWrapper.cs +++ b/src/NSubstitute/Core/Events/DelegateEventWrapper.cs @@ -6,21 +6,24 @@ namespace NSubstitute.Core.Events { public class DelegateEventWrapper : RaiseEventWrapper { - readonly object[] _providedArguments; + readonly object?[] _providedArguments; protected override string RaiseMethodName { get { return "Raise.Event"; } } - public DelegateEventWrapper(params object[] arguments) + public DelegateEventWrapper(params object?[] arguments) { _providedArguments = arguments ?? new object[0]; } +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations public static implicit operator T(DelegateEventWrapper wrapper) { RaiseEvent(wrapper); return default(T); } +#nullable restore annotations - protected override object[] WorkOutRequiredArguments(ICall call) + protected override object?[] WorkOutRequiredArguments(ICall call) { var requiredArgs = typeof(T).GetInvokeMethod().GetParameters(); @@ -44,10 +47,10 @@ bool LooksLikeAnEventStyleCall(ParameterInfo[] parameters) typeof(EventArgs).IsAssignableFrom(parameters[1].ParameterType); } - object[] WorkOutSenderAndEventArgs(Type eventArgsType, ICall call) + object?[] WorkOutSenderAndEventArgs(Type eventArgsType, ICall call) { - object sender; - object eventArgs; + object? sender; + object? eventArgs; if (_providedArguments.Length == 0) { sender = call.Target(); @@ -66,7 +69,7 @@ object[] WorkOutSenderAndEventArgs(Type eventArgsType, ICall call) return new[] { sender, eventArgs }; } - bool RequiredArgsHaveBeenProvided(object[] providedArgs, ParameterInfo[] requiredArgs) + bool RequiredArgsHaveBeenProvided(object?[] providedArgs, ParameterInfo[] requiredArgs) { if (providedArgs.Length != requiredArgs.Length) return false; for (var i = 0; i < providedArgs.Length; i++) diff --git a/src/NSubstitute/Core/Events/EventHandlerWrapper.cs b/src/NSubstitute/Core/Events/EventHandlerWrapper.cs index 709342667..fe33dcb4c 100644 --- a/src/NSubstitute/Core/Events/EventHandlerWrapper.cs +++ b/src/NSubstitute/Core/Events/EventHandlerWrapper.cs @@ -4,20 +4,22 @@ namespace NSubstitute.Core.Events { public class EventHandlerWrapper : RaiseEventWrapper where TEventArgs : EventArgs { - readonly object _sender; - readonly EventArgs _eventArgs; + readonly object? _sender; + readonly EventArgs? _eventArgs; protected override string RaiseMethodName { get { return "Raise.EventWith"; } } public EventHandlerWrapper() : this(null, null) { } - public EventHandlerWrapper(EventArgs eventArgs) : this(null, eventArgs) { } + public EventHandlerWrapper(EventArgs? eventArgs) : this(null, eventArgs) { } - public EventHandlerWrapper(object sender, EventArgs eventArgs) + public EventHandlerWrapper(object? sender, EventArgs? eventArgs) { _sender = sender; _eventArgs = eventArgs; } +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations public static implicit operator EventHandler(EventHandlerWrapper wrapper) { RaiseEvent(wrapper); @@ -29,6 +31,7 @@ public static implicit operator EventHandler(EventHandlerWrapper /// Checks if the instance can be used when a is expected. /// - public static bool IsCompatibleWith(this object instance, Type type) + public static bool IsCompatibleWith(this object? instance, Type type) { - var requiredType = type.IsByRef ? type.GetElementType() : type; + var requiredType = type.IsByRef ? type.GetElementType()! : type; return instance == null ? TypeCanBeNull(requiredType) : requiredType.IsInstanceOfType(instance); } @@ -44,7 +45,7 @@ public static bool IsDelegate(this Type type) public static MethodInfo GetInvokeMethod(this Type type) { - return type.GetMethod("Invoke"); + return type.GetMethod("Invoke") ?? throw new SubstituteInternalException("Expected delegate type"); } private static bool TypeCanBeNull(Type type) @@ -66,7 +67,7 @@ public static string GetNonMangledTypeName(this Type type) void AppendTypeNameRecursively(Type currentType) { - Type declaringType = currentType.DeclaringType; + Type? declaringType = currentType.DeclaringType; if (declaringType != null) { AppendTypeNameRecursively(declaringType); @@ -102,7 +103,7 @@ void AppendTypeNameRecursively(Type currentType) string GetTypeNameWithoutGenericArity(Type t) { - var tn = t.Name; + var tn = t.Name!; var indexOfBacktick = tn.IndexOf('`'); // For nested generic types the back stick symbol might be missing. return indexOfBacktick > -1 ? tn.Substring(0, indexOfBacktick) : tn; diff --git a/src/NSubstitute/Core/ICall.cs b/src/NSubstitute/Core/ICall.cs index 22158d886..18bd3c6ef 100644 --- a/src/NSubstitute/Core/ICall.cs +++ b/src/NSubstitute/Core/ICall.cs @@ -9,14 +9,14 @@ public interface ICall { Type GetReturnType(); MethodInfo GetMethodInfo(); - object[] GetArguments(); - object[] GetOriginalArguments(); + object?[] GetArguments(); + object?[] GetOriginalArguments(); object Target(); IParameterInfo[] GetParameterInfos(); IList GetArgumentSpecifications(); void AssignSequenceNumber(long number); long GetSequenceNumber(); bool CanCallBase { get; } - Maybe TryCallBase(); + Maybe TryCallBase(); } } diff --git a/src/NSubstitute/Core/ICallFactory.cs b/src/NSubstitute/Core/ICallFactory.cs index a30166a54..b6e7fad12 100644 --- a/src/NSubstitute/Core/ICallFactory.cs +++ b/src/NSubstitute/Core/ICallFactory.cs @@ -7,7 +7,7 @@ namespace NSubstitute.Core { public interface ICallFactory { - ICall Create(MethodInfo methodInfo, object[] arguments, object target, IList argumentSpecifications, Func baseMethod); - ICall Create(MethodInfo methodInfo, object[] getterArgs, object target, IList getArgumentSpecifications); + ICall Create(MethodInfo methodInfo, object?[] arguments, object target, IList argumentSpecifications, Func? baseMethod); + ICall Create(MethodInfo methodInfo, object?[] getterArgs, object target, IList getArgumentSpecifications); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/ICallResults.cs b/src/NSubstitute/Core/ICallResults.cs index e5fca79e4..9f3313452 100644 --- a/src/NSubstitute/Core/ICallResults.cs +++ b/src/NSubstitute/Core/ICallResults.cs @@ -3,7 +3,7 @@ namespace NSubstitute.Core public interface ICallResults { void SetResult(ICallSpecification callSpecification, IReturn result); - bool TryGetResult(ICall call, out object result); + bool TryGetResult(ICall call, out object? result); void Clear(); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/ICallRouter.cs b/src/NSubstitute/Core/ICallRouter.cs index 0b46b4ffd..d825417c8 100644 --- a/src/NSubstitute/Core/ICallRouter.cs +++ b/src/NSubstitute/Core/ICallRouter.cs @@ -15,7 +15,7 @@ public interface ICallRouter /// bool CallBaseByDefault { get; set; } ConfiguredCall LastCallShouldReturn(IReturn returnValue, MatchArgs matchArgs, PendingSpecificationInfo pendingSpecInfo); - object Route(ICall call); + object? Route(ICall call); IEnumerable ReceivedCalls(); [Obsolete("This method is deprecated and will be removed in future versions of the product. " + "Please use " + nameof(IThreadLocalContext) + "." + nameof(IThreadLocalContext.SetNextRoute) + " method instead.")] diff --git a/src/NSubstitute/Core/IDefaultForType.cs b/src/NSubstitute/Core/IDefaultForType.cs index 1c35f54d8..c3260b2d9 100644 --- a/src/NSubstitute/Core/IDefaultForType.cs +++ b/src/NSubstitute/Core/IDefaultForType.cs @@ -4,6 +4,6 @@ namespace NSubstitute.Core { public interface IDefaultForType { - object GetDefaultFor(Type type); + object? GetDefaultFor(Type type); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/IDescribeNonMatches.cs b/src/NSubstitute/Core/IDescribeNonMatches.cs index 7698e888e..e3da272d5 100644 --- a/src/NSubstitute/Core/IDescribeNonMatches.cs +++ b/src/NSubstitute/Core/IDescribeNonMatches.cs @@ -8,6 +8,6 @@ public interface IDescribeNonMatches /// /// /// Description of the non-match, or if no description can be provided. - string DescribeFor(object argument); + string DescribeFor(object? argument); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/IPendingSpecification.cs b/src/NSubstitute/Core/IPendingSpecification.cs index 5e66b8337..3a026de8a 100644 --- a/src/NSubstitute/Core/IPendingSpecification.cs +++ b/src/NSubstitute/Core/IPendingSpecification.cs @@ -3,7 +3,7 @@ public interface IPendingSpecification { bool HasPendingCallSpecInfo(); - PendingSpecificationInfo UseCallSpecInfo(); + PendingSpecificationInfo? UseCallSpecInfo(); void SetCallSpecification(ICallSpecification callSpecification); void SetLastCall(ICall lastCall); void Clear(); diff --git a/src/NSubstitute/Core/IProxyFactory.cs b/src/NSubstitute/Core/IProxyFactory.cs index f52e55029..c046cebf4 100644 --- a/src/NSubstitute/Core/IProxyFactory.cs +++ b/src/NSubstitute/Core/IProxyFactory.cs @@ -4,6 +4,6 @@ namespace NSubstitute.Core { public interface IProxyFactory { - object GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, object[] constructorArguments); + object GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[]? additionalInterfaces, object?[]? constructorArguments); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/IResultsForType.cs b/src/NSubstitute/Core/IResultsForType.cs index c89e7faa8..a710f8074 100644 --- a/src/NSubstitute/Core/IResultsForType.cs +++ b/src/NSubstitute/Core/IResultsForType.cs @@ -4,7 +4,7 @@ namespace NSubstitute.Core { public interface IResultsForType { void SetResult(Type type, IReturn resultToReturn); - bool TryGetResult(ICall call, out object result); + bool TryGetResult(ICall call, out object? result); void Clear(); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/IReturn.cs b/src/NSubstitute/Core/IReturn.cs index ee5334683..0901f2820 100644 --- a/src/NSubstitute/Core/IReturn.cs +++ b/src/NSubstitute/Core/IReturn.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using NSubstitute.Exceptions; @@ -8,8 +9,8 @@ namespace NSubstitute.Core { public interface IReturn { - object ReturnFor(CallInfo info); - Type TypeOrNull(); + object? ReturnFor(CallInfo info); + Type? TypeOrNull(); bool CanBeAssignedTo(Type t); } @@ -18,38 +19,38 @@ public interface IReturn /// internal interface ICallIndependentReturn { - object GetReturnValue(); + object? GetReturnValue(); } public class ReturnValue : IReturn, ICallIndependentReturn - { - private readonly object _value; + { + private readonly object? _value; - public ReturnValue(object value) + public ReturnValue(object? value) { _value = value; } - - public object GetReturnValue() => _value; - public object ReturnFor(CallInfo info) => GetReturnValue(); - public Type TypeOrNull() => _value?.GetType(); + + public object? GetReturnValue() => _value; + public object? ReturnFor(CallInfo info) => GetReturnValue(); + public Type? TypeOrNull() => _value?.GetType(); public bool CanBeAssignedTo(Type t) => _value.IsCompatibleWith(t); } public class ReturnValueFromFunc : IReturn { - private readonly Func _funcToReturnValue; + private readonly Func _funcToReturnValue; - public ReturnValueFromFunc(Func funcToReturnValue) + public ReturnValueFromFunc(Func? funcToReturnValue) { _funcToReturnValue = funcToReturnValue ?? ReturnNull(); } - public object ReturnFor(CallInfo info) => _funcToReturnValue(info); + public object? ReturnFor(CallInfo info) => _funcToReturnValue(info); public Type TypeOrNull() => typeof (T); public bool CanBeAssignedTo(Type t) => typeof (T).IsAssignableFrom(t); - private static Func ReturnNull() + private static Func ReturnNull() { if (typeof(T).GetTypeInfo().IsValueType) throw new CannotReturnNullForValueType(typeof(T)); return x => default(T); @@ -58,38 +59,38 @@ private static Func ReturnNull() public class ReturnMultipleValues : IReturn, ICallIndependentReturn { - private readonly ConcurrentQueue _valuesToReturn; - private readonly T _lastValue; + private readonly ConcurrentQueue _valuesToReturn; + private readonly T? _lastValue; - public ReturnMultipleValues(T[] values) + public ReturnMultipleValues(T?[] values) { - _valuesToReturn = new ConcurrentQueue(values); - _lastValue = values.LastOrDefault(); + _valuesToReturn = new ConcurrentQueue(values); + _lastValue = values.Last(); } - public object GetReturnValue() => GetNext(); - public object ReturnFor(CallInfo info) => GetReturnValue(); + public object? GetReturnValue() => GetNext(); + public object? ReturnFor(CallInfo info) => GetReturnValue(); public Type TypeOrNull() => typeof (T); public bool CanBeAssignedTo(Type t) => typeof (T).IsAssignableFrom(t); - private T GetNext() => _valuesToReturn.TryDequeue(out var nextResult) ? nextResult : _lastValue; + private T? GetNext() => _valuesToReturn.TryDequeue(out var nextResult) ? nextResult : _lastValue; } public class ReturnMultipleFuncsValues : IReturn { - private readonly ConcurrentQueue> _funcsToReturn; - private readonly Func _lastFunc; + private readonly ConcurrentQueue> _funcsToReturn; + private readonly Func _lastFunc; - public ReturnMultipleFuncsValues(Func[] funcs) + public ReturnMultipleFuncsValues(Func[] funcs) { - _funcsToReturn = new ConcurrentQueue>(funcs); - _lastFunc = funcs.LastOrDefault(); + _funcsToReturn = new ConcurrentQueue>(funcs); + _lastFunc = funcs.Last(); } - - public object ReturnFor(CallInfo info) => GetNext(info); + + public object? ReturnFor(CallInfo info) => GetNext(info); public Type TypeOrNull() => typeof (T); public bool CanBeAssignedTo(Type t) => typeof (T).IsAssignableFrom(t); - private T GetNext(CallInfo info) => _funcsToReturn.TryDequeue(out var nextFunc) ? nextFunc(info) : _lastFunc(info); + private T? GetNext(CallInfo info) => _funcsToReturn.TryDequeue(out var nextFunc) ? nextFunc(info) : _lastFunc(info); } } \ No newline at end of file diff --git a/src/NSubstitute/Core/ISubstitutionContext.cs b/src/NSubstitute/Core/ISubstitutionContext.cs index 0dce465ac..e4db98d15 100644 --- a/src/NSubstitute/Core/ISubstitutionContext.cs +++ b/src/NSubstitute/Core/ISubstitutionContext.cs @@ -25,7 +25,7 @@ public interface ISubstitutionContext [Obsolete("This property is obsolete and will be removed in a future version of the product. " + "Use the " + nameof(ThreadContext) + "." + nameof(IThreadLocalContext.PendingSpecification) + " property instead. " + "For example: SubstitutionContext.Current.ThreadContext." + nameof(IThreadLocalContext.PendingSpecification) + ".")] - PendingSpecificationInfo PendingSpecificationInfo { get; set; } + PendingSpecificationInfo? PendingSpecificationInfo { get; set; } [Obsolete("This method is obsolete and will be removed in a future version of the product. " + "Use the " + nameof(ThreadContext) + "." + nameof(IThreadLocalContext.LastCallShouldReturn) + "() method instead. " + @@ -55,7 +55,7 @@ public interface ISubstitutionContext [Obsolete("This method is obsolete and will be removed in a future version of the product. " + "Use the " + nameof(ThreadContext) + "." + nameof(IThreadLocalContext.UsePendingRaisingEventArgumentsFactory) + "() method instead. " + "For example: SubstitutionContext.Current.ThreadContext." + nameof(IThreadLocalContext.UsePendingRaisingEventArgumentsFactory) + "().")] - Func DequeuePendingRaisingEventArguments(); + Func? DequeuePendingRaisingEventArguments(); [Obsolete("This method is obsolete and will be removed in a future version of the product. " + "Use the " + nameof(ThreadContext) + "." + nameof(IThreadLocalContext.RunInQueryContext) + "() method instead. " + diff --git a/src/NSubstitute/Core/IThreadLocalContext.cs b/src/NSubstitute/Core/IThreadLocalContext.cs index 1fab0cb6c..d236d2440 100644 --- a/src/NSubstitute/Core/IThreadLocalContext.cs +++ b/src/NSubstitute/Core/IThreadLocalContext.cs @@ -21,16 +21,16 @@ public interface IThreadLocalContext /// Returns the previously configured next route and resets the stored value. /// If route was configured for the different router, returns and persist the route info. /// - Func UseNextRoute(ICallRouter callRouter); + Func? UseNextRoute(ICallRouter callRouter); void EnqueueArgumentSpecification(IArgumentSpecification spec); IList DequeueAllArgumentSpecifications(); - void SetPendingRaisingEventArgumentsFactory(Func getArguments); + void SetPendingRaisingEventArgumentsFactory(Func getArguments); /// /// Returns the previously set arguments factory and resets the stored value. /// - Func UsePendingRaisingEventArgumentsFactory(); + Func? UsePendingRaisingEventArgumentsFactory(); bool IsQuerying { get; } /// diff --git a/src/NSubstitute/Core/Maybe.cs b/src/NSubstitute/Core/Maybe.cs index d5288e5cc..6db701564 100644 --- a/src/NSubstitute/Core/Maybe.cs +++ b/src/NSubstitute/Core/Maybe.cs @@ -1,12 +1,13 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace NSubstitute.Core { /// /// Particularly poor implementation of Maybe/Option type. - /// This is just filling an immediate need; use FSharpOption or XSharpx or similar for a + /// This is just filling an immediate need; use FSharpOption or XSharpx or similar for a /// real implementation. /// /// @@ -27,7 +28,7 @@ public Maybe(T value) : this() public Maybe OrElse(Maybe other) { return OrElse(() => other); } public T ValueOr(Func other) { return Fold(other, x => x); } public T ValueOr(T other) { return ValueOr(() => other); } - public T ValueOrDefault() { return ValueOr(default(T)); } + public T? ValueOrDefault() { return ValueOr(default(T)!); } public TResult Fold(Func handleNoValue, Func handleValue) { diff --git a/src/NSubstitute/Core/PendingSpecificationInfo.cs b/src/NSubstitute/Core/PendingSpecificationInfo.cs index 75b1777f2..23e253005 100644 --- a/src/NSubstitute/Core/PendingSpecificationInfo.cs +++ b/src/NSubstitute/Core/PendingSpecificationInfo.cs @@ -4,30 +4,28 @@ namespace NSubstitute.Core { public class PendingSpecificationInfo { - private readonly bool _hasCallSpec; - private readonly ICallSpecification _callSpecification; - private readonly ICall _lastCall; + private readonly ICallSpecification? _callSpecification; + private readonly ICall? _lastCall; - private PendingSpecificationInfo(bool hasCallSpec, ICallSpecification callSpecification, ICall lastCall) + private PendingSpecificationInfo(ICallSpecification? callSpecification, ICall? lastCall) { - _hasCallSpec = hasCallSpec; _callSpecification = callSpecification; _lastCall = lastCall; } public T Handle(Func onCallSpec, Func onLastCall) { - return _hasCallSpec ? onCallSpec(_callSpecification) : onLastCall(_lastCall); + return _callSpecification != null ? onCallSpec(_callSpecification) : onLastCall(_lastCall!); } public static PendingSpecificationInfo FromLastCall(ICall lastCall) { - return new PendingSpecificationInfo(false, null, lastCall); + return new PendingSpecificationInfo(null, lastCall); } public static PendingSpecificationInfo FromCallSpecification(ICallSpecification callSpecification) { - return new PendingSpecificationInfo(true, callSpecification, null); + return new PendingSpecificationInfo(callSpecification, null); } } } \ No newline at end of file diff --git a/src/NSubstitute/Core/PropertyCallFormatter.cs b/src/NSubstitute/Core/PropertyCallFormatter.cs index 90bbeccf0..a94242281 100644 --- a/src/NSubstitute/Core/PropertyCallFormatter.cs +++ b/src/NSubstitute/Core/PropertyCallFormatter.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using NSubstitute.Exceptions; namespace NSubstitute.Core { @@ -14,6 +15,7 @@ public bool CanFormat(MethodInfo methodInfo) public string Format(MethodInfo methodInfo, IEnumerable arguments) { var propertyInfo = GetPropertyFromGetterOrSetterCall(methodInfo); + if (propertyInfo is null) throw new SubstituteInternalException("The 'CanFormat' method should have guarded it."); var numberOfIndexParams = propertyInfo.GetIndexParameters().Length; var propertyName = (numberOfIndexParams == 0) @@ -23,7 +25,7 @@ public string Format(MethodInfo methodInfo, IEnumerable arguments) return propertyName + FormatArgsAfterIndexParamsAsSetterArgs(numberOfIndexParams, arguments); } - private PropertyInfo GetPropertyFromGetterOrSetterCall(MethodInfo methodInfoOfCall) + private PropertyInfo? GetPropertyFromGetterOrSetterCall(MethodInfo methodInfoOfCall) { return methodInfoOfCall.GetPropertyFromSetterCallOrNull() ?? methodInfoOfCall.GetPropertyFromGetterCallOrNull(); } diff --git a/src/NSubstitute/Core/PropertyHelper.cs b/src/NSubstitute/Core/PropertyHelper.cs index f101462dc..ca637d3ef 100644 --- a/src/NSubstitute/Core/PropertyHelper.cs +++ b/src/NSubstitute/Core/PropertyHelper.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using NSubstitute.Core.Arguments; +using NSubstitute.Exceptions; namespace NSubstitute.Core { @@ -19,29 +21,31 @@ public PropertyHelper(ICallFactory callFactory, IArgumentSpecificationCompatibil public bool IsCallToSetAReadWriteProperty(ICall call) { - var propertySetter = GetPropertyFromSetterCallOrNull(call); + var propertySetter = GetPropertyFromSetterCall(call); return PropertySetterExistsAndHasAGetMethod(propertySetter); } - private bool PropertySetterExistsAndHasAGetMethod(PropertyInfo propertySetter) + private bool PropertySetterExistsAndHasAGetMethod([NotNullWhen(true)] PropertyInfo? propertySetter) { return propertySetter != null && propertySetter.GetGetMethod(nonPublic: true) != null; } - private PropertyInfo GetPropertyFromSetterCallOrNull(ICall call) + private PropertyInfo? GetPropertyFromSetterCall(ICall call) { return call.GetMethodInfo().GetPropertyFromSetterCallOrNull(); } public ICall CreateCallToPropertyGetterFromSetterCall(ICall callToSetter) { - var propertyInfo = GetPropertyFromSetterCallOrNull(callToSetter); + var propertyInfo = GetPropertyFromSetterCall(callToSetter); if (!PropertySetterExistsAndHasAGetMethod(propertyInfo)) { throw new InvalidOperationException("Could not find a GetMethod for \"" + callToSetter.GetMethodInfo() + "\""); } var getter = propertyInfo.GetGetMethod(nonPublic: true); + if (getter is null) throw new SubstituteInternalException("A property with a getter expected."); + var getterArgs = SkipLast(callToSetter.GetOriginalArguments()); var getterArgumentSpecifications = GetGetterCallSpecificationsFromSetterCall(callToSetter); diff --git a/src/NSubstitute/Core/Query.cs b/src/NSubstitute/Core/Query.cs index 32b5f73fc..84bb633e3 100644 --- a/src/NSubstitute/Core/Query.cs +++ b/src/NSubstitute/Core/Query.cs @@ -45,9 +45,9 @@ IEnumerable IQueryResults.QuerySpecification() private class CallSequenceNumberComparer : IEqualityComparer { - public bool Equals(ICall x, ICall y) + public bool Equals(ICall? x, ICall? y) { - return x.GetSequenceNumber() == y.GetSequenceNumber(); + return x?.GetSequenceNumber() == y?.GetSequenceNumber(); } public int GetHashCode(ICall obj) diff --git a/src/NSubstitute/Core/ReflectionExtensions.cs b/src/NSubstitute/Core/ReflectionExtensions.cs index 6763bc12a..ba8b68132 100644 --- a/src/NSubstitute/Core/ReflectionExtensions.cs +++ b/src/NSubstitute/Core/ReflectionExtensions.cs @@ -6,7 +6,7 @@ namespace NSubstitute.Core { public static class ReflectionExtensions { - public static PropertyInfo GetPropertyFromSetterCallOrNull(this MethodInfo call) + public static PropertyInfo? GetPropertyFromSetterCallOrNull(this MethodInfo call) { if (!CanBePropertySetterCall(call)) return null; @@ -19,7 +19,7 @@ public static PropertyInfo GetPropertyFromSetterCallOrNull(this MethodInfo call) return null; } - public static PropertyInfo GetPropertyFromGetterCallOrNull(this MethodInfo call) + public static PropertyInfo? GetPropertyFromGetterCallOrNull(this MethodInfo call) { return GetAllProperties(call.DeclaringType) .FirstOrDefault(x => x.GetGetMethod(nonPublic: true) == call); @@ -41,9 +41,11 @@ private static bool CanBePropertySetterCall(MethodInfo call) return call.Name.StartsWith("set_", StringComparison.Ordinal); } - private static PropertyInfo[] GetAllProperties(Type type) + private static PropertyInfo[] GetAllProperties(Type? type) { - return type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + return type != null + ? type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + : new PropertyInfo[0]; } } } diff --git a/src/NSubstitute/Core/ResultsForType.cs b/src/NSubstitute/Core/ResultsForType.cs index 33f78d303..b777f1403 100644 --- a/src/NSubstitute/Core/ResultsForType.cs +++ b/src/NSubstitute/Core/ResultsForType.cs @@ -19,7 +19,7 @@ public void SetResult(Type type, IReturn resultToReturn) _results.SetResult(new MatchingReturnTypeSpecification(type), resultToReturn); } - public bool TryGetResult(ICall call, out object result) + public bool TryGetResult(ICall call, out object? result) { return _results.TryGetResult(call, out result); } diff --git a/src/NSubstitute/Core/ReturnObservable.cs b/src/NSubstitute/Core/ReturnObservable.cs index 36686b892..b7c0bb982 100644 --- a/src/NSubstitute/Core/ReturnObservable.cs +++ b/src/NSubstitute/Core/ReturnObservable.cs @@ -1,19 +1,20 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace NSubstitute.Core { - internal class ReturnObservable : IObservable + internal class ReturnObservable : IObservable { - T _value; + T? _value; - public ReturnObservable() : this(default(T)) { } + public ReturnObservable() : this(default) { } - public ReturnObservable(T value) + public ReturnObservable(T? value) { _value = value; } - public IDisposable Subscribe(IObserver observer) + public IDisposable Subscribe(IObserver observer) { observer.OnNext(_value); observer.OnCompleted(); diff --git a/src/NSubstitute/Core/RobustThreadLocal.cs b/src/NSubstitute/Core/RobustThreadLocal.cs index 4697e4e48..f6288c527 100644 --- a/src/NSubstitute/Core/RobustThreadLocal.cs +++ b/src/NSubstitute/Core/RobustThreadLocal.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Threading; namespace NSubstitute.Core @@ -11,7 +12,7 @@ namespace NSubstitute.Core internal class RobustThreadLocal { private readonly ThreadLocal _threadLocal; - private readonly Func _initalValueFactory; + private readonly Func? _initialValueFactory; public RobustThreadLocal() { @@ -20,7 +21,7 @@ public RobustThreadLocal() public RobustThreadLocal(Func initialValueFactory) { - _initalValueFactory = initialValueFactory ?? throw new ArgumentNullException(nameof(initialValueFactory)); + _initialValueFactory = initialValueFactory; _threadLocal = new ThreadLocal(initialValueFactory); } @@ -28,13 +29,15 @@ public T Value { get { + // Suppress nullability for result, as we trust type by usage. + // For non-nullable we expect ctor with default to be used. try { - return _threadLocal.Value; + return _threadLocal.Value!; } catch (ObjectDisposedException) { - return _initalValueFactory != null ? _initalValueFactory.Invoke() : default(T); + return _initialValueFactory != null ? _initialValueFactory.Invoke() : default!; } } set diff --git a/src/NSubstitute/Core/RouteAction.cs b/src/NSubstitute/Core/RouteAction.cs index e188935a5..84e0fbb70 100644 --- a/src/NSubstitute/Core/RouteAction.cs +++ b/src/NSubstitute/Core/RouteAction.cs @@ -2,22 +2,22 @@ namespace NSubstitute.Core { public class RouteAction { - private readonly object _returnValue; + private readonly object? _returnValue; private readonly bool _hasReturnValue; public bool HasReturnValue { get { return _hasReturnValue; } } - public object ReturnValue { get { return _returnValue; } } + public object? ReturnValue { get { return _returnValue; } } private static readonly RouteAction _continue = new RouteAction(); public static RouteAction Continue() { return _continue; } - public static RouteAction Return(object value) { return new RouteAction(value); } + public static RouteAction Return(object? value) { return new RouteAction(value); } private RouteAction() { _hasReturnValue = false; } - private RouteAction(object returnValue) + private RouteAction(object? returnValue) { _returnValue = returnValue; _hasReturnValue = true; diff --git a/src/NSubstitute/Core/RouteFactoryCacheWrapper.cs b/src/NSubstitute/Core/RouteFactoryCacheWrapper.cs index d1308857a..4411616cd 100644 --- a/src/NSubstitute/Core/RouteFactoryCacheWrapper.cs +++ b/src/NSubstitute/Core/RouteFactoryCacheWrapper.cs @@ -53,7 +53,7 @@ public IRoute DoNotCallBase(ISubstituteState state, MatchArgs matchArgs) => public IRoute CallBase(ISubstituteState state, MatchArgs matchArgs) => _factory.CallBase(state, matchArgs); - public IRoute RaiseEvent(ISubstituteState state, Func getEventArguments) => + public IRoute RaiseEvent(ISubstituteState state, Func getEventArguments) => _factory.RaiseEvent(state, getEventArguments); private readonly struct CachedRoute diff --git a/src/NSubstitute/Core/SequenceChecking/InstanceTracker.cs b/src/NSubstitute/Core/SequenceChecking/InstanceTracker.cs index a50975515..10186ccf2 100644 --- a/src/NSubstitute/Core/SequenceChecking/InstanceTracker.cs +++ b/src/NSubstitute/Core/SequenceChecking/InstanceTracker.cs @@ -26,7 +26,7 @@ public int InstanceNumber(object o) private class ReferenceEqualityComparer : EqualityComparer { - public override bool Equals(object x, object y) { return object.ReferenceEquals(x, y); } + public override bool Equals(object? x, object? y) { return object.ReferenceEquals(x, y); } public override int GetHashCode(object obj) { return RuntimeHelpers.GetHashCode(obj); } } } diff --git a/src/NSubstitute/Core/SequenceChecking/SequenceFormatter.cs b/src/NSubstitute/Core/SequenceChecking/SequenceFormatter.cs index 99244c1c3..e8f9fd261 100644 --- a/src/NSubstitute/Core/SequenceChecking/SequenceFormatter.cs +++ b/src/NSubstitute/Core/SequenceChecking/SequenceFormatter.cs @@ -44,8 +44,7 @@ private bool HasMultipleCallsOnSameType() var lookup = new Dictionary(); foreach (var x in _query) { - object instance; - if (lookup.TryGetValue(x.DeclaringType, out instance)) + if (lookup.TryGetValue(x.DeclaringType, out var instance)) { if (!ReferenceEquals(x.Target, instance)) { return true; } } @@ -60,8 +59,8 @@ private bool HasMultipleCallsOnSameType() private class CallData { private readonly int _instanceNumber; - private readonly ICall _call; - private readonly CallSpecAndTarget _specAndTarget; + private readonly ICall? _call; + private readonly CallSpecAndTarget? _specAndTarget; public CallData(int instanceNumber, CallSpecAndTarget specAndTarget) { @@ -75,37 +74,32 @@ public CallData(int instanceNumber, ICall call) _call = call; } - private MethodInfo MethodInfo - { - get - { - return _call == null - ? _specAndTarget.CallSpecification.GetMethodInfo() - : _call.GetMethodInfo(); - } - } + private MethodInfo MethodInfo => + _call != null + ? _call.GetMethodInfo() + : _specAndTarget!.CallSpecification.GetMethodInfo(); - public object Target { get { return _call == null ? _specAndTarget.Target : _call.Target(); } } + public object Target => _call != null ? _call.Target() : _specAndTarget!.Target; - public Type DeclaringType { get { return MethodInfo.DeclaringType; } } + public Type DeclaringType => MethodInfo.DeclaringType!; public string Format(bool multipleInstances, bool includeInstanceNumber) { - var call = (_call == null) - ? Format(_specAndTarget) - : Format(_call); + var call = _call != null + ? Format(_call) + : Format(_specAndTarget!); - if (!multipleInstances) { return call; } + if (!multipleInstances) return call; var instanceIdentifier = includeInstanceNumber ? _instanceNumber + "@" : ""; - var declaringTypeName = MethodInfo.DeclaringType.GetNonMangledTypeName(); + var declaringTypeName = MethodInfo.DeclaringType!.GetNonMangledTypeName(); return string.Format("{1}{0}.{2}", declaringTypeName, instanceIdentifier, call); } private string Format(CallSpecAndTarget x) { - return x.CallSpecification.ToString(); + return x.CallSpecification.ToString() ?? string.Empty; } private string Format(ICall call) @@ -122,7 +116,7 @@ private IEnumerable FormatArgs(ArgAndParamInfo[] arguments) var argsWithParamsExpanded = arguments .SelectMany(a => a.ParamInfo.IsParams() - ? ((IEnumerable) a.Argument).Cast() + ? ((IEnumerable) a.Argument!).Cast() : ToEnumerable(a.Argument)) .Select(x => ArgumentFormatter.Default.Format(x, false)) .ToArray(); @@ -139,9 +133,9 @@ private IEnumerable ToEnumerable(T value) private class ArgAndParamInfo { public ParameterInfo ParamInfo { get; private set; } - public object Argument { get; private set; } + public object? Argument { get; private set; } - public ArgAndParamInfo(ParameterInfo paramInfo, object argument) + public ArgAndParamInfo(ParameterInfo paramInfo, object? argument) { ParamInfo = paramInfo; Argument = argument; diff --git a/src/NSubstitute/Core/SubstituteFactory.cs b/src/NSubstitute/Core/SubstituteFactory.cs index f0bf842b3..75a69b2c8 100644 --- a/src/NSubstitute/Core/SubstituteFactory.cs +++ b/src/NSubstitute/Core/SubstituteFactory.cs @@ -24,7 +24,7 @@ public SubstituteFactory(ISubstituteStateFactory substituteStateFactory, ICallRo /// /// /// - public object Create(Type[] typesToProxy, object[] constructorArguments) + public object Create(Type[] typesToProxy, object?[] constructorArguments) { return Create(typesToProxy, constructorArguments, callBaseByDefault: false); } @@ -37,7 +37,7 @@ public object Create(Type[] typesToProxy, object[] constructorArguments) /// /// /// - public object CreatePartial(Type[] typesToProxy, object[] constructorArguments) + public object CreatePartial(Type[] typesToProxy, object?[] constructorArguments) { var primaryProxyType = GetPrimaryProxyType(typesToProxy); if (!CanCallBaseImplementation(primaryProxyType)) @@ -48,7 +48,7 @@ public object CreatePartial(Type[] typesToProxy, object[] constructorArguments) return Create(typesToProxy, constructorArguments, callBaseByDefault: true); } - private object Create(Type[] typesToProxy, object[] constructorArguments, bool callBaseByDefault) + private object Create(Type[] typesToProxy, object?[] constructorArguments, bool callBaseByDefault) { var substituteState = _substituteStateFactory.Create(this); substituteState.CallBaseConfiguration.CallBaseByDefault = callBaseByDefault; diff --git a/src/NSubstitute/Core/SubstitutionContext.cs b/src/NSubstitute/Core/SubstitutionContext.cs index f00d33586..e1cb83fe9 100644 --- a/src/NSubstitute/Core/SubstitutionContext.cs +++ b/src/NSubstitute/Core/SubstitutionContext.cs @@ -59,7 +59,7 @@ public ICallRouter GetCallRouterFor(object substitute) => [Obsolete("This property is obsolete and will be removed in a future version of the product. " + "Use the " + nameof(ThreadContext) + "." + nameof(IThreadLocalContext.PendingSpecification) + " property instead. " + "For example: SubstitutionContext.Current.ThreadContext." + nameof(IThreadLocalContext.PendingSpecification) + ".")] - public PendingSpecificationInfo PendingSpecificationInfo + public PendingSpecificationInfo? PendingSpecificationInfo { get { @@ -83,8 +83,8 @@ public PendingSpecificationInfo PendingSpecificationInfo // Emulate the old API. A bit clumsy, however it's here for the backward compatibility only // and is not expected to be used frequently. var unwrappedValue = value.Handle( - spec => Tuple.Create(spec, (ICall) null), - call => Tuple.Create((ICallSpecification) null, call)); + spec => Tuple.Create(spec, null), + call => Tuple.Create(null, call)); if (unwrappedValue.Item1 != null) { @@ -92,7 +92,7 @@ public PendingSpecificationInfo PendingSpecificationInfo } else { - ThreadContext.PendingSpecification.SetLastCall(unwrappedValue.Item2); + ThreadContext.PendingSpecification.SetLastCall(unwrappedValue.Item2!); } } } @@ -141,7 +141,7 @@ public void RaiseEventForNextCall(Func getArguments) => [Obsolete("This method is obsolete and will be removed in a future version of the product. " + "Use the " + nameof(ThreadContext) + "." + nameof(IThreadLocalContext.UsePendingRaisingEventArgumentsFactory) + "() method instead. " + "For example: SubstitutionContext.Current.ThreadContext." + nameof(IThreadLocalContext.UsePendingRaisingEventArgumentsFactory) + "().")] - public Func DequeuePendingRaisingEventArguments() => + public Func? DequeuePendingRaisingEventArguments() => ThreadContext.UsePendingRaisingEventArgumentsFactory(); [Obsolete("This method is obsolete and will be removed in a future version of the product. " + diff --git a/src/NSubstitute/Core/ThreadLocalContext.cs b/src/NSubstitute/Core/ThreadLocalContext.cs index 9e7d4d1d3..34f161235 100644 --- a/src/NSubstitute/Core/ThreadLocalContext.cs +++ b/src/NSubstitute/Core/ThreadLocalContext.cs @@ -10,22 +10,22 @@ public class ThreadLocalContext : IThreadLocalContext { private static readonly IArgumentSpecification[] EmptySpecifications = new IArgumentSpecification[0]; - private readonly RobustThreadLocal _lastCallRouter; + private readonly RobustThreadLocal _lastCallRouter; private readonly RobustThreadLocal> _argumentSpecifications; - private readonly RobustThreadLocal> _getArgumentsForRaisingEvent; - private readonly RobustThreadLocal _currentQuery; + private readonly RobustThreadLocal?> _getArgumentsForRaisingEvent; + private readonly RobustThreadLocal _currentQuery; private readonly RobustThreadLocal _pendingSpecificationInfo; - private readonly RobustThreadLocal>> _nextRouteFactory; + private readonly RobustThreadLocal>?> _nextRouteFactory; public IPendingSpecification PendingSpecification { get; } public ThreadLocalContext() { - _lastCallRouter = new RobustThreadLocal(); + _lastCallRouter = new RobustThreadLocal(); _argumentSpecifications = new RobustThreadLocal>(() => new List()); - _getArgumentsForRaisingEvent = new RobustThreadLocal>(); - _currentQuery = new RobustThreadLocal(); + _getArgumentsForRaisingEvent = new RobustThreadLocal?>(); + _currentQuery = new RobustThreadLocal(); _pendingSpecificationInfo = new RobustThreadLocal(); - _nextRouteFactory = new RobustThreadLocal>>(); + _nextRouteFactory = new RobustThreadLocal>?>(); PendingSpecification = new PendingSpecificationWrapper(_pendingSpecificationInfo); } @@ -51,7 +51,7 @@ public ConfiguredCall LastCallShouldReturn(IReturn value, MatchArgs matchArgs) throw new UnexpectedArgumentMatcherException(); } - var pendingSpecInfo = PendingSpecification.UseCallSpecInfo(); + var pendingSpecInfo = PendingSpecification.UseCallSpecInfo()!; var configuredCall = lastCallRouter.LastCallShouldReturn(value, matchArgs, pendingSpecInfo); ClearLastCallRouter(); return configuredCall; @@ -65,7 +65,7 @@ public void SetNextRoute(ICallRouter callRouter, Func _nextRouteFactory.Value = Tuple.Create(callRouter, nextRouteFactory); } - public Func UseNextRoute(ICallRouter callRouter) + public Func? UseNextRoute(ICallRouter callRouter) { if (callRouter == null) throw new ArgumentNullException(nameof(callRouter)); @@ -115,12 +115,12 @@ public IList DequeueAllArgumentSpecifications() return queue; } - public void SetPendingRaisingEventArgumentsFactory(Func getArguments) + public void SetPendingRaisingEventArgumentsFactory(Func getArguments) { _getArgumentsForRaisingEvent.Value = getArguments; } - public Func UsePendingRaisingEventArgumentsFactory() + public Func? UsePendingRaisingEventArgumentsFactory() { var result = _getArgumentsForRaisingEvent.Value; if (result != null) @@ -169,7 +169,7 @@ public bool HasPendingCallSpecInfo() return _valueHolder.Value.HasValue; } - public PendingSpecificationInfo UseCallSpecInfo() + public PendingSpecificationInfo? UseCallSpecInfo() { var info = _valueHolder.Value; Clear(); @@ -194,18 +194,18 @@ public void Clear() private readonly struct PendingSpecInfoData { - private readonly ICallSpecification _callSpecification; - private readonly ICall _lastCall; + private readonly ICallSpecification? _callSpecification; + private readonly ICall? _lastCall; public bool HasValue => _lastCall != null || _callSpecification != null; - private PendingSpecInfoData(ICallSpecification callSpecification, ICall lastCall) + private PendingSpecInfoData(ICallSpecification? callSpecification, ICall? lastCall) { _callSpecification = callSpecification; _lastCall = lastCall; } - public PendingSpecificationInfo ToPendingSpecificationInfo() + public PendingSpecificationInfo? ToPendingSpecificationInfo() { if (_callSpecification != null) return PendingSpecificationInfo.FromCallSpecification(_callSpecification); diff --git a/src/NSubstitute/Core/WhenCalled.cs b/src/NSubstitute/Core/WhenCalled.cs index 8ff87d126..08d1bfa51 100644 --- a/src/NSubstitute/Core/WhenCalled.cs +++ b/src/NSubstitute/Core/WhenCalled.cs @@ -17,7 +17,7 @@ public WhenCalled(ISubstitutionContext context, T substitute, Action call, Ma _substitute = substitute; _call = call; _matchArgs = matchArgs; - _callRouter = context.GetCallRouterFor(substitute); + _callRouter = context.GetCallRouterFor(substitute!); _routeFactory = context.RouteFactory; _threadContext = context.ThreadContext; } diff --git a/src/NSubstitute/Exceptions/AmbiguousArgumentsException.cs b/src/NSubstitute/Exceptions/AmbiguousArgumentsException.cs index da021a560..e8b5cc855 100644 --- a/src/NSubstitute/Exceptions/AmbiguousArgumentsException.cs +++ b/src/NSubstitute/Exceptions/AmbiguousArgumentsException.cs @@ -29,7 +29,7 @@ public AmbiguousArgumentsException(string message) : base(message) } public AmbiguousArgumentsException(MethodInfo method, - IEnumerable invocationArguments, + IEnumerable invocationArguments, IEnumerable matchedSpecifications, IEnumerable allSpecifications) : this(BuildExceptionMessage(method, invocationArguments, matchedSpecifications, allSpecifications)) @@ -37,13 +37,13 @@ public AmbiguousArgumentsException(MethodInfo method, } private static string BuildExceptionMessage(MethodInfo method, - IEnumerable invocationArguments, + IEnumerable invocationArguments, IEnumerable matchedSpecifications, IEnumerable allSpecifications) { - string methodSignature = null; - string methodArgsWithHighlightedPossibleArgSpecs = null; - string matchedSpecificationsInfo = null; + string? methodSignature = null; + string? methodArgsWithHighlightedPossibleArgSpecs = null; + string? matchedSpecificationsInfo = null; if (CallFormatter.Default.CanFormat(method)) { var argsWithInlinedParamsArray = invocationArguments.ToArray(); @@ -107,10 +107,10 @@ private static IEnumerable FormatMethodParameterTypes(IEnumerable FormatMethodParameterTypes(IEnumerable FormatMethodArguments(IEnumerable arguments) + private static IEnumerable FormatMethodArguments(IEnumerable arguments) { var defaultChecker = new DefaultChecker(new DefaultForType()); @@ -130,9 +130,9 @@ private static IEnumerable FormatMethodArguments(IEnumerable arg }); } - private static IEnumerable PadNonMatchedSpecifications(IEnumerable matchedSpecifications, IEnumerable allArguments) + private static IEnumerable PadNonMatchedSpecifications(IEnumerable matchedSpecifications, IEnumerable allArguments) { - var allMatchedSpecs = matchedSpecifications.Select(x => x.ToString()).ToArray(); + var allMatchedSpecs = matchedSpecifications.Select(x => x.ToString() ?? string.Empty).ToArray(); int nonResolvedArgumentsCount = allArguments.Count() - allMatchedSpecs.Length; var nonResolvedArgsPlaceholders = Enumerable.Repeat("???", nonResolvedArgumentsCount); diff --git a/src/NSubstitute/Exceptions/CouldNotSetReturnException.cs b/src/NSubstitute/Exceptions/CouldNotSetReturnException.cs index 3418a787f..ae9829b3c 100644 --- a/src/NSubstitute/Exceptions/CouldNotSetReturnException.cs +++ b/src/NSubstitute/Exceptions/CouldNotSetReturnException.cs @@ -40,13 +40,13 @@ public class CouldNotSetReturnDueToMissingInfoAboutLastCallException : CouldNotS public class CouldNotSetReturnDueToTypeMismatchException : CouldNotSetReturnException { - public CouldNotSetReturnDueToTypeMismatchException(Type returnType, MethodInfo member) : base(DescribeProblem(returnType, member)) { } + public CouldNotSetReturnDueToTypeMismatchException(Type? returnType, MethodInfo member) : base(DescribeProblem(returnType, member)) { } - private static string DescribeProblem(Type typeOfReturnValueOrNull, MethodInfo member) + private static string DescribeProblem(Type? typeOfReturnValue, MethodInfo member) { - return typeOfReturnValueOrNull == null - ? String.Format("Can not return null for {0}.{1} (expected type {2}).", member.DeclaringType.Name, member.Name, member.ReturnType.Name) - : String.Format("Can not return value of type {0} for {1}.{2} (expected type {3}).", typeOfReturnValueOrNull.Name, member.DeclaringType.Name, member.Name, member.ReturnType.Name); + return typeOfReturnValue == null + ? string.Format("Can not return null for {0}.{1} (expected type {2}).", member.DeclaringType!.Name, member.Name, member.ReturnType.Name) + : string.Format("Can not return value of type {0} for {1}.{2} (expected type {3}).", typeOfReturnValue.Name, member.DeclaringType!.Name, member.Name, member.ReturnType.Name); } } } diff --git a/src/NSubstitute/Exceptions/NullSubstituteReferenceException.cs b/src/NSubstitute/Exceptions/NullSubstituteReferenceException.cs index 179c7c79e..9010b5cd7 100644 --- a/src/NSubstitute/Exceptions/NullSubstituteReferenceException.cs +++ b/src/NSubstitute/Exceptions/NullSubstituteReferenceException.cs @@ -1,7 +1,8 @@ namespace NSubstitute.Exceptions { - public class NullSubstituteReferenceException : NotASubstituteException + public class NullSubstituteReferenceException : SubstituteException { - public NullSubstituteReferenceException() { } + const string Explanation = "NSubstitute extension methods like .Received() can only be called on non-null objects."; + public NullSubstituteReferenceException() : base(Explanation) { } } } diff --git a/src/NSubstitute/Exceptions/SubstituteException.cs b/src/NSubstitute/Exceptions/SubstituteException.cs index a28886390..500794daa 100644 --- a/src/NSubstitute/Exceptions/SubstituteException.cs +++ b/src/NSubstitute/Exceptions/SubstituteException.cs @@ -6,6 +6,6 @@ public class SubstituteException : Exception { public SubstituteException() : this("") { } public SubstituteException(string message) : this(message, null) { } - public SubstituteException(string message, Exception innerException) : base(message, innerException) { } + public SubstituteException(string message, Exception? innerException) : base(message, innerException) { } } } diff --git a/src/NSubstitute/Exceptions/SubstituteInternalException.cs b/src/NSubstitute/Exceptions/SubstituteInternalException.cs index b1e6e66d2..b5dca1ff1 100644 --- a/src/NSubstitute/Exceptions/SubstituteInternalException.cs +++ b/src/NSubstitute/Exceptions/SubstituteInternalException.cs @@ -7,7 +7,7 @@ public class SubstituteInternalException : SubstituteException { public SubstituteInternalException() : this("") { } public SubstituteInternalException(string message) : this(message, null) { } - public SubstituteInternalException(string message, Exception innerException) + public SubstituteInternalException(string message, Exception? innerException) : base("Please report this exception at https://github.com/nsubstitute/NSubstitute/issues: \n\n" + message, innerException) { } } diff --git a/src/NSubstitute/Extensions/ClearExtensions.cs b/src/NSubstitute/Extensions/ClearExtensions.cs index 167bc23ca..ae4e83afc 100644 --- a/src/NSubstitute/Extensions/ClearExtensions.cs +++ b/src/NSubstitute/Extensions/ClearExtensions.cs @@ -1,5 +1,9 @@ using System; using NSubstitute.Core; +using NSubstitute.Exceptions; + +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations namespace NSubstitute.ClearExtensions { @@ -16,8 +20,10 @@ public static class ClearExtensions /// public static void ClearSubstitute(this T substitute, ClearOptions options = ClearOptions.All) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; - var router = context.GetCallRouterFor(substitute); + var router = context.GetCallRouterFor(substitute!); router.Clear(options); } } diff --git a/src/NSubstitute/Extensions/ConfigurationExtensions.cs b/src/NSubstitute/Extensions/ConfigurationExtensions.cs index 5dba7a92c..32a1b98a1 100644 --- a/src/NSubstitute/Extensions/ConfigurationExtensions.cs +++ b/src/NSubstitute/Extensions/ConfigurationExtensions.cs @@ -1,4 +1,9 @@ -using NSubstitute.Core; +using System; +using NSubstitute.Core; +using NSubstitute.Exceptions; + +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations namespace NSubstitute.Extensions { @@ -21,8 +26,10 @@ public static class ConfigurationExtensions /// public static T Configure(this T substitute) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; - var callRouter = context.GetCallRouterFor(substitute); + var callRouter = context.GetCallRouterFor(substitute!); context.ThreadContext.SetNextRoute(callRouter, context.RouteFactory.RecordCallSpecification); return substitute; diff --git a/src/NSubstitute/Extensions/ExceptionExtensions.cs b/src/NSubstitute/Extensions/ExceptionExtensions.cs index 722e684a2..bea33cb05 100644 --- a/src/NSubstitute/Extensions/ExceptionExtensions.cs +++ b/src/NSubstitute/Extensions/ExceptionExtensions.cs @@ -1,6 +1,9 @@ using System; using NSubstitute.Core; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute.ExceptionExtensions { public static class ExceptionExtensions @@ -23,7 +26,7 @@ public static ConfiguredCall Throws(this object value, Exception ex) /// /// public static ConfiguredCall Throws(this object value) - where TException : Exception, new() + where TException : notnull, Exception, new() { return value.Returns(_ => { throw new TException(); }); } @@ -57,7 +60,7 @@ public static ConfiguredCall ThrowsForAnyArgs(this object value, Exception ex) /// /// public static ConfiguredCall ThrowsForAnyArgs(this object value) - where TException : Exception, new() + where TException : notnull, Exception, new() { return value.ReturnsForAnyArgs(_ => { throw new TException(); }); } diff --git a/src/NSubstitute/Extensions/ReceivedExtensions.cs b/src/NSubstitute/Extensions/ReceivedExtensions.cs index 64b5a29c6..c1293568e 100644 --- a/src/NSubstitute/Extensions/ReceivedExtensions.cs +++ b/src/NSubstitute/Extensions/ReceivedExtensions.cs @@ -2,6 +2,10 @@ using System.Collections.Generic; using System.Linq; using NSubstitute.Core; +using NSubstitute.Exceptions; + +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations namespace NSubstitute.ReceivedExtensions { @@ -16,6 +20,8 @@ public static class ReceivedExtensions /// public static T Received(this T substitute, Quantity requiredQuantity) { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; var callRouter = context.GetCallRouterFor(substitute); @@ -32,6 +38,8 @@ public static T Received(this T substitute, Quantity requiredQuantity) /// public static T ReceivedWithAnyArgs(this T substitute, Quantity requiredQuantity) { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; var callRouter = context.GetCallRouterFor(substitute); diff --git a/src/NSubstitute/Extensions/ReturnsExtensions.cs b/src/NSubstitute/Extensions/ReturnsExtensions.cs index 252ea9921..41b187678 100644 --- a/src/NSubstitute/Extensions/ReturnsExtensions.cs +++ b/src/NSubstitute/Extensions/ReturnsExtensions.cs @@ -1,6 +1,9 @@ using System.Threading.Tasks; using NSubstitute.Core; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute.ReturnsExtensions { public static class ReturnsExtensions diff --git a/src/NSubstitute/Extensions/ReturnsForAllExtensions.cs b/src/NSubstitute/Extensions/ReturnsForAllExtensions.cs index 4486afa96..5c9f56d6c 100644 --- a/src/NSubstitute/Extensions/ReturnsForAllExtensions.cs +++ b/src/NSubstitute/Extensions/ReturnsForAllExtensions.cs @@ -1,5 +1,9 @@ using System; using NSubstitute.Core; +using NSubstitute.Exceptions; + +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations namespace NSubstitute.Extensions { @@ -14,8 +18,10 @@ public static class ReturnsForAllExtensions /// public static void ReturnsForAll(this object substitute, T returnThis) { - var _callRouter = SubstitutionContext.Current.GetCallRouterFor(substitute); - _callRouter.SetReturnForType(typeof(T), new ReturnValue(returnThis)); + if (substitute == null) throw new NullSubstituteReferenceException(); + + var callRouter = SubstitutionContext.Current.GetCallRouterFor(substitute); + callRouter.SetReturnForType(typeof(T), new ReturnValue(returnThis)); } /// @@ -27,6 +33,8 @@ public static void ReturnsForAll(this object substitute, T returnThis) /// public static void ReturnsForAll(this object substitute, Func returnThis) { + if (substitute == null) throw new NullSubstituteReferenceException(); + var _callRouter = SubstitutionContext.Current.GetCallRouterFor(substitute); _callRouter.SetReturnForType(typeof(T), new ReturnValueFromFunc(returnThis)); } diff --git a/src/NSubstitute/NSubstitute.csproj b/src/NSubstitute/NSubstitute.csproj index 1184b4f14..baceab95d 100644 --- a/src/NSubstitute/NSubstitute.csproj +++ b/src/NSubstitute/NSubstitute.csproj @@ -30,7 +30,14 @@ ..\..\bin\$(Configuration)\NSubstitute\$(TargetFramework)\NSubstitute.xml - 1701;1702;1705;1591 + $(NoWarn);1701;1702;1705;1591 + + + + enable + + + $(NoWarn);CS8632 @@ -38,7 +45,7 @@ - + @@ -48,4 +55,8 @@ $(DefineConstants);SYSTEM_REFLECTION_CUSTOMATTRIBUTES_IS_ARRAY + + $(DefineConstants);SYSTEM_DIAGNOSTICS_CODEANALYSIS_NULLABILITY + + diff --git a/src/NSubstitute/Proxies/CastleDynamicProxy/CastleDynamicProxyFactory.cs b/src/NSubstitute/Proxies/CastleDynamicProxy/CastleDynamicProxyFactory.cs index 7748da23b..5ca3315aa 100644 --- a/src/NSubstitute/Proxies/CastleDynamicProxy/CastleDynamicProxyFactory.cs +++ b/src/NSubstitute/Proxies/CastleDynamicProxy/CastleDynamicProxyFactory.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using Castle.DynamicProxy; @@ -22,14 +23,14 @@ public CastleDynamicProxyFactory(ICallFactory callFactory, IArgumentSpecificatio _allMethodsExceptCallRouterCallsHook = new AllMethodsExceptCallRouterCallsHook(); } - public object GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, object[] constructorArguments) + public object GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[]? additionalInterfaces, object?[]? constructorArguments) { return typeToProxy.IsDelegate() ? GenerateDelegateProxy(callRouter, typeToProxy, additionalInterfaces, constructorArguments) : GenerateTypeProxy(callRouter, typeToProxy, additionalInterfaces, constructorArguments); } - private object GenerateTypeProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, object[] constructorArguments) + private object GenerateTypeProxy(ICallRouter callRouter, Type typeToProxy, Type[]? additionalInterfaces, object?[]? constructorArguments) { VerifyClassHasNotBeenPassedAsAnAdditionalInterface(additionalInterfaces); @@ -49,7 +50,7 @@ private object GenerateTypeProxy(ICallRouter callRouter, Type typeToProxy, Type[ return proxy; } - private object GenerateDelegateProxy(ICallRouter callRouter, Type delegateType, Type[] additionalInterfaces, object[] constructorArguments) + private object GenerateDelegateProxy(ICallRouter callRouter, Type delegateType, Type[]? additionalInterfaces, object?[]? constructorArguments) { VerifyNoAdditionalInterfacesGivenForDelegate(additionalInterfaces); VerifyNoConstructorArgumentsGivenForDelegate(constructorArguments); @@ -84,8 +85,8 @@ private CastleForwardingInterceptor CreateForwardingInterceptor(ICallRouter call callRouter); } - private object CreateProxyUsingCastleProxyGenerator(Type typeToProxy, Type[] additionalInterfaces, - object[] constructorArguments, + private object CreateProxyUsingCastleProxyGenerator(Type typeToProxy, Type[]? additionalInterfaces, + object?[]? constructorArguments, IInterceptor[] interceptors, ProxyGenerationOptions proxyGenerationOptions) { @@ -128,7 +129,7 @@ private ProxyGenerationOptions GetOptionsToMixinCallRouterProvider(ICallRouter c return options; } - private static void VerifyNoConstructorArgumentsGivenForInterface(object[] constructorArguments) + private static void VerifyNoConstructorArgumentsGivenForInterface(object?[]? constructorArguments) { if (HasItems(constructorArguments)) { @@ -136,7 +137,7 @@ private static void VerifyNoConstructorArgumentsGivenForInterface(object[] const } } - private static void VerifyNoConstructorArgumentsGivenForDelegate(object[] constructorArguments) + private static void VerifyNoConstructorArgumentsGivenForDelegate(object?[]? constructorArguments) { if (HasItems(constructorArguments)) { @@ -144,7 +145,7 @@ private static void VerifyNoConstructorArgumentsGivenForDelegate(object[] constr } } - private static void VerifyNoAdditionalInterfacesGivenForDelegate(Type[] constructorArguments) + private static void VerifyNoAdditionalInterfacesGivenForDelegate(Type[]? constructorArguments) { if (HasItems(constructorArguments)) { @@ -154,7 +155,7 @@ private static void VerifyNoAdditionalInterfacesGivenForDelegate(Type[] construc } } - private static void VerifyClassHasNotBeenPassedAsAnAdditionalInterface(Type[] additionalInterfaces) + private static void VerifyClassHasNotBeenPassedAsAnAdditionalInterface(Type[]? additionalInterfaces) { if (additionalInterfaces != null && additionalInterfaces.Any(x => x.GetTypeInfo().IsClass)) { @@ -164,7 +165,7 @@ private static void VerifyClassHasNotBeenPassedAsAnAdditionalInterface(Type[] ad } } - private static bool HasItems(T[] array) + private static bool HasItems(T[]? array) { return array != null && array.Length > 0; } diff --git a/src/NSubstitute/Proxies/CastleDynamicProxy/CastleInvocationMapper.cs b/src/NSubstitute/Proxies/CastleDynamicProxy/CastleInvocationMapper.cs index 0b388ba7b..2c6192703 100644 --- a/src/NSubstitute/Proxies/CastleDynamicProxy/CastleInvocationMapper.cs +++ b/src/NSubstitute/Proxies/CastleDynamicProxy/CastleInvocationMapper.cs @@ -17,7 +17,7 @@ public CastleInvocationMapper(ICallFactory callFactory, IArgumentSpecificationDe public virtual ICall Map(IInvocation castleInvocation) { - Func baseMethod = null; + Func? baseMethod = null; if (castleInvocation.InvocationTarget != null && castleInvocation.MethodInvocationTarget.IsVirtual && !castleInvocation.MethodInvocationTarget.IsAbstract && diff --git a/src/NSubstitute/Proxies/CastleDynamicProxy/ProxyIdInterceptor.cs b/src/NSubstitute/Proxies/CastleDynamicProxy/ProxyIdInterceptor.cs index 44b4526c4..4337717cf 100644 --- a/src/NSubstitute/Proxies/CastleDynamicProxy/ProxyIdInterceptor.cs +++ b/src/NSubstitute/Proxies/CastleDynamicProxy/ProxyIdInterceptor.cs @@ -9,7 +9,7 @@ namespace NSubstitute.Proxies.CastleDynamicProxy public class ProxyIdInterceptor : IInterceptor { private readonly Type _primaryProxyType; - private string _cachedProxyId; + private string? _cachedProxyId; public ProxyIdInterceptor(Type primaryProxyType) { diff --git a/src/NSubstitute/Proxies/DelegateProxy/DelegateProxyFactory.cs b/src/NSubstitute/Proxies/DelegateProxy/DelegateProxyFactory.cs index 733e0d634..4a3c61546 100644 --- a/src/NSubstitute/Proxies/DelegateProxy/DelegateProxyFactory.cs +++ b/src/NSubstitute/Proxies/DelegateProxy/DelegateProxyFactory.cs @@ -22,7 +22,7 @@ public DelegateProxyFactory(CastleDynamicProxyFactory objectProxyFactory) _castleObjectProxyFactory = objectProxyFactory ?? throw new ArgumentNullException(nameof(objectProxyFactory)); } - public object GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, object[] constructorArguments) + public object GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[]? additionalInterfaces, object?[]? constructorArguments) { // Castle factory can now resolve delegate proxies as well. return _castleObjectProxyFactory.GenerateProxy(callRouter, typeToProxy, additionalInterfaces, constructorArguments); diff --git a/src/NSubstitute/Proxies/ProxyFactory.cs b/src/NSubstitute/Proxies/ProxyFactory.cs index f78873539..d8ecf957b 100644 --- a/src/NSubstitute/Proxies/ProxyFactory.cs +++ b/src/NSubstitute/Proxies/ProxyFactory.cs @@ -16,7 +16,7 @@ public ProxyFactory(IProxyFactory delegateFactory, IProxyFactory dynamicProxyFac _dynamicProxyFactory = dynamicProxyFactory; } - public object GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[] additionalInterfaces, object[] constructorArguments) + public object GenerateProxy(ICallRouter callRouter, Type typeToProxy, Type[]? additionalInterfaces, object?[]? constructorArguments) { var isDelegate = typeToProxy.IsDelegate(); return isDelegate diff --git a/src/NSubstitute/Raise.cs b/src/NSubstitute/Raise.cs index 8101bfd45..3e367c20d 100644 --- a/src/NSubstitute/Raise.cs +++ b/src/NSubstitute/Raise.cs @@ -4,6 +4,9 @@ using NSubstitute.Core; using NSubstitute.Core.Events; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute { public static class Raise diff --git a/src/NSubstitute/Received.cs b/src/NSubstitute/Received.cs index 4cd8c7552..888c04b45 100644 --- a/src/NSubstitute/Received.cs +++ b/src/NSubstitute/Received.cs @@ -2,6 +2,9 @@ using NSubstitute.Core; using NSubstitute.Core.SequenceChecking; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute { public class Received diff --git a/src/NSubstitute/Routing/AutoValues/AutoArrayProvider.cs b/src/NSubstitute/Routing/AutoValues/AutoArrayProvider.cs index 1c64f8e61..006a537b3 100644 --- a/src/NSubstitute/Routing/AutoValues/AutoArrayProvider.cs +++ b/src/NSubstitute/Routing/AutoValues/AutoArrayProvider.cs @@ -9,11 +9,11 @@ public bool CanProvideValueFor(Type type) return type.IsArray; } - public object GetValue(Type type) + public object? GetValue(Type type) { var rank = type.GetArrayRank(); var dimensionLengths = new int[rank]; - return Array.CreateInstance(type.GetElementType(), dimensionLengths); + return Array.CreateInstance(type.GetElementType()!, dimensionLengths); } } } \ No newline at end of file diff --git a/src/NSubstitute/Routing/AutoValues/AutoObservableProvider.cs b/src/NSubstitute/Routing/AutoValues/AutoObservableProvider.cs index 4ad7caa76..b7a432e23 100644 --- a/src/NSubstitute/Routing/AutoValues/AutoObservableProvider.cs +++ b/src/NSubstitute/Routing/AutoValues/AutoObservableProvider.cs @@ -20,7 +20,7 @@ public bool CanProvideValueFor(Type type) return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(IObservable<>); } - public object GetValue(Type type) + public object? GetValue(Type type) { if (!CanProvideValueFor(type)) throw new InvalidOperationException(); @@ -30,10 +30,10 @@ public object GetValue(Type type) var value = valueProvider == null ? GetDefault(type) : valueProvider.GetValue(innerType); return Activator.CreateInstance( typeof(ReturnObservable<>).MakeGenericType(innerType) - , new object[] { value }); + , new object?[] { value }); } - private static object GetDefault(Type type) + private static object? GetDefault(Type type) { return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null; } diff --git a/src/NSubstitute/Routing/AutoValues/AutoQueryableProvider.cs b/src/NSubstitute/Routing/AutoValues/AutoQueryableProvider.cs index f188a6e47..7b5f69f42 100644 --- a/src/NSubstitute/Routing/AutoValues/AutoQueryableProvider.cs +++ b/src/NSubstitute/Routing/AutoValues/AutoQueryableProvider.cs @@ -11,7 +11,7 @@ public bool CanProvideValueFor(Type type) return type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(IQueryable<>); } - public object GetValue(Type type) + public object? GetValue(Type type) { if (!CanProvideValueFor(type)) throw new InvalidOperationException(); diff --git a/src/NSubstitute/Routing/AutoValues/AutoTaskProvider.cs b/src/NSubstitute/Routing/AutoValues/AutoTaskProvider.cs index e6969daf3..a785d8f80 100644 --- a/src/NSubstitute/Routing/AutoValues/AutoTaskProvider.cs +++ b/src/NSubstitute/Routing/AutoValues/AutoTaskProvider.cs @@ -33,18 +33,18 @@ public object GetValue(Type type) var value = valueProvider == null ? GetDefault(type) : valueProvider.GetValue(taskType); var taskCompletionSourceType = typeof(TaskCompletionSource<>).MakeGenericType(taskType); var taskCompletionSource = Activator.CreateInstance(taskCompletionSourceType); - taskCompletionSourceType.GetMethod("SetResult").Invoke(taskCompletionSource, new[] { value }); - return taskCompletionSourceType.GetProperty("Task").GetValue(taskCompletionSource, null); + taskCompletionSourceType.GetMethod(nameof(TaskCompletionSource.SetResult))!.Invoke(taskCompletionSource, new[] {value}); + return taskCompletionSourceType.GetProperty(nameof(TaskCompletionSource.Task))!.GetValue( taskCompletionSource, null)!; } else { - var taskCompletionSource = new TaskCompletionSource(); + var taskCompletionSource = new TaskCompletionSource(); taskCompletionSource.SetResult(null); return taskCompletionSource.Task; } } - private static object GetDefault(Type type) + private static object? GetDefault(Type type) { return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null; } diff --git a/src/NSubstitute/Routing/AutoValues/AutoValueProvidersFactory.cs b/src/NSubstitute/Routing/AutoValues/AutoValueProvidersFactory.cs index d1eebf758..143220527 100644 --- a/src/NSubstitute/Routing/AutoValues/AutoValueProvidersFactory.cs +++ b/src/NSubstitute/Routing/AutoValues/AutoValueProvidersFactory.cs @@ -9,7 +9,7 @@ public class AutoValueProvidersFactory : IAutoValueProvidersFactory { public IReadOnlyCollection CreateProviders(ISubstituteFactory substituteFactory) { - IAutoValueProvider[] result = null; + IAutoValueProvider[]? result = null; var lazyResult = new Lazy>( () => result ?? throw new InvalidOperationException("Value was not constructed yet."), LazyThreadSafetyMode.PublicationOnly); diff --git a/src/NSubstitute/Routing/AutoValues/IAutoValueProvider.cs b/src/NSubstitute/Routing/AutoValues/IAutoValueProvider.cs index bd3bbda94..8eb3c9a6f 100644 --- a/src/NSubstitute/Routing/AutoValues/IAutoValueProvider.cs +++ b/src/NSubstitute/Routing/AutoValues/IAutoValueProvider.cs @@ -5,6 +5,6 @@ namespace NSubstitute.Routing.AutoValues public interface IAutoValueProvider { bool CanProvideValueFor(Type type); - object GetValue(Type type); + object? GetValue(Type type); } } \ No newline at end of file diff --git a/src/NSubstitute/Routing/Handlers/EventSubscriptionHandler.cs b/src/NSubstitute/Routing/Handlers/EventSubscriptionHandler.cs index 2f5f37310..4314c4fc0 100644 --- a/src/NSubstitute/Routing/Handlers/EventSubscriptionHandler.cs +++ b/src/NSubstitute/Routing/Handlers/EventSubscriptionHandler.cs @@ -49,7 +49,7 @@ private static void If(ICall call, Func> meetsThisSp // It's important to use original arguments, as it provides better performance. // It's safe to use original arguments here, as only by-ref arguments might be modified, // which should never happen for this case. - takeThisAction(matchingEvent.Name, call.GetOriginalArguments()[0]); + takeThisAction(matchingEvent.Name, call.GetOriginalArguments()[0]!); } } @@ -66,7 +66,7 @@ private static Predicate IsEventUnsubscription(ICall call) private static IEnumerable GetEvents(ICall call, Func> createPredicate) { var predicate = createPredicate(call); - return call.GetMethodInfo().DeclaringType.GetEvents().Where(x => predicate(x)); + return call.GetMethodInfo().DeclaringType!.GetEvents().Where(x => predicate(x)); } } } \ No newline at end of file diff --git a/src/NSubstitute/Routing/Handlers/RaiseEventHandler.cs b/src/NSubstitute/Routing/Handlers/RaiseEventHandler.cs index aa122c396..57dfa5013 100644 --- a/src/NSubstitute/Routing/Handlers/RaiseEventHandler.cs +++ b/src/NSubstitute/Routing/Handlers/RaiseEventHandler.cs @@ -9,9 +9,9 @@ namespace NSubstitute.Routing.Handlers public class RaiseEventHandler : ICallHandler { readonly IEventHandlerRegistry _eventHandlerRegistry; - readonly Func _getEventArguments; + readonly Func _getEventArguments; - public RaiseEventHandler(IEventHandlerRegistry eventHandlerRegistry, Func getEventArguments) + public RaiseEventHandler(IEventHandlerRegistry eventHandlerRegistry, Func getEventArguments) { _eventHandlerRegistry = eventHandlerRegistry; _getEventArguments = getEventArguments; @@ -20,11 +20,11 @@ public RaiseEventHandler(IEventHandlerRegistry eventHandlerRegistry, Func (x.GetAddMethod() == methodInfo) || (x.GetRemoveMethod() == methodInfo)); if (eventInfo == null) throw new CouldNotRaiseEventException(); var handlers = _eventHandlerRegistry.GetHandlers(eventInfo.Name); - var eventArguments = _getEventArguments(call); + object?[] eventArguments = _getEventArguments(call); foreach (Delegate handler in handlers) { try @@ -33,7 +33,7 @@ public RouteAction Handle(ICall call) } catch (TargetInvocationException e) { - throw e.InnerException; + throw e.InnerException!; } } return RouteAction.Continue(); diff --git a/src/NSubstitute/Routing/Handlers/ReturnAutoValue.cs b/src/NSubstitute/Routing/Handlers/ReturnAutoValue.cs index 1f90a3cdc..eaf4d6d15 100644 --- a/src/NSubstitute/Routing/Handlers/ReturnAutoValue.cs +++ b/src/NSubstitute/Routing/Handlers/ReturnAutoValue.cs @@ -48,7 +48,7 @@ public RouteAction Handle(ICall call) return RouteAction.Continue(); } - private object GetResultValueUsingProvider(ICall call, Type type, IAutoValueProvider provider) + private object? GetResultValueUsingProvider(ICall call, Type type, IAutoValueProvider provider) { var valueToReturn = provider.GetValue(type); if (_autoValueBehaviour == AutoValueBehaviour.UseValueForSubsequentCalls) diff --git a/src/NSubstitute/Routing/Handlers/ReturnFromAndConfigureDynamicCall.cs b/src/NSubstitute/Routing/Handlers/ReturnFromAndConfigureDynamicCall.cs index 7e982712f..e4fadd2c4 100644 --- a/src/NSubstitute/Routing/Handlers/ReturnFromAndConfigureDynamicCall.cs +++ b/src/NSubstitute/Routing/Handlers/ReturnFromAndConfigureDynamicCall.cs @@ -50,22 +50,22 @@ private bool ReturnsDynamic(ICall call) public class DynamicStub { - public ConfiguredCall Returns(T returnThis, params T[] returnThese) + public ConfiguredCall Returns(T? returnThis, params T?[] returnThese) { return default(T).Returns(returnThis, returnThese); } - public ConfiguredCall Returns(Func returnThis, params Func[] returnThese) + public ConfiguredCall Returns(Func returnThis, params Func[] returnThese) { return default(T).Returns(returnThis, returnThese); } - public ConfiguredCall ReturnsForAnyArgs(T returnThis, params T[] returnThese) + public ConfiguredCall ReturnsForAnyArgs(T? returnThis, params T?[] returnThese) { return default(T).ReturnsForAnyArgs(returnThis, returnThese); } - public ConfiguredCall ReturnsForAnyArgs(Func returnThis, params Func[] returnThese) + public ConfiguredCall ReturnsForAnyArgs(Func returnThis, params Func[] returnThese) { return default(T).ReturnsForAnyArgs(returnThis, returnThese); } diff --git a/src/NSubstitute/Routing/IRoute.cs b/src/NSubstitute/Routing/IRoute.cs index 80071976e..ec6fa819b 100644 --- a/src/NSubstitute/Routing/IRoute.cs +++ b/src/NSubstitute/Routing/IRoute.cs @@ -4,6 +4,6 @@ namespace NSubstitute.Routing { public interface IRoute { - object Handle(ICall call); + object? Handle(ICall call); } } \ No newline at end of file diff --git a/src/NSubstitute/Routing/IRouteFactory.cs b/src/NSubstitute/Routing/IRouteFactory.cs index 8638c4b1d..df8d9ffe4 100644 --- a/src/NSubstitute/Routing/IRouteFactory.cs +++ b/src/NSubstitute/Routing/IRouteFactory.cs @@ -11,7 +11,7 @@ public interface IRouteFactory IRoute DoWhenCalled(ISubstituteState state, Action doAction, MatchArgs matchArgs); IRoute DoNotCallBase(ISubstituteState state, MatchArgs matchArgs); IRoute CallBase(ISubstituteState state, MatchArgs matchArgs); - IRoute RaiseEvent(ISubstituteState state, Func getEventArguments); + IRoute RaiseEvent(ISubstituteState state, Func getEventArguments); IRoute RecordCallSpecification(ISubstituteState state); IRoute RecordReplay(ISubstituteState state); } diff --git a/src/NSubstitute/Routing/Route.cs b/src/NSubstitute/Routing/Route.cs index d0327338c..86ef84035 100644 --- a/src/NSubstitute/Routing/Route.cs +++ b/src/NSubstitute/Routing/Route.cs @@ -15,7 +15,7 @@ public Route(ICallHandler[] handlers) public IEnumerable Handlers => _handlers; - public object Handle(ICall call) + public object? Handle(ICall call) { // This is a hot method which is invoked frequently and has major impact on performance. // Therefore, the LINQ cycle was unwinded to for loop. diff --git a/src/NSubstitute/Routing/RouteFactory.cs b/src/NSubstitute/Routing/RouteFactory.cs index cb718201e..b9a57b02f 100644 --- a/src/NSubstitute/Routing/RouteFactory.cs +++ b/src/NSubstitute/Routing/RouteFactory.cs @@ -75,7 +75,7 @@ public IRoute CallBase(ISubstituteState state, MatchArgs matchArgs) , ReturnDefaultForReturnTypeHandler() }); } - public IRoute RaiseEvent(ISubstituteState state, Func getEventArguments) + public IRoute RaiseEvent(ISubstituteState state, Func getEventArguments) { return new Route(new ICallHandler[] { new ClearLastCallRouterHandler(_threadLocalContext) diff --git a/src/NSubstitute/Substitute.cs b/src/NSubstitute/Substitute.cs index 78d32b309..d1ca5f510 100644 --- a/src/NSubstitute/Substitute.cs +++ b/src/NSubstitute/Substitute.cs @@ -1,6 +1,9 @@ using System; using NSubstitute.Core; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute { /// diff --git a/src/NSubstitute/SubstituteExtensions.cs b/src/NSubstitute/SubstituteExtensions.cs index aae4ad01c..9bdd9f06b 100644 --- a/src/NSubstitute/SubstituteExtensions.cs +++ b/src/NSubstitute/SubstituteExtensions.cs @@ -7,6 +7,9 @@ using NSubstitute.Exceptions; using NSubstitute.ReceivedExtensions; +// Disable nullability for client API, so it does not affect clients. +#nullable disable annotations + namespace NSubstitute { public static class SubstituteExtensions @@ -225,6 +228,8 @@ private static ConfiguredCall ConfigureReturn(MatchArgs matchArgs, Func public static T Received(this T substitute) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + return substitute.Received(Quantity.AtLeastOne()); } @@ -233,6 +238,8 @@ public static T Received(this T substitute) where T : class /// public static T Received(this T substitute, int requiredNumberOfCalls) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + return substitute.Received(Quantity.Exactly(requiredNumberOfCalls)); } @@ -241,6 +248,8 @@ public static T Received(this T substitute, int requiredNumberOfCalls) where /// public static T DidNotReceive(this T substitute) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + return substitute.Received(Quantity.None()); } @@ -249,6 +258,8 @@ public static T DidNotReceive(this T substitute) where T : class /// public static T ReceivedWithAnyArgs(this T substitute) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + return substitute.ReceivedWithAnyArgs(Quantity.AtLeastOne()); } @@ -257,6 +268,8 @@ public static T ReceivedWithAnyArgs(this T substitute) where T : class /// public static T ReceivedWithAnyArgs(this T substitute, int requiredNumberOfCalls) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + return substitute.ReceivedWithAnyArgs(Quantity.Exactly(requiredNumberOfCalls)); } @@ -265,6 +278,8 @@ public static T ReceivedWithAnyArgs(this T substitute, int requiredNumberOfCa /// public static T DidNotReceiveWithAnyArgs(this T substitute) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + return substitute.ReceivedWithAnyArgs(Quantity.None()); } @@ -278,6 +293,8 @@ public static T DidNotReceiveWithAnyArgs(this T substitute) where T : class /// public static void ClearReceivedCalls(this T substitute) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + substitute.ClearSubstitute(ClearOptions.ReceivedCalls); } @@ -287,6 +304,8 @@ public static void ClearReceivedCalls(this T substitute) where T : class /// public static WhenCalled When(this T substitute, Action substituteCall) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; return new WhenCalled(context, substitute, substituteCall, MatchArgs.AsSpecifiedInCall); } @@ -297,6 +316,8 @@ public static WhenCalled When(this T substitute, Action substituteCall) /// public static WhenCalled When(this T substitute, Func substituteCall) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; return new WhenCalled(context, substitute, x => substituteCall(x), MatchArgs.AsSpecifiedInCall); } @@ -307,6 +328,8 @@ public static WhenCalled When(this T substitute, Func substituteC /// public static WhenCalled When(this TSubstitute substitute, Func> substituteCall) where TSubstitute : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; return new WhenCalled(context, substitute, x => substituteCall(x), MatchArgs.AsSpecifiedInCall); } @@ -317,6 +340,8 @@ public static WhenCalled When(this TSubstitut /// public static WhenCalled WhenForAnyArgs(this T substitute, Action substituteCall) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; return new WhenCalled(context, substitute, substituteCall, MatchArgs.Any); } @@ -327,6 +352,8 @@ public static WhenCalled WhenForAnyArgs(this T substitute, Action subst /// public static WhenCalled WhenForAnyArgs(this T substitute, Func substituteCall) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; return new WhenCalled(context, substitute, x => substituteCall(x), MatchArgs.Any); } @@ -337,6 +364,8 @@ public static WhenCalled WhenForAnyArgs(this T substitute, Func s /// public static WhenCalled WhenForAnyArgs(this TSubstitute substitute, Func> substituteCall) where TSubstitute : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + var context = SubstitutionContext.Current; return new WhenCalled(context, substitute, x => substituteCall(x), MatchArgs.Any); } @@ -346,9 +375,11 @@ public static WhenCalled WhenForAnyArgs(this /// public static IEnumerable ReceivedCalls(this T substitute) where T : class { + if (substitute == null) throw new NullSubstituteReferenceException(); + return SubstitutionContext .Current - .GetCallRouterFor(substitute) + .GetCallRouterFor(substitute!) .ReceivedCalls(); } @@ -374,7 +405,7 @@ private static ValueTask CompletedValueTask(T result) private static void ReThrowOnNSubstituteFault(Task task) { - if (task.IsFaulted && task.Exception.InnerExceptions.FirstOrDefault() is SubstituteException) + if (task.IsFaulted && task.Exception!.InnerExceptions.FirstOrDefault() is SubstituteException) { task.GetAwaiter().GetResult(); } @@ -382,7 +413,7 @@ private static void ReThrowOnNSubstituteFault(Task task) private static void ReThrowOnNSubstituteFault(ValueTask task) { - if (task.IsFaulted && task.AsTask().Exception.InnerExceptions.FirstOrDefault() is SubstituteException) + if (task.IsFaulted && task.AsTask().Exception!.InnerExceptions.FirstOrDefault() is SubstituteException) { task.GetAwaiter().GetResult(); }