Skip to content
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 nullable reference types support (Sentry, Sentry.Protocol) #509

Merged
merged 24 commits into from Sep 18, 2020

Conversation

Tyrrrz
Copy link
Contributor

@Tyrrrz Tyrrrz commented Sep 15, 2020

Work in progress

Some notes:


There are two sides to nullable reference types. One is the metadata we provide to users as part of the published library, which helps them correct nullability issues in their integration with Sentry, the other is for static analysis within Sentry itself. While the former can be achieved on all .NET frameworks with backporting (using NuGet package Nullable), the latter can only be done with .NET Core 3.0+ or .NET Std 2.1+. The reason for that is, because earlier versions of the framework are not annotated with NRT attributes, enabling warnings on them will result in huge amount of false positives. So as a solution, the <Nullable> project property is set to enable only when targeting netstandard2.1, while on other targets it's set to annotations (which allows us to annotate, but doesn't produce warnings for our code). Since netstandard2.1 target wasn't there, I added it. This is the framework we should be using when developing in the IDE, so that we get proper nullability warnings/insights. As long as that target is also built on CI, we will get warnings there as well. The added bonus of this approach is that warnings are not duplicated per target either (otherwise we'd have one nullability warning per framework).


Note that NRTs works at compile time and not runtime and since it's not actually part of the type system but rather a mere annotation, it doesn't protect against NullReferenceExceptions. That said, I would suggest not to check for null and rely on static analysis to tell users when they're wrong. Thanks to backporting, NRTs should be available for users targeting older versions of the framework too. Ideally, we should also either hide nullable parameters behind overloads or default them to null so it's a bit more clear.


Some methods were easy enough to analyze in terms of nullability. Some were more difficult. Because there methods that rely a lot on mutability, it was hard to determine whether the value can be null or not at a specific point. In most cases I just marked such instances as nullable. It revealed many places where nulls were not handled. I added some TODOs for these.


I've noticed some types are designed as data contracts with public get/set properties. I see that some of these properties are meant to be required, but since it cannot be enforced at a type system level without a constructor (until C# 9 and records), I marked them as nullable. That's because you simply can't know whether they've been set to a valid value or not. Code that interacts with those types needs to be aware of that too.


There were some instances of defensive programming in the code base that checked method parameters for null even when they weren't really expected to be null. I marked those instances as nullable to be safe, but we should ideally avoid defensive programming and let type system and static analysis take care of such situations for us. In the future we should consider refactoring this.


I found that some method overloads can be simplified by using default parameter value of null where null was expected anyway. Unfortunately, removing overloads is a breaking change so I kept them and added a corresponding TODO to remember for the future.


As I was annotating stuff, I also cleaned up some XML docs by adding a period in the end where applicable (accessibility) and removing empty nodes (e.g. <returns></returns>).


With the advent of .NET 5 we should probably consider dropping runtime-specific target frameworks (i.e. net461 etc) and only keep .NET Standard.


Closes #229
Related to #503
Related to #504

@Tyrrrz Tyrrrz added Feature New feature or request Sentry labels Sep 15, 2020
@codecov-commenter
Copy link

codecov-commenter commented Sep 15, 2020

Codecov Report

Merging #509 into main will decrease coverage by 0.28%.
The diff coverage is 87.47%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #509      +/-   ##
==========================================
- Coverage   86.82%   86.53%   -0.29%     
==========================================
  Files         120      120              
  Lines        2998     3001       +3     
  Branches      685      687       +2     
==========================================
- Hits         2603     2597       -6     
  Misses        214      214              
