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

Do not pass mutable value types by value #5454

Open
wants to merge 21 commits into
base: main
Choose a base branch
from

Conversation

NewellClark
Copy link
Contributor

@NewellClark NewellClark commented Sep 7, 2021

Fix dotnet/runtime#33772.

As of right now, the analyzer reports all parameters that are typed as mutable value types and are not marked as ref or out. We also report all methods who's return type is a mutable value type that do not return by reference, unless the return type is a struct-enumerator type and the method's name is "GetEnumerator".
We report all properties of a mutable value type that do not return by reference.

I ran the analyzer against dotnet/runtime and found multiple false-positives in JsonElement.cs (EnumerateArray() and EnumerateObject()) and ThrowHelper.Serialization.cs.

There were false positive violations for both the return-values rule and the pass-by-value rule. All the return-value rule violations were factory methods. I think we should consider not reporting violations for method return values, as that would raise a false positive for any factory method, which seems like a fairly common scenario.
In the meantime, I've added an exception in the analyzer for methods named "GetEnumerator" that return a struct-enumerator type, as otherwise we'd get a huge number of false positives throughout dotnet/runtime.

All of the pass-by-value violations were in parameters, and I was able to determine that the methods do not call any mutating members on the arguments in question. We may want to consider not flagging in parameters. Unlike passing by-value, which is the default, it seems likely that any parameter marked as in was done so with the intention that the method will not be modifying it, and therefore is likely safe.

- Add CA2019 files
- Add CA2019 resources
- Suppress IDE0130 "Namespace does not match folder structure" warning for unit test project, since it flags literally every unit test file.
- Analyzer tests for known problematic types
Add tests ensuring fixer updates lvalue callsites.
- Minor adjustment to the span that is underlined for visual basic return value errors.
- Run msbuild
@NewellClark NewellClark requested a review from a team as a code owner September 7, 2021 20:32

using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Style", "IDE0130:Namespace does not match folder structure", Justification = "<Pending>", Scope = "namespace", Target = "~N:Microsoft.NetCore.CSharp.Analyzers.Runtime")]
Copy link
Member

Choose a reason for hiding this comment

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

❗ This repository doesn't use global suppressions. The rule can be configured in the appropriate .globalconfig file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@sharwell
Copy link
Member

sharwell commented Sep 7, 2021

I strongly believe that RS0042 (Do not copy value) is superior to this proposed rule in all aspects.

@sharwell
Copy link
Member

sharwell commented Sep 7, 2021

This is a really complicated analysis scenario. I ran into a large number of issues working in this space in the past and I'm not sure a pull request review is the right place to try and cover them all.

@NewellClark
Copy link
Contributor Author

This is a really complicated analysis scenario. I ran into a large number of issues working in this space in the past and I'm not sure a pull request review is the right place to try and cover them all.

Is there a slack or discord channel where we could discuss this further?

@codecov
Copy link

codecov bot commented Sep 8, 2021

Codecov Report

Merging #5454 (6a2807f) into main (b94f168) will increase coverage by 0.04%.
The diff coverage is 88.97%.

❗ Current head 6a2807f differs from pull request most recent head 9e2080a. Consider uploading reports for the commit 9e2080a to get more accurate results

@@            Coverage Diff             @@
##             main    #5454      +/-   ##
==========================================
+ Coverage   95.49%   95.53%   +0.04%     
==========================================
  Files        1217     1276      +59     
  Lines      277298   289016   +11718     
  Branches    16741    17384     +643     
==========================================
+ Hits       264817   276125   +11308     
- Misses      10280    10547     +267     
- Partials     2201     2344     +143     

@NewellClark
Copy link
Contributor Author

I strongly believe that RS0042 (Do not copy value) is superior to this proposed rule in all aspects.

@sharwell It looks like RS0042 does much more than what was specified here.

@sharwell
Copy link
Member

sharwell commented Sep 9, 2021

Yes, I'm still discussing this with the runtime team. It looks like some incorrect assumptions were made regarding Utf8JsonReader, and it's much easier to misuse than expected.

@@ -1620,6 +1620,30 @@ Number of parameters supplied in the logging message template do not match the n
|CodeFix|False|
---

## [CA2019](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2019): Do not pass mutable value types by value
Copy link
Member

Choose a reason for hiding this comment

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

For both of these, what is the plan for handling and/or annotating value types which explicitly should be passed and returned by value?

For example, this is going to have tons of false positives in interop code. It's likewise not going to be taking SIMD (Vector2, Vecto3, or Vector4) or ABI (Unix can pass a struct in multiple registers sometimes) into account where passing by ref or in is generally inappropriate and may hurt perf or cause other regressions. This may likewise flag things like Guid incorrectly on older TFMs given that it wasn't updated to be readonly until newer releases (even though it was "functionally readonly" already).

Copy link
Contributor Author

@NewellClark NewellClark Sep 19, 2021

Choose a reason for hiding this comment

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

@tannergooding As per this post, this analyzer only flags value types that are one of the following:

  • On a built-in list of problematic types (the list currently contains SpinLock and Utf8JsonReader)
  • Added as problematic mutable value types via an .editorconfig option,
  • Types that it determines to be struct-enumerator types (must be a nested value type who's name ends with "Enumerator").

Edit: also, for struct-enumerator types, we suppress the return-by-value warning if returned from a method named "GetEnumerator".

@jmarolf jmarolf changed the base branch from release/7.0.1xx to main September 28, 2021 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Do not pass Utf8JsonReader by value
3 participants