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

Code fix to add DisableRuntimeMarshalling when required #67676

Merged
Merged
10 changes: 5 additions & 5 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@
<PropertyGroup>
<!-- Code analysis dependencies -->
<MicrosoftCodeAnalysisAnalyzersVersion>3.3.3</MicrosoftCodeAnalysisAnalyzersVersion>
<MicrosoftCodeAnalysisCSharpCodeStyleVersion>4.2.0-2.final</MicrosoftCodeAnalysisCSharpCodeStyleVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesVersion>4.2.0-2.final</MicrosoftCodeAnalysisCSharpWorkspacesVersion>
<MicrosoftCodeAnalysisCSharpVersion>4.2.0-2.final</MicrosoftCodeAnalysisCSharpVersion>
<MicrosoftCodeAnalysisCSharpCodeStyleVersion>4.3.0-1.22206.2</MicrosoftCodeAnalysisCSharpCodeStyleVersion>
<MicrosoftCodeAnalysisCSharpWorkspacesVersion>4.3.0-1.22206.2</MicrosoftCodeAnalysisCSharpWorkspacesVersion>
<MicrosoftCodeAnalysisCSharpVersion>4.3.0-1.22206.2</MicrosoftCodeAnalysisCSharpVersion>
<MicrosoftCodeAnalysisNetAnalyzersVersion>7.0.0-preview1.22207.3</MicrosoftCodeAnalysisNetAnalyzersVersion>
<MicrosoftCodeAnalysisVersion>4.2.0-2.final</MicrosoftCodeAnalysisVersion>
<MicrosoftCodeAnalysisVersion>4.3.0-1.22206.2</MicrosoftCodeAnalysisVersion>
<!--
TODO: Remove pinned version once arcade supplies a 4.3 compiler.
-->
Expand Down Expand Up @@ -173,7 +173,7 @@
<FsCheckVersion>2.14.3</FsCheckVersion>
<!-- Uncomment to set a fixed version, else the latest is used -->
<!--<SdkVersionForWorkloadTesting>7.0.100-preview.3.22151.18</SdkVersionForWorkloadTesting>-->
<CompilerPlatformTestingVersion>1.1.2-beta1.22122.4</CompilerPlatformTestingVersion>
<CompilerPlatformTestingVersion>1.1.2-beta1.22205.2</CompilerPlatformTestingVersion>
<!-- Docs -->
<MicrosoftPrivateIntellisenseVersion>7.0.0-preview-20220331.1</MicrosoftPrivateIntellisenseVersion>
<!-- ILLink -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// 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.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Editing;

namespace Microsoft.Interop.Analyzers
{
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
public class AddDisableRuntimeMarshallingAttributeFixer : CodeFixProvider
{
private const string EquivalenceKey = nameof(AddDisableRuntimeMarshallingAttributeFixer);

private const string PropertiesFolderName = "Properties";
private const string AssemblyInfoFileName = "AssemblyInfo.cs";

public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(GeneratorDiagnostics.Ids.TypeNotSupported);

// TODO: Write a custom fix all provider
public override FixAllProvider? GetFixAllProvider() => null;

public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
List<Diagnostic> fixedDiagnostics = new(context.Diagnostics.Where(IsRequiresDiableRuntimeMarshallingDiagnostic));

if (fixedDiagnostics.Count > 0)
{
context.RegisterCodeFix(
CodeAction.Create(
"Add DisableRuntimeMarshallingAttribute to the assembly.",
ct => AddDisableRuntimeMarshallingAttributeApplicationToProject(context.Document.Project, ct),
EquivalenceKey),
fixedDiagnostics);
}

return Task.CompletedTask;

static bool IsRequiresDiableRuntimeMarshallingDiagnostic(Diagnostic diagnostic)
{
return diagnostic.Properties.ContainsKey(GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute);
}
}

private static async Task<Solution> AddDisableRuntimeMarshallingAttributeApplicationToProject(Project project, CancellationToken cancellationToken)
{
Document? assemblyInfo = project.Documents.FirstOrDefault(IsPropertiesAssemblyInfo);

if (assemblyInfo is null)
{
assemblyInfo = project.AddDocument(AssemblyInfoFileName, "", folders: new[] { PropertiesFolderName });
}

DocumentEditor editor = await DocumentEditor.CreateAsync(assemblyInfo, cancellationToken).ConfigureAwait(false);

var syntaxRoot = await assemblyInfo.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

editor.ReplaceNode(
syntaxRoot,
editor.Generator.AddAttributes(
syntaxRoot,
editor.Generator.Attribute(editor.Generator.DottedName(TypeNames.System_Runtime_CompilerServices_DisableRuntimeMarshallingAttribute))));

return editor.GetChangedDocument().Project.Solution;

static bool IsPropertiesAssemblyInfo(Document document)
{
// 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.
// We are very strict about this to ensure that we discover the correct file when it is already created and added to the project.
return document.Name == AssemblyInfoFileName
&& document.Folders.Count == 1
&& document.Folders[0] == PropertiesFolderName;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;

namespace Microsoft.Interop
Expand Down Expand Up @@ -226,7 +227,8 @@ public void ReportConfigurationNotSupported(
public void ReportMarshallingNotSupported(
MethodDeclarationSyntax method,
TypePositionInfo info,
string? notSupportedDetails)
string? notSupportedDetails,
ImmutableDictionary<string, string> diagnosticProperties)
{
Location diagnosticLocation = Location.None;
string elementName = string.Empty;
Expand All @@ -252,6 +254,7 @@ public void ReportMarshallingNotSupported(
_diagnostics.Add(
diagnosticLocation.CreateDiagnostic(
GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails,
diagnosticProperties,
notSupportedDetails!,
elementName));
}
Expand All @@ -260,6 +263,7 @@ public void ReportMarshallingNotSupported(
_diagnostics.Add(
diagnosticLocation.CreateDiagnostic(
GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails,
diagnosticProperties,
notSupportedDetails!,
elementName));
}
Expand All @@ -274,6 +278,7 @@ public void ReportMarshallingNotSupported(
_diagnostics.Add(
diagnosticLocation.CreateDiagnostic(
GeneratorDiagnostics.ReturnConfigurationNotSupported,
diagnosticProperties,
nameof(System.Runtime.InteropServices.MarshalAsAttribute),
elementName));
}
Expand All @@ -282,6 +287,7 @@ public void ReportMarshallingNotSupported(
_diagnostics.Add(
diagnosticLocation.CreateDiagnostic(
GeneratorDiagnostics.ParameterConfigurationNotSupported,
diagnosticProperties,
nameof(System.Runtime.InteropServices.MarshalAsAttribute),
elementName));
}
Expand All @@ -294,6 +300,7 @@ public void ReportMarshallingNotSupported(
_diagnostics.Add(
diagnosticLocation.CreateDiagnostic(
GeneratorDiagnostics.ReturnTypeNotSupported,
diagnosticProperties,
info.ManagedType.DiagnosticFormattedName,
elementName));
}
Expand All @@ -302,6 +309,7 @@ public void ReportMarshallingNotSupported(
_diagnostics.Add(
diagnosticLocation.CreateDiagnostic(
GeneratorDiagnostics.ParameterTypeNotSupported,
diagnosticProperties,
info.ManagedType.DiagnosticFormattedName,
elementName));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateSou
pinvokeStub.LibraryImportData.SetLastError && !options.GenerateForwarders,
(elementInfo, ex) =>
{
diagnostics.ReportMarshallingNotSupported(originalSyntax, elementInfo, ex.NotSupportedDetails);
diagnostics.ReportMarshallingNotSupported(originalSyntax, elementInfo, ex.NotSupportedDetails, ex.DiagnosticProperties ?? ImmutableDictionary<string, string>.Empty);
},
pinvokeStub.StubContext.GeneratorFactory);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static Diagnostic CreateDiagnostic(
}

return firstLocation is null ?
Diagnostic.Create(descriptor, Location.None, args) :
Diagnostic.Create(descriptor, Location.None, properties: properties, args) :
Diagnostic.Create(descriptor, firstLocation, additionalLocations, properties, args);
}

Expand All @@ -89,22 +89,24 @@ public static Diagnostic CreateDiagnostic(
location: location.IsInSource ? location : Location.None,
messageArgs: args);
}

public static Diagnostic CreateDiagnostic(
this Location location,
DiagnosticDescriptor descriptor,
ImmutableDictionary<string, string> properties,
params object[] args)
{
return Diagnostic.Create(
descriptor,
location: location.IsInSource ? location : Location.None,
properties: properties,
messageArgs: args);
}
}


public interface IGeneratorDiagnostics
{
/// <summary>
/// Report diagnostic for marshalling of a parameter/return that is not supported
/// </summary>
/// <param name="method">Method with the parameter/return</param>
/// <param name="info">Type info for the parameter/return</param>
/// <param name="notSupportedDetails">[Optional] Specific reason for lack of support</param>
void ReportMarshallingNotSupported(
MethodDeclarationSyntax method,
TypePositionInfo info,
string? notSupportedDetails);

/// <summary>
/// Report diagnostic for configuration that is not supported by the DLL import source generator
/// </summary>
Expand All @@ -127,4 +129,9 @@ public static class IGeneratorDiagnosticsExtensions
public static void ReportConfigurationNotSupported(this IGeneratorDiagnostics diagnostics, AttributeData attributeData, string configurationName)
=> diagnostics.ReportConfigurationNotSupported(attributeData, configurationName, null);
}

public class GeneratorDiagnosticProperties
{
public const string AddDisableRuntimeMarshallingAttribute = nameof(AddDisableRuntimeMarshallingAttribute);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
Expand All @@ -15,6 +16,9 @@ namespace Microsoft.Interop

public class AttributedMarshallingModelGeneratorFactory : IMarshallingGeneratorFactory
{
private static readonly ImmutableDictionary<string, string> AddDisableRuntimeMarshallingAttributeProperties =
ImmutableDictionary<string, string>.Empty.Add(GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute, GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute);

private static readonly BlittableMarshaller s_blittable = new BlittableMarshaller();
private static readonly Forwarder s_forwarder = new Forwarder();

Expand Down Expand Up @@ -55,7 +59,8 @@ public IMarshallingGenerator Create(TypePositionInfo info, StubCodeContext conte
UnmanagedBlittableMarshallingInfo or NativeMarshallingAttributeInfo when !Options.RuntimeMarshallingDisabled =>
throw new MarshallingNotSupportedException(info, context)
{
NotSupportedDetails = SR.RuntimeMarshallingMustBeDisabled
NotSupportedDetails = SR.RuntimeMarshallingMustBeDisabled,
DiagnosticProperties = AddDisableRuntimeMarshallingAttributeProperties
},
GeneratedNativeMarshallingAttributeInfo => s_forwarder,
MissingSupportMarshallingInfo => s_forwarder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

Expand Down Expand Up @@ -160,5 +161,10 @@ public MarshallingNotSupportedException(TypePositionInfo info, StubCodeContext c
/// [Optional] Specific reason marshalling of the supplied type isn't supported.
/// </summary>
public string? NotSupportedDetails { get; init; }

/// <summary>
/// [Optional] Properties to attach to any diagnostic emitted due to this exception.
/// </summary>
public ImmutableDictionary<string, string>? DiagnosticProperties { get; init; }
}
}
Loading