- Partials      181      190       +9     
Impacted Files Coverage Δ
src/Sentry.Protocol/Package.cs 100.00% <ø> (ø)
src/Sentry.Protocol/SdkVersion.cs 100.00% <ø> (ø)
src/Sentry.Protocol/SentryId.cs 100.00% <ø> (ø)
src/Sentry.Protocol/SentryValues.cs 50.00% <0.00%> (ø)
src/Sentry/DsnAttribute.cs 100.00% <ø> (ø)
...entry/Extensibility/FormRequestPayloadExtractor.cs 66.66% <ø> (ø)
...try/Extensibility/SentryEventExceptionProcessor.cs 100.00% <ø> (ø)
...c/Sentry/Infrastructure/ConsoleDiagnosticLogger.cs 100.00% <ø> (ø)
src/Sentry/Infrastructure/DebugDiagnosticLogger.cs 0.00% <ø> (ø)
src/Sentry/Infrastructure/SystemClock.cs 100.00% <ø> (ø)
... and 63 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a183a39...1196e28. Read the comment docs.

@Tyrrrz Tyrrrz added this to In progress in 3.0 Sep 15, 2020
@Tyrrrz Tyrrrz changed the title Add nullable reference types support Add nullable reference types support (Sentry, Sentry.Protocol) Sep 16, 2020
@Tyrrrz Tyrrrz marked this pull request as ready for review September 16, 2020 16:34
Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass: Made it half way through.

src/Sentry.Protocol/BaseScopeExtensions.cs Show resolved Hide resolved
src/Sentry.Protocol/BaseScopeExtensions.cs Outdated Show resolved Hide resolved
src/Sentry.Protocol/Context/Contexts.cs Show resolved Hide resolved
src/Sentry/Extensibility/SentryStackTraceFactory.cs Outdated Show resolved Hide resolved
Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

90% done

src/Sentry.Protocol/Dsn.cs Outdated Show resolved Hide resolved
src/Sentry.Protocol/Request.cs Show resolved Hide resolved
src/Sentry/Extensibility/SentryStackTraceFactory.cs Outdated Show resolved Hide resolved
src/Sentry/HubExtensions.cs Outdated Show resolved Hide resolved
src/Sentry/Internal/MainExceptionProcessor.cs Outdated Show resolved Hide resolved
src/Sentry/Internal/MainExceptionProcessor.cs Outdated Show resolved Hide resolved
src/Sentry/Internal/SdkComposer.cs Outdated Show resolved Hide resolved
src/Sentry/Internal/SentryScopeManager.cs Show resolved Hide resolved
Tyrrrz and others added 7 commits September 17, 2020 16:22
Co-authored-by: Bruno Garcia <bruno@brunogarcia.com>
Co-authored-by: Bruno Garcia <bruno@brunogarcia.com>
src/Sentry/SentryClient.cs Outdated Show resolved Hide resolved
Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using the LOGAF scale here: https://blog.danlew.net/2020/04/15/the-logaf-scale/

src/Sentry.Protocol/CollectionsExtensions.cs Show resolved Hide resolved
Directory.Build.props Show resolved Hide resolved
Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 or 2 notes but LGTM, lets merge

/// <inheritdoc />
public SentryId CaptureEvent(SentryEvent @event, Scope scope = null)
public SentryId CaptureEvent(SentryEvent? @event, Scope? scope = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Event shouldn't really be nullable (even though the code checks for it below to avoid NPE).
I'll address it in a follow up PR

{
if (_options.SampleRate is float sample)
if (_options.SampleRate != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason to change this? Not sure why change from the pattern matching to null check and a call on the property again.

One of the reasons I prefer the pattern matching is that the value is available on the reference declared, as opposed to a second access to property, that could possibly have change in between the two reads if mutable.

I realize the amount of bikesheeding that C# has created by having so many ways to do the same thing

src/Sentry/SentryClient.cs Show resolved Hide resolved
@bruno-garcia bruno-garcia merged commit 31d19bb into main Sep 18, 2020
@bruno-garcia bruno-garcia deleted the feature/nrt branch September 18, 2020 19:20
@bruno-garcia bruno-garcia moved this from In progress to Done in 3.0 Sep 18, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature New feature or request
Projects
No open projects
3.0
  
Done
Development

Successfully merging this pull request may close these issues.

None yet

3 participants