Skip to content

Commit 5707668

Browse files
authored
Code fix to add DisableRuntimeMarshalling when required (#67676)
1 parent 6ea5fa5 commit 5707668

File tree

8 files changed

+299
-20
lines changed

8 files changed

+299
-20
lines changed

eng/Versions.props

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@
4141
<PropertyGroup>
4242
<!-- Code analysis dependencies -->
4343
<MicrosoftCodeAnalysisAnalyzersVersion>3.3.3</MicrosoftCodeAnalysisAnalyzersVersion>
44-
<MicrosoftCodeAnalysisCSharpCodeStyleVersion>4.2.0-2.final</MicrosoftCodeAnalysisCSharpCodeStyleVersion>
45-
<MicrosoftCodeAnalysisCSharpWorkspacesVersion>4.2.0-2.final</MicrosoftCodeAnalysisCSharpWorkspacesVersion>
46-
<MicrosoftCodeAnalysisCSharpVersion>4.2.0-2.final</MicrosoftCodeAnalysisCSharpVersion>
44+
<MicrosoftCodeAnalysisCSharpCodeStyleVersion>4.3.0-1.22206.2</MicrosoftCodeAnalysisCSharpCodeStyleVersion>
45+
<MicrosoftCodeAnalysisCSharpWorkspacesVersion>4.3.0-1.22206.2</MicrosoftCodeAnalysisCSharpWorkspacesVersion>
46+
<MicrosoftCodeAnalysisCSharpVersion>4.3.0-1.22206.2</MicrosoftCodeAnalysisCSharpVersion>
4747
<MicrosoftCodeAnalysisNetAnalyzersVersion>7.0.0-preview1.22207.3</MicrosoftCodeAnalysisNetAnalyzersVersion>
48-
<MicrosoftCodeAnalysisVersion>4.2.0-2.final</MicrosoftCodeAnalysisVersion>
48+
<MicrosoftCodeAnalysisVersion>4.3.0-1.22206.2</MicrosoftCodeAnalysisVersion>
4949
<!--
5050
TODO: Remove pinned version once arcade supplies a 4.3 compiler.
5151
-->
@@ -173,7 +173,7 @@
173173
<FsCheckVersion>2.14.3</FsCheckVersion>
174174
<!-- Uncomment to set a fixed version, else the latest is used -->
175175
<!--<SdkVersionForWorkloadTesting>7.0.100-preview.3.22151.18</SdkVersionForWorkloadTesting>-->
176-
<CompilerPlatformTestingVersion>1.1.2-beta1.22122.4</CompilerPlatformTestingVersion>
176+
<CompilerPlatformTestingVersion>1.1.2-beta1.22205.2</CompilerPlatformTestingVersion>
177177
<!-- Docs -->
178178
<MicrosoftPrivateIntellisenseVersion>7.0.0-preview-20220331.1</MicrosoftPrivateIntellisenseVersion>
179179
<!-- ILLink -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Collections.Immutable;
7+
using System.Composition;
8+
using System.Linq;
9+
using System.Text;
10+
using System.Threading;
11+
using System.Threading.Tasks;
12+
using Microsoft.CodeAnalysis;
13+
using Microsoft.CodeAnalysis.CodeActions;
14+
using Microsoft.CodeAnalysis.CodeFixes;
15+
using Microsoft.CodeAnalysis.Editing;
16+
17+
namespace Microsoft.Interop.Analyzers
18+
{
19+
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
20+
public class AddDisableRuntimeMarshallingAttributeFixer : CodeFixProvider
21+
{
22+
private const string EquivalenceKey = nameof(AddDisableRuntimeMarshallingAttributeFixer);
23+
24+
private const string PropertiesFolderName = "Properties";
25+
private const string AssemblyInfoFileName = "AssemblyInfo.cs";
26+
27+
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(GeneratorDiagnostics.Ids.TypeNotSupported);
28+
29+
// TODO: Write a custom fix all provider
30+
public override FixAllProvider? GetFixAllProvider() => null;
31+
32+
public override Task RegisterCodeFixesAsync(CodeFixContext context)
33+
{
34+
List<Diagnostic> fixedDiagnostics = new(context.Diagnostics.Where(IsRequiresDiableRuntimeMarshallingDiagnostic));
35+
36+
if (fixedDiagnostics.Count > 0)
37+
{
38+
context.RegisterCodeFix(
39+
CodeAction.Create(
40+
"Add DisableRuntimeMarshallingAttribute to the assembly.",
41+
ct => AddDisableRuntimeMarshallingAttributeApplicationToProject(context.Document.Project, ct),
42+
EquivalenceKey),
43+
fixedDiagnostics);
44+
}
45+
46+
return Task.CompletedTask;
47+
48+
static bool IsRequiresDiableRuntimeMarshallingDiagnostic(Diagnostic diagnostic)
49+
{
50+
return diagnostic.Properties.ContainsKey(GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute);
51+
}
52+
}
53+
54+
private static async Task<Solution> AddDisableRuntimeMarshallingAttributeApplicationToProject(Project project, CancellationToken cancellationToken)
55+
{
56+
Document? assemblyInfo = project.Documents.FirstOrDefault(IsPropertiesAssemblyInfo);
57+
58+
if (assemblyInfo is null)
59+
{
60+
assemblyInfo = project.AddDocument(AssemblyInfoFileName, "", folders: new[] { PropertiesFolderName });
61+
}
62+
63+
DocumentEditor editor = await DocumentEditor.CreateAsync(assemblyInfo, cancellationToken).ConfigureAwait(false);
64+
65+
var syntaxRoot = await assemblyInfo.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
66+
67+
editor.ReplaceNode(
68+
syntaxRoot,
69+
editor.Generator.AddAttributes(
70+
syntaxRoot,
71+
editor.Generator.Attribute(editor.Generator.DottedName(TypeNames.System_Runtime_CompilerServices_DisableRuntimeMarshallingAttribute))));
72+
73+
return editor.GetChangedDocument().Project.Solution;
74+
75+
static bool IsPropertiesAssemblyInfo(Document document)
76+
{
77+
// We specifically want to match a file in the Properties folder with the provided name (AssemblyInfo.cs) to match other VS templates that add this file.
78+
// We are very strict about this to ensure that we discover the correct file when it is already created and added to the project.
79+
return document.Name == AssemblyInfoFileName
80+
&& document.Folders.Count == 1
81+
&& document.Folders[0] == PropertiesFolderName;
82+
}
83+
}
84+
}
85+
}

src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/GeneratorDiagnostics.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.CodeAnalysis.CSharp.Syntax;
66
using System;
77
using System.Collections.Generic;
8+
using System.Collections.Immutable;
89
using System.Diagnostics;
910

1011
namespace Microsoft.Interop
@@ -226,7 +227,8 @@ public void ReportConfigurationNotSupported(
226227
public void ReportMarshallingNotSupported(
227228
MethodDeclarationSyntax method,
228229
TypePositionInfo info,
229-
string? notSupportedDetails)
230+
string? notSupportedDetails,
231+
ImmutableDictionary<string, string> diagnosticProperties)
230232
{
231233
Location diagnosticLocation = Location.None;
232234
string elementName = string.Empty;
@@ -252,6 +254,7 @@ public void ReportMarshallingNotSupported(
252254
_diagnostics.Add(
253255
diagnosticLocation.CreateDiagnostic(
254256
GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails,
257+
diagnosticProperties,
255258
notSupportedDetails!,
256259
elementName));
257260
}
@@ -260,6 +263,7 @@ public void ReportMarshallingNotSupported(
260263
_diagnostics.Add(
261264
diagnosticLocation.CreateDiagnostic(
262265
GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails,
266+
diagnosticProperties,
263267
notSupportedDetails!,
264268
elementName));
265269
}
@@ -274,6 +278,7 @@ public void ReportMarshallingNotSupported(
274278
_diagnostics.Add(
275279
diagnosticLocation.CreateDiagnostic(
276280
GeneratorDiagnostics.ReturnConfigurationNotSupported,
281+
diagnosticProperties,
277282
nameof(System.Runtime.InteropServices.MarshalAsAttribute),
278283
elementName));
279284
}
@@ -282,6 +287,7 @@ public void ReportMarshallingNotSupported(
282287
_diagnostics.Add(
283288
diagnosticLocation.CreateDiagnostic(
284289
GeneratorDiagnostics.ParameterConfigurationNotSupported,
290+
diagnosticProperties,
285291
nameof(System.Runtime.InteropServices.MarshalAsAttribute),
286292
elementName));
287293
}
@@ -294,6 +300,7 @@ public void ReportMarshallingNotSupported(
294300
_diagnostics.Add(
295301
diagnosticLocation.CreateDiagnostic(
296302
GeneratorDiagnostics.ReturnTypeNotSupported,
303+
diagnosticProperties,
297304
info.ManagedType.DiagnosticFormattedName,
298305
elementName));
299306
}
@@ -302,6 +309,7 @@ public void ReportMarshallingNotSupported(
302309
_diagnostics.Add(
303310
diagnosticLocation.CreateDiagnostic(
304311
GeneratorDiagnostics.ParameterTypeNotSupported,
312+
diagnosticProperties,
305313
info.ManagedType.DiagnosticFormattedName,
306314
elementName));
307315
}

src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateSou
501501
pinvokeStub.LibraryImportData.SetLastError && !options.GenerateForwarders,
502502
(elementInfo, ex) =>
503503
{
504-
diagnostics.ReportMarshallingNotSupported(originalSyntax, elementInfo, ex.NotSupportedDetails);
504+
diagnostics.ReportMarshallingNotSupported(originalSyntax, elementInfo, ex.NotSupportedDetails, ex.DiagnosticProperties ?? ImmutableDictionary<string, string>.Empty);
505505
},
506506
pinvokeStub.StubContext.GeneratorFactory);
507507

src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/IGeneratorDiagnostics.cs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static Diagnostic CreateDiagnostic(
7575
}
7676

7777
return firstLocation is null ?
78-
Diagnostic.Create(descriptor, Location.None, args) :
78+
Diagnostic.Create(descriptor, Location.None, properties: properties, args) :
7979
Diagnostic.Create(descriptor, firstLocation, additionalLocations, properties, args);
8080
}
8181

