-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Report a diagnostic for return types with HResult-like named structur…
…es and provide a code-fix to do the correct marshalling (#90282)
- Loading branch information
1 parent
2d03dc4
commit d303781
Showing
30 changed files
with
581 additions
and
14 deletions.
There are no files selected for viewing
79 changes: 79 additions & 0 deletions
79
...Runtime.InteropServices/gen/ComInterfaceGenerator/Analyzers/AddMarshalAsToElementFixer.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.Editing; | ||
|
||
namespace Microsoft.Interop.Analyzers | ||
{ | ||
[ExportCodeFixProvider(LanguageNames.CSharp), Shared] | ||
public sealed class AddMarshalAsToElementFixer : CodeFixProvider | ||
{ | ||
public override FixAllProvider? GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | ||
|
||
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(GeneratorDiagnostics.Ids.NotRecommendedGeneratedComInterfaceUsage); | ||
|
||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
// Get the syntax root and semantic model | ||
Document doc = context.Document; | ||
SyntaxNode? root = await doc.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
if (root == null) | ||
return; | ||
|
||
SyntaxNode node = root.FindNode(context.Span); | ||
|
||
foreach (var diagnostic in context.Diagnostics) | ||
{ | ||
if (!diagnostic.Properties.TryGetValue(GeneratorDiagnosticProperties.AddMarshalAsAttribute, out string? addMarshalAsAttribute)) | ||
{ | ||
continue; | ||
} | ||
|
||
foreach (var unmanagedType in addMarshalAsAttribute.Split(',')) | ||
{ | ||
string unmanagedTypeName = unmanagedType.Trim(); | ||
context.RegisterCodeFix( | ||
CodeAction.Create( | ||
$"Add [MarshalAs(UnmanagedType.{unmanagedTypeName})]", | ||
async ct => | ||
{ | ||
DocumentEditor editor = await DocumentEditor.CreateAsync(doc, ct).ConfigureAwait(false); | ||
|
||
SyntaxGenerator gen = editor.Generator; | ||
|
||
SyntaxNode marshalAsAttribute = gen.Attribute( | ||
TypeNames.System_Runtime_InteropServices_MarshalAsAttribute, | ||
gen.AttributeArgument( | ||
gen.MemberAccessExpression( | ||
gen.DottedName(TypeNames.System_Runtime_InteropServices_UnmanagedType), | ||
gen.IdentifierName(unmanagedTypeName.Trim())))); | ||
|
||
if (node.IsKind(SyntaxKind.MethodDeclaration)) | ||
{ | ||
editor.AddReturnAttribute(node, marshalAsAttribute); | ||
} | ||
else | ||
{ | ||
editor.AddAttribute(node, marshalAsAttribute); | ||
} | ||
|
||
return editor.GetChangedDocument(); | ||
}, | ||
$"AddUnmanagedType.{unmanagedTypeName}"), | ||
diagnostic); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
...InteropServices/gen/ComInterfaceGenerator/Marshallers/StructAsHResultMarshallerFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; | ||
|
||
namespace Microsoft.Interop | ||
{ | ||
internal sealed class StructAsHResultMarshallerFactory : IMarshallingGeneratorFactory | ||
{ | ||
private static readonly Marshaller s_marshaller = new(); | ||
|
||
private readonly IMarshallingGeneratorFactory _inner; | ||
|
||
public StructAsHResultMarshallerFactory(IMarshallingGeneratorFactory inner) | ||
{ | ||
_inner = inner; | ||
} | ||
|
||
public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context) | ||
{ | ||
// Value type with MarshalAs(UnmanagedType.Error), to be marshalled as an unmanaged HRESULT. | ||
if (info is { ManagedType: ValueTypeInfo, MarshallingAttributeInfo: MarshalAsInfo(UnmanagedType.Error, _) }) | ||
{ | ||
return ResolvedGenerator.Resolved(s_marshaller); | ||
} | ||
|
||
return _inner.Create(info, context); | ||
} | ||
|
||
private sealed class Marshaller : IMarshallingGenerator | ||
{ | ||
public ManagedTypeInfo AsNativeType(TypePositionInfo info) => SpecialTypeInfo.Int32; | ||
|
||
public IEnumerable<StatementSyntax> Generate(TypePositionInfo info, StubCodeContext context) | ||
{ | ||
var (managed, unmanaged) = context.GetIdentifiers(info); | ||
|
||
switch (context.CurrentStage) | ||
{ | ||
case StubCodeContext.Stage.Marshal: | ||
if (MarshallerHelpers.GetMarshalDirection(info, context) is MarshalDirection.ManagedToUnmanaged or MarshalDirection.Bidirectional) | ||
{ | ||
// unmanaged = Unsafe.BitCast<managedType, int>(managed); | ||
yield return ExpressionStatement( | ||
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, | ||
IdentifierName(unmanaged), | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
ParseTypeName(TypeNames.System_Runtime_CompilerServices_Unsafe), | ||
GenericName(Identifier("BitCast"), | ||
TypeArgumentList( | ||
SeparatedList( | ||
new[] | ||
{ | ||
info.ManagedType.Syntax, | ||
AsNativeType(info).Syntax | ||
})))), | ||
ArgumentList(SingletonSeparatedList(Argument(IdentifierName(managed))))))); | ||
} | ||
break; | ||
case StubCodeContext.Stage.Unmarshal: | ||
if (MarshallerHelpers.GetMarshalDirection(info, context) is MarshalDirection.UnmanagedToManaged or MarshalDirection.Bidirectional) | ||
{ | ||
// managed = Unsafe.BitCast<int, managedType>(unmanaged); | ||
yield return ExpressionStatement( | ||
AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, | ||
IdentifierName(managed), | ||
InvocationExpression( | ||
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, | ||
ParseTypeName(TypeNames.System_Runtime_CompilerServices_Unsafe), | ||
GenericName(Identifier("BitCast"), | ||
TypeArgumentList( | ||
SeparatedList( | ||
new[] | ||
{ | ||
AsNativeType(info).Syntax, | ||
info.ManagedType.Syntax | ||
})))), | ||
ArgumentList(SingletonSeparatedList(Argument(IdentifierName(unmanaged))))))); | ||
} | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
public SignatureBehavior GetNativeSignatureBehavior(TypePositionInfo info) | ||
{ | ||
return info.IsByRef ? SignatureBehavior.PointerToNativeType : SignatureBehavior.NativeType; | ||
} | ||
|
||
public ValueBoundaryBehavior GetValueBoundaryBehavior(TypePositionInfo info, StubCodeContext context) | ||
{ | ||
if (info.IsByRef) | ||
{ | ||
return ValueBoundaryBehavior.AddressOfNativeIdentifier; | ||
} | ||
|
||
return ValueBoundaryBehavior.NativeIdentifier; | ||
} | ||
|
||
public bool IsSupported(TargetFramework target, Version version) => target == TargetFramework.Net && version.Major >= 8; | ||
|
||
public ByValueMarshalKindSupport SupportsByValueMarshalKind(ByValueContentsMarshalKind marshalKind, TypePositionInfo info, StubCodeContext context, out GeneratorDiagnostic? diagnostic) | ||
=> ByValueMarshalKindSupportDescriptor.Default.GetSupport(marshalKind, info, context, out diagnostic); | ||
|
||
public bool UsesNativeIdentifier(TypePositionInfo info, StubCodeContext context) => true; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.