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 DoNotPassStructToArgumentNullExceptionThrowIfNullAnalyzer #6815

Merged
merged 11 commits into from
Feb 23, 2024

Conversation

CollinAlpert
Copy link
Contributor

# Conflicts:
#	src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
#	src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt
@CollinAlpert CollinAlpert requested a review from a team as a code owner July 28, 2023 13:11
@codecov
Copy link

codecov bot commented Jul 28, 2023

Codecov Report

Attention: 16 lines in your changes are missing coverage. Please review.

Comparison is base (ab13ac7) 96.45% compared to head (b3eebbb) 96.46%.

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #6815    +/-   ##
========================================
  Coverage   96.45%   96.46%            
========================================
  Files        1422     1427     +5     
  Lines      340585   341582   +997     
  Branches    11230    11246    +16     
========================================
+ Hits       328511   329503   +992     
+ Misses       9236     9234     -2     
- Partials     2838     2845     +7     

@stephentoub
Copy link
Member

Thanks, @CollinAlpert.

I suggest we broaden this slightly. The reason structs don't make sense here is because they'll be boxed to objects and then will never be null, making the whole operation an expensive nop. That same reasoning applies to other misuse, as well, for example accidentally passing a nameof(...) to ThrowIfNull (dotnet/runtime#89751 (comment)), or passing new Class(). Basically, make this analyzer about catching non-sensical uses of ThrowIfNull where the argument is provably never null by the compiler. (It should not rely on nullable annotations.) So, boxing structs, nameof, new Class(...), and anything else we can think of that's provably never null.

var typeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation);
var throwIfNullMethod = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemArgumentNullException)
?.GetMembers("ThrowIfNull")
.FirstOrDefault(m => m is IMethodSymbol method && method.Parameters[0].Type.SpecialType == SpecialType.System_Object);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.FirstOrDefault(m => m is IMethodSymbol method && method.Parameters[0].Type.SpecialType == SpecialType.System_Object);
.FirstOrDefault(m => m is IMethodSymbol method && method.Parameters.Length > 0 && method.Parameters[0].Type.SpecialType == SpecialType.System_Object);

Copy link
Member

@sharwell sharwell Aug 3, 2023

Choose a reason for hiding this comment

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

You could go further if you want:

m is IMethodSymbol { Parameters: [{ Type.SpecialType: SpecialType.System_Object }, ..] }

# Conflicts:
#	src/NetAnalyzers/RulesMissingDocumentation.md
@CollinAlpert
Copy link
Contributor Author

@stephentoub @Youssef1313 Thank you for your feedback, I have addressed it and added appropriate tests.

@mpidash
Copy link
Contributor

mpidash commented Sep 19, 2023

Seems like CA1869 (and CA1870) got used so this analyzer has to move to CA1871.

<target state="new">Do not call 'ArgumentNullException.ThrowIfNull' with a struct</target>
<note />
</trans-unit>
<trans-unit id="DoNotPassNullableStructToArgumentNullExceptionThrowIfNullDescription">
Copy link
Member

Choose a reason for hiding this comment

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

❓ Do we really need this? SharpLab suggests that release builds will not box a non-null value in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can you share an example? I can see the box instruction here.

# Conflicts:
#	src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
#	src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
#	src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md
#	src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt
# Conflicts:
#	src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
#	src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
#	src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md
#	src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif
#	src/NetAnalyzers/RulesMissingDocumentation.md
Copy link
Member

@buyaa-n buyaa-n left a comment

Choose a reason for hiding this comment

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

Sorry for delayed review @CollinAlpert, left some comments/questions, mostly NITs. Overall the PR looks great, thank you!

Testing with runtime got 1 valid hit, the analyzer/fixer works well in functional testing.

# Conflicts:
#	src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md
#	src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md
#	src/NetAnalyzers/RulesMissingDocumentation.md
Copy link
Member

@buyaa-n buyaa-n left a comment

Choose a reason for hiding this comment

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

LGTM, thank you @CollinAlpert!

@buyaa-n buyaa-n merged commit ba8b7f2 into dotnet:main Feb 23, 2024
11 checks passed
@CollinAlpert CollinAlpert deleted the issue_85154 branch February 23, 2024 06:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Warning for ArgumentNullException.ThrowIfNull when passing a struct
6 participants