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

Implement DisableRuntimeMarshalling analyzer and code fixer #5822

Merged
merged 22 commits into from
Mar 18, 2022

Conversation

jkoritzinsky
Copy link
Member

@jkoritzinsky jkoritzinsky requested a review from a team as a code owner January 28, 2022 01:11
@jkoritzinsky
Copy link
Member Author

I meant to open this as a draft PR since it's not totally ready yet. My mistake.

int sizePassedInType = {|CA1421:Marshal.SizeOf(type)|};
int sizePassedInGeneric = {|CA1421:Marshal.SizeOf<T>()|};
int sizePassedInGenericUnmanaged = {|CA1421:Marshal.SizeOf<U>()|};
}
Copy link
Member

Choose a reason for hiding this comment

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

This tests whether we'll crash on accessing the first argument or not.

                "SizeOf" => invocation.TargetMethod.IsGenericMethod || invocation.Arguments[0].Value is ITypeOfOperation { TypeOperand.IsUnmanagedType: true },
Suggested change
}
int nonGenericNoParameter = Marshal.SizeOf();
}

Consider also adding the test for StructureToPtr

else if (operation.Arguments[0].Value is ITypeOfOperation { TypeOperand.IsUnmanagedType: true } typeOf)
{
addUnsafeToEnclosingMethod = true;
editor.ReplaceNode(syntax, SyntaxFactory.SizeOfExpression(GetTypeOfTypeSyntax((TypeOfExpressionSyntax)typeOf.Syntax)));
Copy link
Member

Choose a reason for hiding this comment

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

Consider handling trivia.

@mavasani
Copy link
Contributor

I meant to open this as a draft PR since it's not totally ready yet. My mistake.

@jkoritzinsky Is the PR ready for review after your latest commits? If not, can you please convert it to a draft? Thanks.

@jkoritzinsky jkoritzinsky marked this pull request as draft January 31, 2022 18:02
@codecov
Copy link

codecov bot commented Jan 31, 2022

Codecov Report

Merging #5822 (e1cd121) into main (9228fb6) will increase coverage by 0.00%.
The diff coverage is 96.42%.

@@            Coverage Diff            @@
##             main    #5822     +/-   ##
=========================================
  Coverage   96.02%   96.03%             
=========================================
  Files        1310     1318      +8     
  Lines      302825   304802   +1977     
  Branches     9572     9657     +85     
=========================================
+ Hits       290799   292718   +1919     
- Misses       9740     9768     +28     
- Partials     2286     2316     +30     

@jkoritzinsky jkoritzinsky marked this pull request as ready for review February 1, 2022 01:58
@jkoritzinsky
Copy link
Member Author

@mavasani this is ready for review.

@jkoritzinsky
Copy link
Member Author

@mavasani any more feedback on this PR?

// The first run fixes all the fixable diagnostics.
// Since there are still some (unfixable) diagnostics, the test infrastructure decides to run the fix-all provider again.
// The second run doesn't do anything, since the remaining diagnostics are unfixable.
// Setting NumberOfFixAllIterations to -2 specifies that the fix-all provider can be run up to 2 times as part of a test run.
Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you for the comment!

@@ -1382,6 +1382,7 @@ private static async Task VerifyCSCodeFixAsync(string source, string codeFix, bo

// Verify that there are some instances of the diagnostic that we can't fix.
test.FixedState.MarkupHandling = MarkupMode.Allow;
test.MarkupOptions = MarkupOptions.UseFirstDescriptor;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is good to know!

Copy link
Contributor

@mavasani mavasani left a comment

Choose a reason for hiding this comment

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

Overall LGTM. Have added minor comments to address. Feel free to merge after addressing/responding to them. Thanks.

Comment on lines 32 to 36
if (document.Project.CompilationOptions is CSharpCompilationOptions { AllowUnsafe: false })
{
// We can't code fix if unsafe code isn't allowed.
return await document.GetSyntaxRootAsync(fixAllContext.CancellationToken);
}
Copy link
Member

Choose a reason for hiding this comment

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

Is this redundant? This was already checked in RegisterCodeFixesAsync

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried turning this into an assert and it tripped on a few tests, so I guess it's not redundant.

{
IMethodSymbol method = (IMethodSymbol)context.Symbol;

DllImportData? dllImportData = method.GetDllImportData();
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised this didn't cause a crash due to:

https://github.com/dotnet/roslyn/blob/7e2966e180eef601cb0fc4a029d4f2093d22b639/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerMethodSymbol.cs#L829

Also surprised on roslyn's side there is an exception thrown in this code path instead of simply returning null. cc @333fred

Copy link
Member Author

Choose a reason for hiding this comment

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

We don't go down this path for function pointers. We go from AnalyzeFunctionPointerCall to AnalyzeMethodSignature directly.

Copy link
Member

Choose a reason for hiding this comment

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

FunctionPointerMethodSymbol is a method symbol. Shouldn't we get callbacks for it from context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);? then we meet method.GetDllImportData() unconditionally. I know this is likely not happening, otherwise a test would have crashed. But I don't yet see the reason

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we don't get callbacks for it for the same reason that we don't get callbacks for SymbolKind.Method for local functions (which I just added code to handle in my most recent commit). It seems that symbols that are introduced due to an IOperation are exposed through that operation in RegisterOperationAction instead of through RegisterSymbolAction.

Copy link
Member

Choose a reason for hiding this comment

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

Good to know! Thanks!

Copy link
Member

@Youssef1313 Youssef1313 left a comment

Choose a reason for hiding this comment

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

Do we want to report a diagnostic for properties?

e.g:

public static extern string P { [DllImport("abc")] get; }

I don't know if there will already be a diagnostic or not.

@jkoritzinsky
Copy link
Member Author

I didn't know that was even valid...

Yes, we should add the diagnostic there too.

@Youssef1313
Copy link
Member

Youssef1313 commented Mar 18, 2022

@jkoritzinsky Consider also:

using System;
using System.Runtime.InteropServices;

public class C
{
    [DllImport("abc")]
    public static extern int operator +(C a, C b);
    
    [method: DllImport("abc")]
    public extern static event Action G;
}

Note: those edge cases are being stolen from Roslyn tests 😄

@jkoritzinsky
Copy link
Member Author

This is so much sadness right now 😢

The way the event test case compiles down also makes me sad, but I guess I should add tests to validate behavior.

@jkoritzinsky
Copy link
Member Author

@mavasani I don't have permissions to merge, so can you hit the button for me?

@jmarolf jmarolf merged commit 0495618 into dotnet:main Mar 18, 2022
@github-actions github-actions bot added this to the vNext milestone Mar 18, 2022
@jkoritzinsky jkoritzinsky deleted the disableruntimemarshalling-analyzer branch March 18, 2022 23:15
@jmarolf jmarolf modified the milestones: vNext, .NET 7.x Nov 15, 2022
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.

Analyzer for System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute
7 participants