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

[Analyzer] Adjust/simplify code for numeric IntPtr #74518

Open
buyaa-n opened this issue Aug 24, 2022 · 6 comments
Open

[Analyzer] Adjust/simplify code for numeric IntPtr #74518

buyaa-n opened this issue Aug 24, 2022 · 6 comments
Labels
api-approved API was approved in API review, it can be implemented area-Meta code-analyzer Marks an issue that suggests a Roslyn analyzer code-fixer Marks an issue that suggests a Roslyn code fixer help wanted [up-for-grabs] Good issue for external contributors
Milestone

Comments

@buyaa-n
Copy link
Member

buyaa-n commented Aug 24, 2022

Related to #72348

With numeric IntPtr feature code simplification opportunities now available that could be suggested by this analyzer

  • U?IntPtr.Zero0 or nu?int.Zero if it would introduce ambiguity
  // Flag:
  IntPtr intPtr = IntPtr.Zero;      // or nint intPtr1 = IntPtr.Zero;
  UIntPtr uintPtr1 = UIntPtr.Zero;  // nuint uintPtr1 = UIntPtr.Zero; 

  // Violations fixed
  nint intPtr1 = 0; // or void* intPtr1 = null; if it needs to be pointer
  nuint uintPtr1 = 0;
  • new U?IntPtr(x)x or (nu?int)x if it would introduce ambiguity, or unchecked((nu?int)x) if we are in a checked context.
  // Flag:
  IntPtr intPtr1 = new IntPtr(x);
  uintPtr1 = new UIntPtr(x);

  // Fixed
  nint intPtr1 = x; 
  uintPtr1 = (nuint)x;
  • x.ToU?Int32()checked((u?int)x) or (u?int)x if we are in a checked context
  // Flag:
  int i = intPtr1.ToInt32();
  uint ui = uintPtr1.ToUInt32();

  // Fixed
  int i = (int)intPtr1;
  uint ui = (uint)uintPtr1;
  • x.ToU?Int64()(u?long)x
  // Flag:
  long longValue = intPtr1.ToInt64();
  ulong uLongValue = uintPtr1.ToUInt64();

  // Fixed
  long longValue = (long)intPtr1;
  ulong uLongValue = (ulong)uintPtr1;
  • x.ToPointer()(void*) x
  // Flag:
  void* ptr1 = intPtr1.ToPointer();
  void* ptr2 = uintPtr1.ToPointer();

  // Fixed
  void* ptr1 = (void*)intPtr1;
  void* ptr2 = (void*)uintPtr1; 

Suggested severity : "Info"
Suggested category : "Style" or "Usage"

This analyzer should be triggered only if the underlying runtime supports numeric IntPtr feature, i.e. if System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr is available in corelib.

Other related questions:

  • In case the statement includes a variable declaration with U?IntPt, the type also could be converted to nu?int which also could be covered by IDE0049 should be updated to suggest converting IntPtr to nint and UIntPtr to nuint, so these scenarios might also could be added to that issue to fix all references with appropriate values.
  • Also, as @jkotas suggested it would be nice for the code-fixer to suggest replacing IntPtr with actual pointer types where the code is actually operating on pointers. It might be not easy to determine if actual pointer should be suggested instead of nu?int

CC @tannergooding @jeffhandley

@buyaa-n buyaa-n added code-analyzer Marks an issue that suggests a Roslyn analyzer code-fixer Marks an issue that suggests a Roslyn code fixer labels Aug 24, 2022
@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Aug 24, 2022
@ghost
Copy link

ghost commented Aug 24, 2022

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

Issue Details

Related to #72348 (comment)

With numeric IntPtr feature code simplification opportunities now available that could be suggested by this analyzer

  • U?IntPtr.Zero0 or (nu?int)0 if it would introduce ambiguity
  • new U?IntPtr(x)x or (nu?int)x if it would introduce ambiguity, or unchecked((nu?int)x) if we are in a checked context.
  • x.ToU?Int32()checked((u?int)x) or (u?int)x if we are in a checked context
  • x.ToU?Int64()(u?long)x
  • x.ToPointer()(void*) x

These analyzer should be triggered only if the underlying runtime supports numeric IntPtr feature, i.e. if System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr is available in corelib.

Suggested severity : "Info"
Suggested category : "Style" or "Usage"

Examples:

class Example
{
    int intValue;
    uint uintValue;

    void Test ()
    {
        // Examples to flag:
        IntPtr  intPtr1 = IntPtr.Zero;
        UIntPtr  uintPtr1 = UIntPtr.Zero;

        // Violations fixed
        intPtr1 = 0; 
        uintPtr1 = 0;


        // flag:
        intPtr1 = new IntPtr(intValue);
        uintPtr1 = new UIntPtr(uintValue);

        // fixed
        intPtr1 = intValue; 
        uintPtr1 = uintValue;


        // flag:
        intValue = intPtr1.ToInt32();
        uintValue = uintPtr1.ToUInt32();

        // fixed
        intValue = (int)intPtr1;
        uintValue = (uint)uintPtr1;


        // flag:
        long longValue = intPtr1.ToInt64();
        ulong uLongValue = uintPtr1.ToUInt64();

        // fixed
        longValue = intPtr1;
        uLongValue = uintPtr1;


        // flag:
        void* ptr1 = intPtr1.ToPointer();
        void* ptr2 = uintPtr1.ToPointer();

        // fixed
        ptr1 = (void*)intPtr1;
        ptr2 = (void*)uintPtr1;
    }
}

CC @tannergooding @jeffhandley

Author: buyaa-n
Assignees: -
Labels:

area-Meta, untriaged, code-analyzer, code-fixer

Milestone: -

@buyaa-n buyaa-n added this to the Future milestone Aug 24, 2022
@ghost ghost removed the untriaged New issue has not been triaged by the area owner label Aug 24, 2022
@buyaa-n buyaa-n added the api-ready-for-review API is ready for review, it is NOT ready for implementation label Aug 24, 2022
@buyaa-n buyaa-n modified the milestones: Future, 8.0.0 Nov 11, 2022
@iSazonov
Copy link
Contributor

iSazonov commented Dec 2, 2022

It would be great to have in .Net 8!

@GrabYourPitchforks
Copy link
Member

We did an unofficial (lacking quorum) review of this. Some notes:

  • Would we prefer to use nint.Zero instead of (nint)0 to avoid ambiguity at call sites where we can't use simply 0?
  • It's common enough for projects to compile debug in checked, release in unchecked. This could complicate the fixer's logic as to whether any given call site is checked or unchecked, since it could be influenced by external factors unknown to the fixer. Might be safest to have it insert an explicit checked(...) statement, regardless of what the fixer believes the current context is.
  • For conversions to void* and [u]long, no need to worry about checked / unchecked context. These conversions are guaranteed lossless so will never fail.

@buyaa-n
Copy link
Member Author

buyaa-n commented Jan 4, 2023

  • Would we prefer to use nint.Zero instead of (nint)0 to avoid ambiguity at call sites where we can't use simply 0?

Sounds good, updated the description accordingly

  • It's common enough for projects to compile debug in checked, release in unchecked. This could complicate the fixer's logic as to whether any given call site is checked or unchecked, since it could be influenced by external factors unknown to the fixer. Might be safest to have it insert an explicit checked(...) statement, regardless of what the fixer believes the current context is.

The operations coming from roslyn provide Checked property we could use for determining checked/unchecked context , I am not sure that covers the scenario you mentioned, most likely it is. Anyway, instead of trying to check the context and offer an appropriate fixer or always offering explicit checked(...) we could just offer the 2 options both: checked((u?int)x) and (u?int)x, and let the user choose.

  • For conversions to void* and [u]long, no need to worry about checked / unchecked context. These conversions are guaranteed lossless so will never fail.

Right, updated conversions to [u]long,

@bartonjs
Copy link
Member

Looks good as proposed. The only representative case that's not explicitly mentioned is var nint = IntPtr.Zero, which needs special handling because of var.

@bartonjs bartonjs added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Feb 28, 2023
@jeffhandley jeffhandley modified the milestones: 8.0.0, Future Aug 11, 2023
@buyaa-n buyaa-n added the help wanted [up-for-grabs] Good issue for external contributors label Apr 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-approved API was approved in API review, it can be implemented area-Meta code-analyzer Marks an issue that suggests a Roslyn analyzer code-fixer Marks an issue that suggests a Roslyn code fixer help wanted [up-for-grabs] Good issue for external contributors
Projects
None yet
Development

No branches or pull requests

5 participants