@@ -89,22 +89,24 @@ public static Diagnostic CreateDiagnostic(
8989
location: location.IsInSource ? location : Location.None,
9090
messageArgs: args);
9191
}
92+
93+
public static Diagnostic CreateDiagnostic(
94+
this Location location,
95+
DiagnosticDescriptor descriptor,
96+
ImmutableDictionary<string, string> properties,
97+
params object[] args)
98+
{
99+
return Diagnostic.Create(
100+
descriptor,
101+
location: location.IsInSource ? location : Location.None,
102+
properties: properties,
103+
messageArgs: args);
104+
}
92105
}
93106

94107

95108
public interface IGeneratorDiagnostics
96109
{
97-
/// <summary>
98-
/// Report diagnostic for marshalling of a parameter/return that is not supported
99-
/// </summary>
100-
/// <param name="method">Method with the parameter/return</param>
101-
/// <param name="info">Type info for the parameter/return</param>
102-
/// <param name="notSupportedDetails">[Optional] Specific reason for lack of support</param>
103-
void ReportMarshallingNotSupported(
104-
MethodDeclarationSyntax method,
105-
TypePositionInfo info,
106-
string? notSupportedDetails);
107-
108110
/// <summary>
109111
/// Report diagnostic for configuration that is not supported by the DLL import source generator
110112
/// </summary>
@@ -127,4 +129,9 @@ public static class IGeneratorDiagnosticsExtensions
127129
public static void ReportConfigurationNotSupported(this IGeneratorDiagnostics diagnostics, AttributeData attributeData, string configurationName)
128130
=> diagnostics.ReportConfigurationNotSupported(attributeData, configurationName, null);
129131
}
132+
133+
public class GeneratorDiagnosticProperties
134+
{
135+
public const string AddDisableRuntimeMarshallingAttribute = nameof(AddDisableRuntimeMarshallingAttribute);
136+
}
130137
}

