-
-
Notifications
You must be signed in to change notification settings - Fork 540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support c# 8 nullable reference #1115
Comments
This is indeed an interesting area, which I'm following closely. If we add this, it should align with #1074. |
I don't know the implementation details. |
Be aware that these attributes will also not be available in .netstandard 2.0. |
Do you mean this? There're some BCL libraries, e.g. https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces . Maybe there will be one for those attributes. |
This comment:
If I we're to add the attributes, I would probably wait until there is more information available about how to use them in multi-targeting libraries. |
And apparently some folks managed to get it to work with .NET Standard. See https://twitter.com/ddoomen/status/1160171490081419271 |
So now that .NET Core 3.0 has officially launched yesterday, I was watching the "What's new in C# 8.0 - Part 1" by C# Language Designer Mads Torgersen, and towards the end (2:19:09) he mentions library writers. He recommends multi-targeting (FA already is), and manually enabling C# 8 by editing the project file. Not all features are guaranteed to work, but nullable reference types should, in his words, work just fine. I, personally, am very excited for this feature The clip I'm referring to (with timestamp) is this one: https://www.youtube.com/watch?v=W8yL8vRnUnA&t=8349 |
@jnyrup do we need to do anything here? |
I did play with it in https://github.com/jnyrup/fluentassertions/commits/nullableReferenceTypes It should at most be able to give compile-errors for FA users if we change an annotation which produces a warning and they have WarningsAsErrors enabled. |
I think the clue of this is about not annotations on types, but about attributes. I.e.
|
I'm having exactly the same issue with the same pattern as @POnakS. |
We are having the same issue with nullable reference types. Fixing it via attributes isn't trivial as the My proposal is to create an extra extension method using System;
using System.Diagnostics.CodeAnalysis;
using FluentAssertions.Primitives;
#nullable enable
namespace FluentAssertions
{
#if NETCOREAPP3_0
public static class AssertionNullableExtensions
{
/// <summary>
/// Asserts that the current object has been initialized.
/// </summary>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <paramref name="because" />.
/// </param>
public static AndConstraint<ObjectAssertions> ShouldNotBeNull(
[NotNull] this object? actualValue,
string because = "",
params object[] becauseArgs)
{
var result = actualValue.Should().NotBeNull(because, becauseArgs);
if (actualValue == null)
{
throw new ArgumentNullException(nameof(actualValue)); // Will never be thrown, needed only to trick the compiler
}
return result;
}
}
#endif
} This extension properly shutdowns the warning. object? someObject = null;
someObject.ShouldNotBeNull();
var result = someObject.ToString(); // No warnings here I know this isn't perfectly fitting into the API but seems like an acceptable compromise to me. WDYT? Let me know if it sounds good and I'll drop a PR (code is ready). EDIT: cc @jnyrup |
See draft PR -> #1689 |
This also works using the example test from #1689 /// <summary>
/// Returns an <see cref="ObjectAssertions"/> object that can be used to assert the
/// current <see cref="object"/>.
/// </summary>
[Pure]
public static ObjectAssertions Should([System.Diagnostics.CodeAnalysis.NotNull] this object actualValue)
{
return new ObjectAssertions(actualValue);
} |
Yes @dennisdoomen, it works but it’s a lie. Calling |
But when you use FA that's not relevant. That's why you use it in the first place. |
I see what you mean but it's wrong in my opinion. You are just suppressing the compiler null check without actually checking for null on your part. It's not very correct. User? user = null;
user.Should().NotBeOfType<SuperUser>(); // This wrongly disables the null check
user.IsEnabled.Should().BeTrue(); // This throws a NullReferenceException |
But all FA APIs are already prepared to check for that. So if So although I see what you're trying to accomplish, I don't think with the approach. |
If would have thought that The principle is still valid. The following code throws a [Fact]
public void Test1()
{
User? someObject = null;
User? someOtherObject = null;
someObject.Should().BeSameAs(someOtherObject);
someObject.IsEnabled.Should().BeTrue();
} As said in the first post, I understand that my proposed solution is not really in line with the FA API. I also don't like it a lot. It's not a big deal and we can in the worst case create this If you can think of a better way to fix this, even better. |
And this time it's correct, since you're trying to get a property from a |
Yes, I mean that your hack in the And again I understand your point about the API pollution. I unfortunately don't have a better idea to solve this annoyance without rethinking the API. |
I respect your choices and decisions. But it's a combination of the API choices we made in the past and the way Microsoft decided to implement nullable reference types. For now, I don't see any solution I feel comfortable with, other than just disabling by making |
I see. I totally understand your point of view. In this case, I'd just leave it alone. 😉 |
Not sure if it belongs here, but found another construct that need some love: object value = Func(); // usually from nuget package without nullable enabled ...
value.Should().BeNull(); // here i get 2x possible null reference exception:
// value can be null
// value.Should() can be null currently i can 'fix' it by calling it like this: value!.Should()!.BeNull(); // or value?.Should()?.BeNull(); but that just ... wrong 'simplest' fix would be to turn on nullable types in 'csproj' and fix all warnings that will popup ... |
@CzBuCHi your alternative: |
@scott-moxham oh .. i didnt noticed that one .... good catch .... -public static ObjectAssertions Should(this object actualValue) {
+public static ObjectAssertions Should(this object? actualValue) {
return new ObjectAssertions(actualValue);
} |
I regularly stumble on this when writing tests and would love to see @dennisdoomen's proposal to add Also, this post by Brad Wilson might be relevant. (It might also not be relevant, I haven't fully investigated.)
|
I'm having a hard time selling FA to my team because of this. It requires thousands of tests be sprinkled with From a technical perspective, adding In another project with another team, we ended up adding dozens of methods like |
I agree that a "real" solution in FluentAssertions would be great, but what's so bad with disabling the CS8602 diagnostic in test projects? |
Basically the same as for your porduction code. Why bother when you can run the code and see that there is null reference there? Bot jokes aside, you want this inspection in all your code, and especially in your tests - as your test code is not tested. |
you can verify your tests with dotnet stryker ... or more precisely stryker will tell you what parts of your code you can change without breaking any tests (aka untested code) |
Compelling example: ```csharp [Theory] [InlineData(true)] [InlineData(false)] public void Test1(bool currentUser) { IPrincipal principal = currentUser ? new WindowsPrincipal(WindowsIdentity.GetCurrent()) : new ClaimsPrincipal(); IIdentity? identity = principal.Identity; identity.Should().NotBeOfType<GenericIdentity>(); identity.IsAuthenticated.Should().BeFalse(); } ``` Fixes fluentassertions#1115
I just submitted #2380 that will address this issue. Here's a compelling example: [Theory]
[InlineData(true)]
[InlineData(false)]
public void Test1(bool currentUser)
{
IPrincipal principal = currentUser ? new WindowsPrincipal(WindowsIdentity.GetCurrent()) : new ClaimsPrincipal();
IIdentity? identity = principal.Identity;
identity.Should().NotBeOfType<GenericIdentity>();
identity.IsAuthenticated.Should().BeFalse();
} This test fails (as expected) with the following errors:
But most importantly, the CS8602 warning is gone (Dereference of a possibly null reference). No more need to rely on |
Compelling example: ```csharp [Theory] [InlineData(true)] [InlineData(false)] public void Test1(bool currentUser) { IPrincipal principal = currentUser ? new WindowsPrincipal(WindowsIdentity.GetCurrent()) : new ClaimsPrincipal(); IIdentity? identity = principal.Identity; identity.Should().NotBeOfType<GenericIdentity>(); identity.IsAuthenticated.Should().BeFalse(); } ``` Fixes fluentassertions#1115
Compelling example: ```csharp [Theory] [InlineData(true)] [InlineData(false)] public void Test1(bool currentUser) { IPrincipal principal = currentUser ? new WindowsPrincipal(WindowsIdentity.GetCurrent()) : new ClaimsPrincipal(); IIdentity? identity = principal.Identity; identity.Should().NotBeOfType<GenericIdentity>(); identity.IsAuthenticated.Should().BeFalse(); } ``` Fixes fluentassertions#1115
Compelling example: ```csharp [Theory] [InlineData(true)] [InlineData(false)] public void Test1(bool currentUser) { IPrincipal principal = currentUser ? new WindowsPrincipal(WindowsIdentity.GetCurrent()) : new ClaimsPrincipal(); IIdentity? identity = principal.Identity; identity.Should().NotBeOfType<GenericIdentity>(); identity.IsAuthenticated.Should().BeFalse(); } ``` Fixes fluentassertions#1115
Compelling example: ```csharp [Theory] [InlineData(true)] [InlineData(false)] public void Test1(bool currentUser) { IPrincipal principal = currentUser ? new WindowsPrincipal(WindowsIdentity.GetCurrent()) : new ClaimsPrincipal(); IIdentity? identity = principal.Identity; identity.Should().NotBeOfType<GenericIdentity>(); identity.IsAuthenticated.Should().BeFalse(); } ``` Fixes fluentassertions#1115
Compelling example: ```csharp [Theory] [InlineData(true)] [InlineData(false)] public void Test1(bool currentUser) { IPrincipal principal = currentUser ? new WindowsPrincipal(WindowsIdentity.GetCurrent()) : new ClaimsPrincipal(); IIdentity? identity = principal.Identity; identity.Should().NotBeOfType<GenericIdentity>(); identity.IsAuthenticated.Should().BeFalse(); } ``` Fixes fluentassertions#1115
…2380) * Add [NotNull] attribute on the Should() method of reference types Compelling example: ```csharp [Theory] [InlineData(true)] [InlineData(false)] public void Test1(bool currentUser) { IPrincipal principal = currentUser ? new WindowsPrincipal(WindowsIdentity.GetCurrent()) : new ClaimsPrincipal(); IIdentity? identity = principal.Identity; identity.Should().NotBeOfType<GenericIdentity>(); identity.IsAuthenticated.Should().BeFalse(); } ``` Fixes #1115 * Add entry in the release notes under improvements
Just found this dotnet/standard#1359 .
Maybe this lib can do similar things for c# 8 nullable reference.
The text was updated successfully, but these errors were encountered: