-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Roslyn Code analyzers for interop #37039
Comments
In most Win32 APIs, it is wrong to use |
That is definitely the case for the Win32 APIs themselves. However, when going through a p/invoke in .NET Core with |
Interesting, I didn't know that. It doesn't really solve the problem though. This code relies on undocumented behavior:
This code doesn't directly check the last error but is perfectly valid (
|
The spirit of the rule is that there is a path after the p/invoke where the last error is retrieved without another call in between. As your example points out, only expecting |
One issue which seems to have fallen through the tracks seems to be #33808 which is not referenced in the todo list if I read this correctly |
Thanks @Mrnikbobjeff - updated the list. |
Are these rules already approved when they are in the list? Would a PR be accepted for them or do they individually require approval? |
@Mrnikbobjeff Great question. This issue is for tracking interop analyzers as an initiative and doesn't represent specific approval for any of them. Each analyzer requires a separate API review via a new issue. Some however have had API reviews and been approved - see #35695 for "Prefer If so incline, anyone in the community can submit a PR for the existing approved but not completed analyzer above (#35695) and/or submit new issues marked with |
For the GetLastError heuristic some things are not clear to me. How would one arrive at a list of methods calling setlasterror. I thought about how other languages might approach this, as I had already written a Java bytecode analyzer which finds method calls inside of a function. Build the list up transitively and one would know which methods call SetLastError. As this is not viable in ASM and SetLastError is documented on a per function basis I have no idea on creating the desired list. Are there any approaches already considered? |
@Mrnikbobjeff Unfortunately there isn't. Any Win32 API could call another Win32 API that call The TL;DR of the hand-wavy statements above is the only way to "know" is to follow the official documentation or guess based on the function signature. |
@elinor-fung Looks like this other approved proposal belongs to this list: |
@carlossanlop This issue is listing out a backlog of interop-related analyzers. That one looks to be more general performance. I don't believe we do a tracking list for general runtime/performance analyzers. |
This tracking issue for a small backlog of rules around interop to add to the officially recommended set of analyzers. The rules are grouped around runtime functionality/features, but would represent separate analyzers in implementation. Since many issues concerning interop are rooted in a discrepancy between the user's intent and implementation, it is expected that only a small number of rules would be useful and applicable to all users. Official documentation will remain the main source of guidance for more nuanced behaviour that requires clear understanding of intent.
The upcoming plan to provide source generators for p/invokes should not directly affect the rules here, as that functionality would be using a different attribute for declaring p/invokes. However, there is the opportunity to provide an analyzer to help users migrate to the source generator approach. Once a source generator exists, the rules here could also be updated to consider p/invokes which use that generator or, depending on their severity, warnings within the generator itself.
The intent is that as the runtime interop team works through a backlog of analyzers around existing features and functionality, the concept of considering and adding analyzers for any new features will become natural and simply be part of that feature work. The P/Invokes and Marshalling rules are expected to be the starting points for working through this backlog.
P/Invokes
These rules inspect the actual declaration and invocation of p/invokes.
[Out]
string for P/Invokes: Do not use [Out] string for P/Invokes #35692StringBuilder
parameters for P/Invokes: Avoid StringBuilder parameters for P/Invokes #35693ExactSpelling=true
in[DllImport]
for known APIs: Prefer ExactSpelling=true on [DllImport] for known APIs #35695[DllImport]
declaration Remove redundant configuration from [DllImport] declaration #33808SafeHandle
overIntPtr
for known APIs: Analyzer suggestion: Prefer SafeHandle over IntPtr #42404SetLastError=true
Marshal.GetLastWin32Error
should be called to retrieve the last error.SetLastError=true
in[DllImport]
for known APIsSetLastError=true
should be specified. This would require having/building a database of APIs to compare against.Marshalling
These rules inspect how data is marshalled. They would apply to the parameters on p/invokes and any types (e.g. marshalled structs, delegates) used by p/invokes.
SizeConst
when marshalling as ByValArray: Specify SizeConst when marshalling field as ByValArray #36134Delegate
orMulticastDelegate
fields in marshalled structsDelegate
andMulticastDelegate
do not have a required signature, so they do not guarantee that the delegate passed in will match the signature the native code expects. Marshalling a struct containing aDelegate
orMulticastDelegate
from its native representation to a managed object can destabilize the runtime if the value of the field in the native representation is not a function pointer that wraps a managed delegate. Use a specific delegate type instead ofDelegate
orMulticastDelegate
.ICustomMarshaler
GetInstance()
requirement - Analyzer proposal: Missing GetInstance forICustomMarshaler
s #46521COM
These rules inspect COM-related functionality. Many of the existing rules that have not yet been ported are around COM.
[ComImport]
and[ComVisible(true)]
ComVisible(true)
should be public, non-abstract, and have a public parameterless constructor.ComImport
– Warning for partial ComImport types with members in more than one type part #59013.Low-level interop APIs
These rules are related to APIs that provide low-level interaction/integration with the runtime's interop system.
DynamicInterfaceCastableImplementationAttribute
should provide a default implementation of all inherited interface methods: All inherited interface members should be implemented on an interface marked with DynamicInterfaceCastableImplementationAttribute #41529Cross-platform
There is an existing proposal for an analyzer that detects the use of platform-specific APIs where the API might not be available. Many interop-related APIs are platform-specific and would be given the appropriate attribute such that the proposed analyzer would flag their use.
The logic that will be used by the proposed analyzer for platform-specific APIs could be leveraged for platform-specifc interop behaviour that is not tied to a specifid API. There are some types or
MarshalAs
values for which marshalling is not supported on all platforms. In these cases, it is not a known API that is platform-specific, but a user-defined p/invoke that is platform-specific due to the way it is defined. An analyzer would detect those p/invokes and treat those as platform-specific calls, following the same logic as that for the platform-specific APIs for determining the platform context and checking for platform guards around the call site.Rules:
<Type>
requires<OS>
<UnmanagedType.*>
requires<OS>
Existing rules
There are a number of rules around interop and marshalling that go through legacy (static) analysis. They have all previously been deprecated or slated to be ported as time allows. See FxCop rule port status for a list of all ported, tracked, and deprecated rules.
The text was updated successfully, but these errors were encountered: