-
Notifications
You must be signed in to change notification settings - Fork 733
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
Add attributes to Assert.NotNull method to tell compiler input is checked for null. #3677
Conversation
Please take a look at this comment. Such attributes were never added, because NUnit does not guarantee that it will fail in case of null argument - because it might be inside Assert.Multiple block. |
@Dreamescaper Yes, I know NUnit doesn't throw inside an Yes for:
But not:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The team needs to have a conversation if we want to change the decision made at #3473 (comment). That's something I'm open to thinking about because I'm amenable to arguments based on multiple asserts being a sufficiently negligible pitfall. There would then be a lot of new annotations to add, including [MaybeNull]
on the Assert.Null methods, [DoesNotReturn]
on Assert.Fail, and multiple [NotNullIfNotNull(...)]
on equality asserts.
/// Verifies that the object that is passed in is not equal to <see langword="null"/>. Returns without throwing an | ||
/// exception when inside a multiple assert block. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note this documentation I added to explain why we decided not to add [NotNull]
.
@rprouse Where would you recommend that we have this discussion? |
@jnm2 let's have a side discussion in Slack then bring it back here after we chat. |
public static T AssertNotNull<T>(this T? source) | ||
where T : class | ||
{ | ||
Assert.NotNull(source); | ||
|
||
return source; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just noticed these new extension method Assert APIs. This is something we should also discuss. If we add these, we should add T : struct
copies of these methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we change the language version to C#9 we can drop the T : class
constraint and it will work for both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return type would not be as convenient if we did that though. T?
means "defaultable" rather than "nullable" if there is no class or struct constraint.
Having to do nullableValueType.AssertNotNull().Value
would be unpleasant to me since we can avoid making the .Value
part (logically an assertion itself) necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I stand corrected. Thanks. Disappointing that even though the return type is T
it still returns T?
I thought C#9 fixed this.
As constraints are still not part of the signature we cannot have two versions, one with where T : class
and one with where T : struct
. However, I managed to make the 2nd by explicitly declaring it as:
public static T AssertNotNull<T>(this Nullable<T> source)
where T : struct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We actually can have both versions with the same name (method overloads) because the signature is still different for the T : struct
version when the parameter is T?
. Nullable<T>
and T?
are only different at the syntax level when T : struct
, not different in meaning.
This is how I did it for Shouldly: https://github.com/shouldly/shouldly/blob/master/src/Shouldly/ShouldlyExtensionMethods/ShouldBeNullExtensions.cs#L21
The reason that C# 9 didn't fix this the way people hoped was that there is no sequence of bytes the compiler can emit that would work that way. The .NET runtime would have to gain a new feature and have a new way to encode ECMA-335 metadata that would not be compatible with previous runtimes and versions.
(The ability to call overloads like this is a newish C# compiler feature IIRC, but IIRC it was backported to all previous language versions. Don't quote me on this.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct again. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries, glad I can help.
I had a long discussion with @jnm2 about this in Slack. I'm okay with changing the legacy asserts in this way, just not adding new ones, so that part isn't a problem. My issue is that this is a partial fix that cannot have a complete fix within the framework. If we do this for I think that we could handle all of these things in the Analyzer project, so it is the best place for that. Unfortunately, there are also some technical difficulties there, so let's leave this PR open for now and see how the work goes in the Analyzer project. I'll post this comment to the issue too so that if we close this PR, it will also be tracked there. |
@rprouse I have implemented the DiagnosticSuppressor in NUnit.Analyzers and it works a charm. |
Partially fixes #3376