src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/AttributedMarshallingModelGeneratorFactory.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Collections.Immutable;
67
using System.Runtime.InteropServices;
78
using Microsoft.CodeAnalysis;
89
using Microsoft.CodeAnalysis.CSharp;
@@ -15,6 +16,9 @@ namespace Microsoft.Interop
1516

1617
public class AttributedMarshallingModelGeneratorFactory : IMarshallingGeneratorFactory
1718
{
19+
private static readonly ImmutableDictionary<string, string> AddDisableRuntimeMarshallingAttributeProperties =
20+
ImmutableDictionary<string, string>.Empty.Add(GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute, GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute);
21+
1822
private static readonly BlittableMarshaller s_blittable = new BlittableMarshaller();
1923
private static readonly Forwarder s_forwarder = new Forwarder();
2024

@@ -55,7 +59,8 @@ public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext conte
5559
UnmanagedBlittableMarshallingInfo or NativeMarshallingAttributeInfo when !Options.RuntimeMarshallingDisabled =>
5660
throw new MarshallingNotSupportedException(info, context)
5761
{
58-
NotSupportedDetails = SR.RuntimeMarshallingMustBeDisabled
62+
NotSupportedDetails = SR.RuntimeMarshallingMustBeDisabled,
63+
DiagnosticProperties = AddDisableRuntimeMarshallingAttributeProperties
5964
},
6065
GeneratedNativeMarshallingAttributeInfo => s_forwarder,
6166
MissingSupportMarshallingInfo => s_forwarder,

src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGenerator.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using System.Collections.Immutable;
67
using Microsoft.CodeAnalysis.CSharp.Syntax;
78
using Microsoft.CodeAnalysis.Diagnostics;
89

@@ -160,5 +161,10 @@ public MarshallingNotSupportedException(TypePositionInfo info, StubCodeContext c
160161
/// [Optional] Specific reason marshalling of the supplied type isn't supported.
161162
/// </summary>
162163
public string? NotSupportedDetails { get; init; }
164+
165+
/// <summary>
166+
/// [Optional] Properties to attach to any diagnostic emitted due to this exception.
167+
/// </summary>
168+
public ImmutableDictionary<string, string>? DiagnosticProperties { get; init; }
163169
}
164170
}

0 commit comments

Comments
 (0)