using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
-
+using Microsoft.CodeAnalysis.DotnetRuntime.Extensions;
using static Microsoft.Interop.Analyzers.AnalyzerDiagnostics;
namespace Microsoft.Interop.Analyzers
{
private const string Category = "Interoperability";
- private static readonly string[] s_unsupportedTypeNames = new string[]
- {
- "System.Runtime.InteropServices.CriticalHandle",
- "System.Runtime.InteropServices.HandleRef",
- "System.Text.StringBuilder"
- };
-
public static readonly DiagnosticDescriptor ConvertToLibraryImport =
new DiagnosticDescriptor(
Ids.ConvertToLibraryImport,
public const string CharSet = nameof(CharSet);
public const string ExactSpelling = nameof(ExactSpelling);
+ public const string MayRequireAdditionalWork = nameof(MayRequireAdditionalWork);
+
+ private static readonly HashSet<string> s_unsupportedTypeNames = new()
+ {
+ "global::System.Runtime.InteropServices.CriticalHandle",
+ "global::System.Runtime.InteropServices.HandleRef",
+ "global::System.Text.StringBuilder"
+ };
public override void Initialize(AnalysisContext context)
{
context =>
{
// Nothing to do if the LibraryImportAttribute is not in the compilation
- INamedTypeSymbol? libraryImportAttrType = context.Compilation.GetTypeByMetadataName(TypeNames.LibraryImportAttribute);
+ INamedTypeSymbol? libraryImportAttrType = context.Compilation.GetBestTypeByMetadataName(TypeNames.LibraryImportAttribute);
if (libraryImportAttrType == null)
return;
- INamedTypeSymbol? marshalAsAttrType = context.Compilation.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute);
-
- var knownUnsupportedTypes = new List<ITypeSymbol>(s_unsupportedTypeNames.Length);
- foreach (string typeName in s_unsupportedTypeNames)
- {
- INamedTypeSymbol? unsupportedType = context.Compilation.GetTypeByMetadataName(typeName);
- if (unsupportedType != null)
- {
- knownUnsupportedTypes.Add(unsupportedType);
- }
- }
-
- context.RegisterSymbolAction(symbolContext => AnalyzeSymbol(symbolContext, knownUnsupportedTypes, marshalAsAttrType, libraryImportAttrType), SymbolKind.Method);
+ context.RegisterSymbolAction(symbolContext => AnalyzeSymbol(symbolContext, libraryImportAttrType), SymbolKind.Method);
});
}
- private static void AnalyzeSymbol(SymbolAnalysisContext context, List<ITypeSymbol> knownUnsupportedTypes, INamedTypeSymbol? marshalAsAttrType, INamedTypeSymbol libraryImportAttrType)
+ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol libraryImportAttrType)
{
var method = (IMethodSymbol)context.Symbol;
}
}
- // Ignore methods with unsupported parameters
- foreach (IParameterSymbol parameter in method.Parameters)
- {
- if (knownUnsupportedTypes.Contains(parameter.Type)
- || HasUnsupportedUnmanagedTypeValue(parameter.GetAttributes(), marshalAsAttrType))
- {
- return;
- }
- }
-
// Ignore methods with unsupported returns
if (method.ReturnsByRef || method.ReturnsByRefReadonly)
return;
- if (knownUnsupportedTypes.Contains(method.ReturnType) || HasUnsupportedUnmanagedTypeValue(method.GetReturnTypeAttributes(), marshalAsAttrType))
+ // Use the DllImport attribute data and the method signature to do some of the work the generator will do after conversion.
+ // If any diagnostics or failures to marshal are reported, then mark this diagnostic with a property signifying that it may require
+ // later user work.
+ AnyDiagnosticsSink diagnostics = new();
+ StubEnvironment env = context.Compilation.CreateStubEnvironment();
+ SignatureContext targetSignatureContext = SignatureContext.Create(method, CreateInteropAttributeDataFromDllImport(dllImportData), env, diagnostics, typeof(ConvertToLibraryImportAnalyzer).Assembly);
+
+ var generatorFactoryKey = LibraryImportGeneratorHelpers.CreateGeneratorFactory(env, new LibraryImportGeneratorOptions(context.Options.AnalyzerConfigOptionsProvider.GlobalOptions));
+
+ bool mayRequireAdditionalWork = diagnostics.AnyDiagnostics;
+ bool anyExplicitlyUnsupportedInfo = false;
+
+ var stubCodeContext = new ManagedToNativeStubCodeContext(env, "return", "nativeReturn");
+
+ var forwarder = new Forwarder();
+ // We don't actually need the bound generators. We just need them to be attempted to be bound to determine if the generator will be able to bind them.
+ _ = new BoundGenerators(targetSignatureContext.ElementTypeInformation, info =>
+ {
+ if (s_unsupportedTypeNames.Contains(info.ManagedType.FullTypeName))
+ {
+ anyExplicitlyUnsupportedInfo = true;
+ return forwarder;
+ }
+ if (HasUnsupportedMarshalAsInfo(info))
+ {
+ anyExplicitlyUnsupportedInfo = true;
+ return forwarder;
+ }
+ try
+ {
+ return generatorFactoryKey.GeneratorFactory.Create(info, stubCodeContext);
+ }
+ catch (MarshallingNotSupportedException)
+ {
+ mayRequireAdditionalWork = true;
+ return forwarder;
+ }
+ });
+
+ if (anyExplicitlyUnsupportedInfo)
+ {
+ // If we have any parameters/return value with an explicitly unsupported marshal type or marshalling info,
+ // don't offer the fix. The amount of work for the user to get to pairity would be too expensive.
return;
+ }
ImmutableDictionary<string, string>.Builder properties = ImmutableDictionary.CreateBuilder<string, string>();
properties.Add(CharSet, dllImportData.CharacterSet.ToString());
properties.Add(ExactSpelling, dllImportData.ExactSpelling.ToString());
+ properties.Add(MayRequireAdditionalWork, mayRequireAdditionalWork.ToString());
context.ReportDiagnostic(method.CreateDiagnostic(ConvertToLibraryImport, properties.ToImmutable(), method.Name));
}
- private static bool HasUnsupportedUnmanagedTypeValue(ImmutableArray<AttributeData> attributes, INamedTypeSymbol? marshalAsAttrType)
+ private static bool HasUnsupportedMarshalAsInfo(TypePositionInfo info)
{
- if (marshalAsAttrType == null)
- return false;
-
- AttributeData? marshalAsAttr = null;
- foreach (AttributeData attr in attributes)
- {
- if (SymbolEqualityComparer.Default.Equals(attr.AttributeClass, marshalAsAttrType))
- {
- marshalAsAttr = attr;
- break;
- }
- }
-
- if (marshalAsAttr == null || marshalAsAttr.ConstructorArguments.IsEmpty)
+ if (info.MarshallingAttributeInfo is not MarshalAsInfo(UnmanagedType unmanagedType, _))
return false;
- object unmanagedTypeObj = marshalAsAttr.ConstructorArguments[0].Value!;
- UnmanagedType unmanagedType = unmanagedTypeObj is short unmanagedTypeAsShort
- ? (UnmanagedType)unmanagedTypeAsShort
- : (UnmanagedType)unmanagedTypeObj;
-
return !System.Enum.IsDefined(typeof(UnmanagedType), unmanagedType)
|| unmanagedType == UnmanagedType.CustomMarshaler
|| unmanagedType == UnmanagedType.Interface
|| unmanagedType == UnmanagedType.IUnknown
|| unmanagedType == UnmanagedType.SafeArray;
}
+
+ private static InteropAttributeData CreateInteropAttributeDataFromDllImport(DllImportData dllImportData)
+ {
+ InteropAttributeData interopData = new();
+ if (dllImportData.SetLastError)
+ {
+ interopData = interopData with { IsUserDefined = interopData.IsUserDefined | InteropAttributeMember.SetLastError, SetLastError = true };
+ }
+ if (dllImportData.CharacterSet != System.Runtime.InteropServices.CharSet.None)
+ {
+ // Treat all strings as UTF-16 for the purposes of determining if we can marshal the parameters of this signature. We'll handle a more accurate conversion in the fixer.
+ interopData = interopData with { IsUserDefined = interopData.IsUserDefined | InteropAttributeMember.StringMarshalling, StringMarshalling = StringMarshalling.Utf16 };
+ }
+ return interopData;
+ }
+
+ private sealed class AnyDiagnosticsSink : IGeneratorDiagnostics
+ {
+ public bool AnyDiagnostics { get; private set; }
+ public void ReportConfigurationNotSupported(AttributeData attributeData, string configurationName, string? unsupportedValue) => AnyDiagnostics = true;
+ public void ReportInvalidMarshallingAttributeInfo(AttributeData attributeData, string reasonResourceName, params string[] reasonArgs) => AnyDiagnostics = true;
+ }
}
}
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.DotnetRuntime.Extensions;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.FindSymbols;
using static Microsoft.Interop.Analyzers.AnalyzerDiagnostics;
public override FixAllProvider GetFixAllProvider() => CustomFixAllProvider.Instance;
- private const string ConvertToLibraryImportKey = "ConvertToLibraryImport";
- private const string ConvertToLibraryImportWithASuffixKey = "ConvertToLibraryImportA";
- private const string ConvertToLibraryImportWithWSuffixKey = "ConvertToLibraryImportW";
+ private const string ConvertToLibraryImportKey = "ConvertToLibraryImport,";
+ private static string AddSuffixKey(string baseKey, char suffix) => $"{baseKey}{suffix},";
+ private static string AddUnsafeKey(string baseKey) => baseKey + "AddUnsafe,";
+ private static string AddMayRequireAdditionalWorkKey(string baseKey) => baseKey + $"{ConvertToLibraryImportAnalyzer.MayRequireAdditionalWork},";
private static readonly string[] s_preferredAttributeArgumentOrder =
{
if (root.FindNode(context.Span) is not MethodDeclarationSyntax methodSyntax)
return;
- // Register code fix
- context.RegisterCodeFix(
- CodeAction.Create(
- SR.ConvertToLibraryImport,
- cancelToken => ConvertToLibraryImport(
- context.Document,
- methodSyntax,
- entryPointSuffix: null,
- cancelToken),
- equivalenceKey: ConvertToLibraryImportKey),
- context.Diagnostics);
+ bool enableUnsafe = doc.Project.CompilationOptions is CSharpCompilationOptions { AllowUnsafe: false };
+
+ ICodeActionFactory codeActionFactory = enableUnsafe ? new AddUnsafeCodeActionFactory() : new DefaultCodeActionFactory();
foreach (Diagnostic diagnostic in context.Diagnostics)
{
+ bool mayRequireAdditionalWork = bool.TryParse(diagnostic.Properties[ConvertToLibraryImportAnalyzer.MayRequireAdditionalWork], out bool mayRequireAdditionalWorkValue)
+ ? mayRequireAdditionalWorkValue
+ : false;
+ // Register code fix
+ context.RegisterCodeFix(
+ codeActionFactory.CreateConvertToLibraryImportCodeFix(doc, methodSyntax, mayRequireAdditionalWork),
+ context.Diagnostics);
if (!bool.Parse(diagnostic.Properties[ConvertToLibraryImportAnalyzer.ExactSpelling]))
{
CharSet charSet = (CharSet)Enum.Parse(typeof(CharSet), diagnostic.Properties[ConvertToLibraryImportAnalyzer.CharSet]);
if (charSet is CharSet.None or CharSet.Ansi or CharSet.Auto)
{
context.RegisterCodeFix(
- CodeAction.Create(
- string.Format(SR.ConvertToLibraryImportWithSuffix, "A"),
- cancelToken => ConvertToLibraryImport(
- context.Document,
- methodSyntax,
- entryPointSuffix: 'A',
- cancelToken),
- equivalenceKey: ConvertToLibraryImportWithASuffixKey),
+ codeActionFactory.CreateConvertToLibraryImportWithSuffixCodeFix(doc, methodSyntax, 'A', mayRequireAdditionalWork),
context.Diagnostics);
}
if (charSet is CharSet.Unicode or CharSet.Auto)
{
context.RegisterCodeFix(
- CodeAction.Create(
- string.Format(SR.ConvertToLibraryImportWithSuffix, "W"),
- cancelToken => ConvertToLibraryImport(
- context.Document,
- methodSyntax,
- entryPointSuffix: 'W',
- cancelToken),
- equivalenceKey: ConvertToLibraryImportWithWSuffixKey),
+ codeActionFactory.CreateConvertToLibraryImportWithSuffixCodeFix(doc, methodSyntax, 'W', mayRequireAdditionalWork),
context.Diagnostics);
}
}
}
}
- private sealed class CustomFixAllProvider : DocumentBasedFixAllProvider
+ internal interface ICodeActionFactory
{
- public static readonly CustomFixAllProvider Instance = new();
+ CodeAction CreateConvertToLibraryImportCodeFix(Document document, MethodDeclarationSyntax methodSyntax, bool mayRequireAdditionalWork);
+ CodeAction CreateConvertToLibraryImportWithSuffixCodeFix(Document document, MethodDeclarationSyntax methodSyntax, char suffix, bool mayRequireAdditionalWork);
+ }
- protected override async Task<Document?> FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray<Diagnostic> diagnostics)
+ private sealed class DefaultCodeActionFactory : ICodeActionFactory
+ {
+ public CodeAction CreateConvertToLibraryImportCodeFix(Document document, MethodDeclarationSyntax methodSyntax, bool mayRequireAdditionalWork)
{
- DocumentEditor editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false);
- SyntaxGenerator generator = editor.Generator;
+ return CodeAction.Create(
+ SR.ConvertToLibraryImport,
+ cancelToken => ConvertToLibraryImport(
+ document,
+ methodSyntax,
+ mayRequireAdditionalWork,
+ entryPointSuffix: null,
+ cancelToken),
+ equivalenceKey: mayRequireAdditionalWork ? AddMayRequireAdditionalWorkKey(ConvertToLibraryImportKey) : ConvertToLibraryImportKey);
+ }
- SyntaxNode? root = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
- if (root == null)
- return document;
+ public CodeAction CreateConvertToLibraryImportWithSuffixCodeFix(Document document, MethodDeclarationSyntax methodSyntax, char suffix, bool mayRequireAdditionalWork)
+ {
+ return CodeAction.Create(
+ string.Format(SR.ConvertToLibraryImportWithSuffix, suffix),
+ cancelToken => ConvertToLibraryImport(
+ document,
+ methodSyntax,
+ mayRequireAdditionalWork,
+ entryPointSuffix: suffix,
+ cancelToken),
+ equivalenceKey: AddSuffixKey(mayRequireAdditionalWork ? AddMayRequireAdditionalWorkKey(ConvertToLibraryImportKey) : ConvertToLibraryImportKey, suffix));
+ }
+ }
- foreach (Diagnostic diagnostic in diagnostics)
- {
- // Get the syntax node tied to the diagnostic and check that it is a method declaration
- if (root.FindNode(diagnostic.Location.SourceSpan) is not MethodDeclarationSyntax methodSyntax)
- continue;
- if (editor.SemanticModel.GetDeclaredSymbol(methodSyntax, fixAllContext.CancellationToken) is not IMethodSymbol methodSymbol)
- continue;
+ private sealed class AddUnsafeCodeActionFactory : ICodeActionFactory
+ {
+ public CodeAction CreateConvertToLibraryImportCodeFix(Document document, MethodDeclarationSyntax methodSyntax, bool mayRequireAdditionalWork)
+ {
+ return CodeAction.Create(
+ SR.ConvertToLibraryImportAddUnsafe,
+ async cancelToken =>
+ {
+ Solution solutionWithUnsafe = AddUnsafe(document.Project.Solution, document.Project);
+ SolutionEditor editor = new SolutionEditor(solutionWithUnsafe);
+
+ await ConvertToLibraryImport(
+ await editor.GetDocumentEditorAsync(document.Id, cancelToken).ConfigureAwait(false),
+ methodSyntax,
+ mayRequireAdditionalWork,
+ entryPointSuffix: null,
+ cancelToken).ConfigureAwait(false);
+ return editor.GetChangedSolution();
+ },
+ equivalenceKey: AddUnsafeKey(mayRequireAdditionalWork ? AddMayRequireAdditionalWorkKey(ConvertToLibraryImportKey) : ConvertToLibraryImportKey));
+ }
- SyntaxNode generatedDeclaration = await ConvertMethodDeclarationToLibraryImport(methodSyntax, editor, generator, methodSymbol, GetSuffixFromEquivalenceKey(fixAllContext.CodeActionEquivalenceKey), fixAllContext.CancellationToken).ConfigureAwait(false);
+ public CodeAction CreateConvertToLibraryImportWithSuffixCodeFix(Document document, MethodDeclarationSyntax methodSyntax, char suffix, bool mayRequireAdditionalWork)
+ {
+ return CodeAction.Create(
+ string.Format(SR.ConvertToLibraryImportWithSuffixAddUnsafe, suffix),
+ async cancelToken =>
+ {
+ Solution solutionWithUnsafe = AddUnsafe(document.Project.Solution, document.Project);
+ SolutionEditor editor = new SolutionEditor(solutionWithUnsafe);
+
+ await ConvertToLibraryImport(
+ await editor.GetDocumentEditorAsync(document.Id, cancelToken).ConfigureAwait(false),
+ methodSyntax,
+ mayRequireAdditionalWork,
+ entryPointSuffix: suffix,
+ cancelToken).ConfigureAwait(false);
+ return editor.GetChangedSolution();
+ },
+ equivalenceKey: AddUnsafeKey(AddSuffixKey(mayRequireAdditionalWork ? AddMayRequireAdditionalWorkKey(ConvertToLibraryImportKey) : ConvertToLibraryImportKey, suffix)));
+ }
+ }
- if (!methodSymbol.MethodImplementationFlags.HasFlag(System.Reflection.MethodImplAttributes.PreserveSig))
+ private sealed class CustomFixAllProvider : FixAllProvider
+ {
+ public static readonly CustomFixAllProvider Instance = new();
+ public override async Task<CodeAction?> GetFixAsync(FixAllContext fixAllContext)
+ {
+ bool addUnsafe = fixAllContext.CodeActionEquivalenceKey.Contains(",AddUnsafe,");
+ bool includeFixesWithAdditionalWork = fixAllContext.CodeActionEquivalenceKey.Contains($",{ConvertToLibraryImportAnalyzer.MayRequireAdditionalWork},");
+ ImmutableArray<Diagnostic> diagnosticsInScope = await fixAllContext.GetDiagnosticsInScopeAsync().ConfigureAwait(false);
+
+ return CodeAction.Create(addUnsafe ? SR.ConvertToLibraryImportAddUnsafe : SR.ConvertToLibraryImport,
+ async ct =>
{
- bool shouldWarn = await TransformCallersOfNoPreserveSigMethod(editor, methodSymbol, fixAllContext.CancellationToken).ConfigureAwait(false);
- if (shouldWarn)
+ HashSet<Project> projectsToAddUnsafe = new();
+ SolutionEditor solutionEditor = new SolutionEditor(fixAllContext.Solution);
+
+ foreach (var diagnostic in diagnosticsInScope)
{
- generatedDeclaration = generatedDeclaration.WithAdditionalAnnotations(WarningAnnotation.Create(SR.ConvertNoPreserveSigDllImportToGeneratedMayProduceInvalidCode));
- }
- }
+ bool mayRequireAdditionalWork = bool.TryParse(diagnostic.Properties[ConvertToLibraryImportAnalyzer.MayRequireAdditionalWork], out bool mayRequireAdditionalWorkValue)
+ ? mayRequireAdditionalWorkValue
+ : false;
+ if (mayRequireAdditionalWork && !includeFixesWithAdditionalWork)
+ {
+ // Don't fix any diagnostics that require additional work if the "fix all" command wasn't triggered from a location
+ // that was able to warn the user that additional work may be required.
+ continue;
+ }
+ DocumentId documentId = solutionEditor.OriginalSolution.GetDocumentId(diagnostic.Location.SourceTree)!;
+ DocumentEditor editor = await solutionEditor.GetDocumentEditorAsync(documentId, ct).ConfigureAwait(false);
+ SyntaxNode root = await diagnostic.Location.SourceTree.GetRootAsync(ct).ConfigureAwait(false);
- // Replace the original method with the updated one
- editor.ReplaceNode(methodSyntax, generatedDeclaration);
+ // Get the syntax node tied to the diagnostic and check that it is a method declaration
+ if (root.FindNode(diagnostic.Location.SourceSpan) is not MethodDeclarationSyntax methodSyntax)
+ continue;
- MakeEnclosingTypesPartial(editor, methodSyntax);
- }
+ await ConvertToLibraryImport(editor, methodSyntax, mayRequireAdditionalWork, GetSuffixFromEquivalenceKey(fixAllContext.CodeActionEquivalenceKey), ct).ConfigureAwait(false);
+
+ // Record this project as a project we need to allow unsafe blocks in.
+ projectsToAddUnsafe.Add(solutionEditor.OriginalSolution.GetDocument(documentId).Project);
+ }
+
+ Solution solutionWithUpdatedSources = solutionEditor.GetChangedSolution();
- return editor.GetChangedDocument();
+ if (addUnsafe)
+ {
+ foreach (var project in projectsToAddUnsafe)
+ {
+ solutionWithUpdatedSources = AddUnsafe(solutionWithUpdatedSources, project);
+ }
+ }
+ return solutionWithUpdatedSources;
+ },
+ equivalenceKey: fixAllContext.CodeActionEquivalenceKey);
}
- private static char? GetSuffixFromEquivalenceKey(string equivalenceKey) => equivalenceKey switch
+
+ private static char? GetSuffixFromEquivalenceKey(string equivalenceKey)
{
- ConvertToLibraryImportWithASuffixKey => 'A',
- ConvertToLibraryImportWithWSuffixKey => 'W',
- _ => null
- };
+ if (equivalenceKey.Contains(",A,"))
+ {
+ return 'A';
+ }
+ if (equivalenceKey.Contains(",W,"))
+ {
+ return 'W';
+ }
+ return null;
+ }
+ }
+
+ private static Solution AddUnsafe(Solution solution, Project project)
+ {
+ return solution.WithProjectCompilationOptions(project.Id, ((CSharpCompilationOptions)project.CompilationOptions).WithAllowUnsafe(true));
}
private static async Task<Document> ConvertToLibraryImport(
Document doc,
MethodDeclarationSyntax methodSyntax,
+ bool warnForAdditionalWork,
char? entryPointSuffix,
CancellationToken cancellationToken)
{
DocumentEditor editor = await DocumentEditor.CreateAsync(doc, cancellationToken).ConfigureAwait(false);
+ await ConvertToLibraryImport(editor, methodSyntax, warnForAdditionalWork, entryPointSuffix, cancellationToken).ConfigureAwait(false);
+ return editor.GetChangedDocument();
+ }
+ private static async Task ConvertToLibraryImport(
+ DocumentEditor editor,
+ MethodDeclarationSyntax methodSyntax,
+ bool warnForAdditionalWork,
+ char? entryPointSuffix,
+ CancellationToken cancellationToken)
+ {
SyntaxGenerator generator = editor.Generator;
if (editor.SemanticModel.GetDeclaredSymbol(methodSyntax, cancellationToken) is not IMethodSymbol methodSymbol)
- return doc;
+ return;
- SyntaxNode generatedDeclaration = await ConvertMethodDeclarationToLibraryImport(methodSyntax, editor, generator, methodSymbol, entryPointSuffix, cancellationToken).ConfigureAwait(false);
+ SyntaxNode generatedDeclaration = await ConvertMethodDeclarationToLibraryImport(
+ methodSyntax,
+ editor,
+ generator,
+ methodSymbol,
+ warnForAdditionalWork,
+ entryPointSuffix,
+ cancellationToken).ConfigureAwait(false);
if (!methodSymbol.MethodImplementationFlags.HasFlag(System.Reflection.MethodImplAttributes.PreserveSig))
{
editor.ReplaceNode(methodSyntax, generatedDeclaration);
MakeEnclosingTypesPartial(editor, methodSyntax);
-
- return editor.GetChangedDocument();
}
private static async Task<SyntaxNode> ConvertMethodDeclarationToLibraryImport(
DocumentEditor editor,
SyntaxGenerator generator,
IMethodSymbol methodSymbol,
+ bool warnForAdditionalWork,
char? entryPointSuffix,
CancellationToken cancellationToken)
{
out SyntaxNode? unmanagedCallConvAttributeMaybe);
// Add annotation about potential behavioural and compatibility changes
- libraryImportSyntax = libraryImportSyntax.WithAdditionalAnnotations(
- WarningAnnotation.Create(string.Format(SR.ConvertToLibraryImportWarning, "[TODO] Documentation link")));
+ if (warnForAdditionalWork)
+ {
+ libraryImportSyntax = libraryImportSyntax.WithAdditionalAnnotations(
+ WarningAnnotation.Create(SR.ConvertToLibraryImportMayRequireCustomMarshalling));
+ }
// Replace DllImport with LibraryImport
SyntaxNode generatedDeclaration = generator.ReplaceNode(methodSyntax, dllImportSyntax, libraryImportSyntax);
{
// For Unicode, we can translate the argument to StringMarshalling.Utf16
// TODO: Handle ANSI once we have a public marshaller type for ANSI strings that we can use with StringMarshallerCustomType
- if (dllImportData.CharacterSet == CharSet.Unicode)
+ if (dllImportData.CharacterSet == CharSet.Unicode || (dllImportData.CharacterSet == CharSet.Auto && entryPointSuffix is 'W' or null))
{
- ITypeSymbol stringMarshallingType = editor.SemanticModel.Compilation.GetTypeByMetadataName(TypeNames.StringMarshalling)!;
+ ITypeSymbol stringMarshallingType = editor.SemanticModel.Compilation.GetBestTypeByMetadataName(TypeNames.StringMarshalling)!;
argumentsToAdd.Add(generator.AttributeArgument(
nameof(StringMarshalling),
generator.MemberAccessExpression(
generator.TypeExpression(stringMarshallingType),
generator.IdentifierName(nameof(StringMarshalling.Utf16)))));
}
+ else if (dllImportData.CharacterSet == CharSet.Ansi || (dllImportData.CharacterSet == CharSet.Auto && entryPointSuffix == 'A'))
+ {
+ ITypeSymbol stringMarshallingType = editor.SemanticModel.Compilation.GetBestTypeByMetadataName(TypeNames.StringMarshalling)!;
+ argumentsToAdd.Add(generator.AttributeArgument(
+ nameof(StringMarshalling),
+ generator.MemberAccessExpression(
+ generator.TypeExpression(stringMarshallingType),
+ generator.IdentifierName(nameof(StringMarshalling.Custom)))));
+ argumentsToAdd.Add(generator.AttributeArgument(
+ "StringMarshallingCustomType",
+ generator.TypeOfExpression(generator.TypeExpression(editor.SemanticModel.Compilation.GetBestTypeByMetadataName(TypeNames.AnsiStringMarshaller)))));
+ }
}
argumentsToRemove.Add(argument);
--- /dev/null
+// 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.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+
+namespace Microsoft.Interop.Analyzers
+{
+ internal static class FixAllContextExtensions
+ {
+ public static async Task<ImmutableArray<Diagnostic>> GetDiagnosticsInScopeAsync(this FixAllContext context)
+ {
+ switch (context.Scope)
+ {
+ case FixAllScope.Document:
+ return await context.GetDocumentDiagnosticsAsync(context.Document).ConfigureAwait(false);
+ case FixAllScope.Project:
+ return await context.GetAllDiagnosticsAsync(context.Project).ConfigureAwait(false);
+ case FixAllScope.Solution:
+ Solution solution = context.Solution;
+ ProjectDependencyGraph dependencyGraph = solution.GetProjectDependencyGraph();
+
+ // Walk through each project in topological order, determining and applying the diagnostics for each
+ // project. We do this in topological order so that the compilations for successive projects are readily
+ // available as we just computed them for dependent projects. If we were to do it out of order, we might
+ // start with a project that has a ton of dependencies, and we'd spend an inordinate amount of time just
+ // building the compilations for it before we could proceed.
+ //
+ // By processing one project at a time, we can also let go of a project once done with it, allowing us to
+ // reclaim lots of the memory so we don't overload the system while processing a large solution.
+ //
+ // Note: we have to filter down to projects of the same language as the FixAllContext points at a
+ // CodeFixProvider, and we can't call into providers of different languages with diagnostics from a
+ // different language.
+ IEnumerable<Project?> sortedProjects = dependencyGraph.GetTopologicallySortedProjects(context.CancellationToken)
+ .Select(solution.GetProject)
+ .Where(p => p.Language == context.Project.Language);
+ return (await Task.WhenAll(sortedProjects.Select(context.GetAllDiagnosticsAsync)).ConfigureAwait(false)).SelectMany(diag => diag).ToImmutableArray();
+ default:
+ return ImmutableArray<Diagnostic>.Empty;
+ }
+ }
+
+ public static async Task<ImmutableArray<Project>> GetProjectsWithDiagnosticsAsync(this FixAllContext context)
+ {
+ switch (context.Scope)
+ {
+ case FixAllScope.ContainingMember:
+ case FixAllScope.ContainingType:
+ case FixAllScope.Document:
+ case FixAllScope.Project:
+ return ImmutableArray.Create(context.Project);
+ case FixAllScope.Solution:
+ ImmutableArray<Project>.Builder projectsWithDiagnostics = ImmutableArray.CreateBuilder<Project>();
+ foreach (var project in context.Solution.Projects)
+ {
+ ImmutableArray<Diagnostic> diagnostics = await context.GetAllDiagnosticsAsync(project).ConfigureAwait(false);
+ if (diagnostics.Length != 0)
+ {
+ projectsWithDiagnostics.Add(project);
+ }
+ }
+ return projectsWithDiagnostics.ToImmutable();
+ default:
+ return ImmutableArray<Project>.Empty;
+ }
+ }
+ }
+}
new MethodSignatureDiagnosticLocations(originalSyntax),
additionalAttributes.ToImmutableArray(),
libraryImportData,
- CreateGeneratorFactory(environment, options),
+ LibraryImportGeneratorHelpers.CreateGeneratorFactory(environment, options),
generatorDiagnostics.Diagnostics.ToImmutableArray());
}
- private static MarshallingGeneratorFactoryKey<(TargetFramework, Version, LibraryImportGeneratorOptions)> CreateGeneratorFactory(StubEnvironment env, LibraryImportGeneratorOptions options)
- {
- IMarshallingGeneratorFactory generatorFactory;
-
- if (options.GenerateForwarders)
- {
- generatorFactory = new ForwarderMarshallingGeneratorFactory();
- }
- else
- {
- if (env.TargetFramework != TargetFramework.Net || env.TargetFrameworkVersion.Major < 7)
- {
- // If we're using our downstream support, fall back to the Forwarder marshaller when the TypePositionInfo is unhandled.
- generatorFactory = new ForwarderMarshallingGeneratorFactory();
- }
- else
- {
- // If we're in a "supported" scenario, then emit a diagnostic as our final fallback.
- generatorFactory = new UnsupportedMarshallingFactory();
- }
-
- generatorFactory = new NoMarshallingInfoErrorMarshallingFactory(generatorFactory);
-
- // The presence of System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute is tied to TFM,
- // so we use TFM in the generator factory key instead of the Compilation as the compilation changes on every keystroke.
- IAssemblySymbol coreLibraryAssembly = env.Compilation.GetSpecialType(SpecialType.System_Object).ContainingAssembly;
- ITypeSymbol? disabledRuntimeMarshallingAttributeType = coreLibraryAssembly.GetTypeByMetadataName(TypeNames.System_Runtime_CompilerServices_DisableRuntimeMarshallingAttribute);
- bool runtimeMarshallingDisabled = disabledRuntimeMarshallingAttributeType is not null
- && env.Compilation.Assembly.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, disabledRuntimeMarshallingAttributeType));
-
- // Since the char type can go into the P/Invoke signature here, we can only use it when
- // runtime marshalling is disabled.
- generatorFactory = new CharMarshallingGeneratorFactory(generatorFactory, useBlittableMarshallerForUtf16: runtimeMarshallingDisabled);
-
- InteropGenerationOptions interopGenerationOptions = new(options.UseMarshalType);
- generatorFactory = new MarshalAsMarshallingGeneratorFactory(interopGenerationOptions, generatorFactory);
-
- IMarshallingGeneratorFactory elementFactory = new AttributedMarshallingModelGeneratorFactory(
- // Since the char type in an array will not be part of the P/Invoke signature, we can
- // use the regular blittable marshaller in all cases.
- new CharMarshallingGeneratorFactory(generatorFactory, useBlittableMarshallerForUtf16: true),
- new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, MarshalMode.ElementIn, MarshalMode.ElementRef, MarshalMode.ElementOut));
- // We don't need to include the later generator factories for collection elements
- // as the later generator factories only apply to parameters.
- generatorFactory = new AttributedMarshallingModelGeneratorFactory(
- generatorFactory,
- elementFactory,
- new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, MarshalMode.ManagedToUnmanagedIn, MarshalMode.ManagedToUnmanagedRef, MarshalMode.ManagedToUnmanagedOut));
-
- generatorFactory = new ByValueContentsMarshalKindValidator(generatorFactory);
- }
-
- return MarshallingGeneratorFactoryKey.Create((env.TargetFramework, env.TargetFrameworkVersion, options), generatorFactory);
- }
-
private static (MemberDeclarationSyntax, ImmutableArray<Diagnostic>) GenerateSource(
IncrementalStubGenerationContext pinvokeStub,
LibraryImportGeneratorOptions options)
--- /dev/null
+// 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.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Interop
+{
+ internal static class LibraryImportGeneratorHelpers
+ {
+ public static MarshallingGeneratorFactoryKey<(TargetFramework, Version, LibraryImportGeneratorOptions)> CreateGeneratorFactory(StubEnvironment env, LibraryImportGeneratorOptions options)
+ {
+ IMarshallingGeneratorFactory generatorFactory;
+
+ if (options.GenerateForwarders)
+ {
+ generatorFactory = new ForwarderMarshallingGeneratorFactory();
+ }
+ else
+ {
+ if (env.TargetFramework != TargetFramework.Net || env.TargetFrameworkVersion.Major < 7)
+ {
+ // If we're using our downstream support, fall back to the Forwarder marshaller when the TypePositionInfo is unhandled.
+ generatorFactory = new ForwarderMarshallingGeneratorFactory();
+ }
+ else
+ {
+ // If we're in a "supported" scenario, then emit a diagnostic as our final fallback.
+ generatorFactory = new UnsupportedMarshallingFactory();
+ }
+
+ generatorFactory = new NoMarshallingInfoErrorMarshallingFactory(generatorFactory);
+
+ // The presence of System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute is tied to TFM,
+ // so we use TFM in the generator factory key instead of the Compilation as the compilation changes on every keystroke.
+ IAssemblySymbol coreLibraryAssembly = env.Compilation.GetSpecialType(SpecialType.System_Object).ContainingAssembly;
+ ITypeSymbol? disabledRuntimeMarshallingAttributeType = coreLibraryAssembly.GetTypeByMetadataName(TypeNames.System_Runtime_CompilerServices_DisableRuntimeMarshallingAttribute);
+ bool runtimeMarshallingDisabled = disabledRuntimeMarshallingAttributeType is not null
+ && env.Compilation.Assembly.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, disabledRuntimeMarshallingAttributeType));
+
+ // Since the char type can go into the P/Invoke signature here, we can only use it when
+ // runtime marshalling is disabled.
+ generatorFactory = new CharMarshallingGeneratorFactory(generatorFactory, useBlittableMarshallerForUtf16: runtimeMarshallingDisabled);
+
+ InteropGenerationOptions interopGenerationOptions = new(options.UseMarshalType);
+ generatorFactory = new MarshalAsMarshallingGeneratorFactory(interopGenerationOptions, generatorFactory);
+
+ IMarshallingGeneratorFactory elementFactory = new AttributedMarshallingModelGeneratorFactory(
+ // Since the char type in an array will not be part of the P/Invoke signature, we can
+ // use the regular blittable marshaller in all cases.
+ new CharMarshallingGeneratorFactory(generatorFactory, useBlittableMarshallerForUtf16: true),
+ new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, MarshalMode.ElementIn, MarshalMode.ElementRef, MarshalMode.ElementOut));
+ // We don't need to include the later generator factories for collection elements
+ // as the later generator factories only apply to parameters.
+ generatorFactory = new AttributedMarshallingModelGeneratorFactory(
+ generatorFactory,
+ elementFactory,
+ new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, MarshalMode.ManagedToUnmanagedIn, MarshalMode.ManagedToUnmanagedRef, MarshalMode.ManagedToUnmanagedOut));
+
+ generatorFactory = new ByValueContentsMarshalKindValidator(generatorFactory);
+ }
+
+ return MarshallingGeneratorFactoryKey.Create((env.TargetFramework, env.TargetFrameworkVersion, options), generatorFactory);
+ }
+ }
+}
<data name="ConvertToLibraryImportTitle" xml:space="preserve">
<value>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</value>
</data>
- <data name="ConvertToLibraryImportWarning" xml:space="preserve">
- <value>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</value>
- <comment>{0} is a documentation link</comment>
- </data>
<data name="ConvertToLibraryImportWithSuffix" xml:space="preserve">
<value>Convert to 'LibraryImport' with '{0}' suffix</value>
</data>
<data name="RequiresAllowUnsafeBlocksTitle" xml:space="preserve">
<value>LibraryImportAttribute requires unsafe code.</value>
</data>
+ <data name="ConvertToLibraryImportAddUnsafe" xml:space="preserve">
+ <value>Convert to 'LibraryImport' and enable unsafe code</value>
+ </data>
+ <data name="ConvertToLibraryImportMayRequireCustomMarshalling" xml:space="preserve">
+ <value>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</value>
+ </data>
+ <data name="ConvertToLibraryImportWithSuffixAddUnsafe" xml:space="preserve">
+ <value>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</value>
+ </data>
</root>
\ No newline at end of file
<target state="translated">Převést na LibraryImport</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">K vygenerování kódu zařazování P/Invoke v době kompilace použijte LibraryImportAttribute místo DllImportAttribute</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Označit metodu {0} pomocí LibraryImportAttribute místo DllImportAttribute, aby došlo k vygenerování kódu zařazování volání P/Invoke za kompilace.</target>
<target state="translated">K vygenerování kódu zařazování P/Invoke v době kompilace použijte LibraryImportAttribute místo DllImportAttribute</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">Převod na LibraryImport může vést ke změně chování a kompatibility. Zjistěte více informací přechodem na {0}.</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">Převést na LibraryImport s příponou {0}</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">Zařazovací typ nemá požadovaný tvar</target>
<target state="translated">In \"LibraryImport\" konvertieren</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Verwenden Sie \"LibraryImportAttribute\" anstelle von \"DllImportAttribute\", um P/Invoke-Marshallingcode zur Kompilierzeit zu generieren.</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Markieren Sie die Methode \"{0}\" mit \"LibraryImportAttribute\" anstelle von \"DllImportAttribute\", um zur Kompilierzeit P/Invoke-Marshallingcode zu generieren.</target>
<target state="translated">Verwenden Sie \"LibraryImportAttribute\" anstelle von \"DllImportAttribute\", um P/Invoke-Marshallingcode zur Kompilierzeit zu generieren.</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">Die Konvertierung in \"LibraryImport\" kann das Verhalten und die Kompatibilität ändern. Weitere Informationen finden Sie unter {0}.</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">In \"LibraryImport\" mit Suffix \"{0}\" konvertieren</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">Der Marshaller-Typ weist nicht die erforderliche Form auf</target>
<target state="translated">Convertir en “LibraryImport”</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Use “LibraryImportAttribute” en lugar de “DllImportAttribute” para generar código de serialización P/Invoke en el tiempo de compilación</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Marque el método “{0}” con “LibraryImportAttribute” en lugar de “DllImportAttribute” para generar código de serialización P/Invoke en el tiempo de compilación</target>
<target state="translated">Use “LibraryImportAttribute” en lugar de “DllImportAttribute” para generar código de serialización P/Invoke en el tiempo de compilación</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">La conversión a “LibraryImport” debe cambiar de comportamiento y compatibilidad. Vea {0} para obtener más información.</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">Convertir a “LibraryImport” con sufijo “{0}”</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">El tipo de serializador no tiene la forma necesaria</target>
<target state="translated">Convertir en « LibraryImport »</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Utilisez « LibraryImportAttribute » à la place de « DllImportAttribute » pour générer du code de marshaling P/Invoke au moment de la compilation</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Marquer la méthode « {0} » avec « LibraryImportAttribute » à la place de « DllImportAttribute » pour générer du code de marshaling P/Invoke au moment de la compilation</target>
<target state="translated">Utilisez « LibraryImportAttribute » à la place de « DllImportAttribute » pour générer du code de marshaling P/Invoke au moment de la compilation</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">La conversion en « LibraryImport » peut modifier le comportement et la compatibilité. Pour plus d’informations, consultez {0}.</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">Convertir en « LibraryImport » avec suffixe « {0} »</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">Le type Marshaller n’a pas la forme requise</target>
<target state="translated">Converti in 'LibraryImport'</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Usare 'LibraryImportAttribute' invece di 'DllImportAttribute' per generare codice di marshalling di P/Invoke in fase di compilazione</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Contrassegnare il metodo '{0}' con 'LibraryImportAttribute' invece di 'DllImportAttribute' per generare codice di marshalling di P/Invoke in fase di compilazione</target>
<target state="translated">Usare 'LibraryImportAttribute' invece di 'DllImportAttribute' per generare codice di marshalling di P/Invoke in fase di compilazione</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">La conversione in 'LibraryImport' può modificare il comportamento e la compatibilità. Per altre informazioni, vedere {0}.</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">Converti in 'LibraryImport' con il suffisso '{0}'</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">Il tipo di marshaller non ha la forma richiesta</target>
<target state="translated">'LibraryImport' への変換</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">コンパイル時に P/Invoke マーシャリング コードを生成するには、'DllImportAttribute' の代わりに 'LibraryImportAttribute' を使用します</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">コンパイル時に P/Invoke マーシャリング コードを生成するには、'DllImportAttribute' の代わりに 'LibraryImportAttribute' を持つメソッド '{0}' をマークします</target>
<target state="translated">コンパイル時に P/Invoke マーシャリング コードを生成するには、'DllImportAttribute' の代わりに 'LibraryImportAttribute' を使用します</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">'LibraryImport' に変換すると、動作と互換性が変更される場合があります。詳細については、「{0}」を参照してください。</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">'{0}' サフィックスを持つ 'LibraryImport' への変換</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">マーシャラー型に必要な図形がありません</target>
<target state="translated">'LibraryImport'로 변환</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">컴파일 타임에 P/Invoke 마샬링 코드를 생성하려면 'DllImportAttribute' 대신 'LibraryImportAttribute'를 사용하세요.</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">컴파일 타임에 P/Invoke 마샬링 코드를 생성하려면 'DllImportAttribute' 대신 'LibraryImportAttribute'를 사용하여 '{0}' 메서드를 표시하세요.</target>
<target state="translated">컴파일 타임에 P/Invoke 마샬링 코드를 생성하려면 'DllImportAttribute' 대신 'LibraryImportAttribute'를 사용하세요.</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">'LibraryImport'로 변환하면 동작과 호환성이 변경될 수 있습니다. 자세한 내용은 {0}을(를) 참조하세요.</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">'{0}' 접미사가 있는 'LibraryImport'로 변환</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">마샬러 형식에 필요한 셰이프가 없습니다.</target>
<target state="translated">Konwertuj na element „LibraryImport”</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Użyj elementu „LibraryImportAttribute” zamiast elementu „DllImportAttribute”, aby wygenerować kod skierowania funkcji P/Invoke w czasie kompilacji</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Oznacz metodę „{0}” za pomocą elementu „LibraryImportAttribute” zamiast elementu „DllImportAttribute”, aby wygenerować kod skierowania funkcji P/Invoke w czasie kompilacji</target>
<target state="translated">Użyj elementu „LibraryImportAttribute” zamiast elementu „DllImportAttribute”, aby wygenerować kod skierowania funkcji P/Invoke w czasie kompilacji</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">Konwertowanie na element „LibraryImport” może zmienić zachowanie i zgodność. Zobacz link {0}, aby uzyskać więcej informacji.</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">Konwertuj na element „LibraryImport” z sufiksem „{0}”</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">Typ marshallera nie ma wymaganego kształtu</target>
<target state="translated">Converter em 'LibraryImport'</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Use 'LibraryImportAttribute' em vez de 'DllImportAttribute' para gerar código de marshalling P/Invoke no tempo de compilação</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Marque o método '{0}' com 'LibraryImportAttribute' em vez de 'DllImportAttribute' para gerar código de marshaling P/Invoke em tempo de compilação</target>
<target state="translated">Use 'LibraryImportAttribute' em vez de 'DllImportAttribute' para gerar código de marshalling P/Invoke no tempo de compilação</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">A conversão para 'LibraryImport' pode alterar o comportamento e a compatibilidade. Consulte {0} para obter mais informações.</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">Converter em 'LibraryImport' com '{0}' sufixo</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">O tipo de marshaller não tem a forma necessária</target>
<target state="translated">Преобразовать в \"LibraryImport\"</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Используйте \"LibraryImportAttribute\" вместо \"DllImportAttribute\" для генерирования кода маршализации P/Invoke во время компиляции</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">Пометьте метод \"{0}\" атрибутом \"LibraryImportAttribute\" вместо \"DllImportAttribute\", чтобы генерировать код маршализации P/Invoke во время компиляции</target>
<target state="translated">Используйте \"LibraryImportAttribute\" вместо \"DllImportAttribute\" для генерирования кода маршализации P/Invoke во время компиляции</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">Преобразование в \"LibraryImport\" может изменить поведение и совместимость. Дополнительные сведения см. в {0}.</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">Преобразовать в \"LibraryImport\" с суффиксом \"{0}\"</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">Тип маршалера не имеет требуемой фигуры</target>
<target state="translated">'LibraryImport' olarak dönüştür</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">P/Invoke sıralama kodunu derleme zamanında oluşturmak için 'DllImportAttribute' yerine 'LibraryImportAttribute' kullanın</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">P/Invoke sıralama kodunu derleme zamanında oluşturmak için '{0}' metodunu, 'DllImportAttribute' yerine 'LibraryImportAttribute' ile işaretleyin</target>
<target state="translated">P/Invoke sıralama kodunu derleme zamanında oluşturmak için 'DllImportAttribute' yerine 'LibraryImportAttribute' kullanın</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">'LibraryImport' olarak dönüştürme işlemi davranışı ve uyumluluğu değiştirebilir. Daha fazla bilgi için bkz. {0}</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">'{0}' soneki ile 'LibraryImport' olarak dönüştür</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">Sıralayıcı türü gerekli şekle sahip değil</target>
<target state="translated">转换为 “LibraryImport”</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">使用 “LibraryImportAttribute” 而不是 “DllImportAttribute” 在编译时生成 P/Invoke 封送代码</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">使用 “LibraryImportAttribute” 而不是 “DllImportAttribute” 标记方法“{0}”,以便在编译时生成 P/Invoke 封送代码</target>
<target state="translated">使用 “LibraryImportAttribute” 而不是 “DllImportAttribute” 在编译时生成 P/Invoke 封送代码</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">转换为 “LibraryImport” 可能会更改行为和兼容性。有关详细信息,请参阅 {0}。</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">转换为带“{0}”后缀的 “LibraryImport”</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">封送程序类型没有必需的形状</target>
<target state="translated">轉換為 'LibraryImport'</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportAddUnsafe">
+ <source>Convert to 'LibraryImport' and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportDescription">
<source>Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">使用 'LibraryImportAttribute' 而非 'DllImportAttribute' 在編譯時間產生 P/Invoke 封送處理程式碼</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportMayRequireCustomMarshalling">
+ <source>Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</source>
+ <target state="new">Converting this API to 'LibraryImport' will require additional code to provide custom marshallers for some parameters.</target>
+ <note />
+ </trans-unit>
<trans-unit id="ConvertToLibraryImportMessage">
<source>Mark the method '{0}' with 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time</source>
<target state="translated">以 'LibraryImportAttribute' 代替 'DllImportAttribute' 標示方法'{0}',以在編譯時間產生 P/Invoke 封送處理程式碼</target>
<target state="translated">使用 'LibraryImportAttribute' 而非 'DllImportAttribute' 在編譯時間產生 P/Invoke 封送處理程式碼</target>
<note />
</trans-unit>
- <trans-unit id="ConvertToLibraryImportWarning">
- <source>Conversion to 'LibraryImport' may change behavior and compatibility. See {0} for more information.</source>
- <target state="translated">轉換為 'LibraryImport' 可能會變更行為和兼容性。如需詳細資訊,請參閱 {0}。</target>
- <note>{0} is a documentation link</note>
- </trans-unit>
<trans-unit id="ConvertToLibraryImportWithSuffix">
<source>Convert to 'LibraryImport' with '{0}' suffix</source>
<target state="translated">轉換為具有 '{0}'後綴的 'LibraryImport'</target>
<note />
</trans-unit>
+ <trans-unit id="ConvertToLibraryImportWithSuffixAddUnsafe">
+ <source>Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</source>
+ <target state="new">Convert to 'LibraryImport' with '{0}' suffix and enable unsafe code</target>
+ <note />
+ </trans-unit>
<trans-unit id="CustomMarshallerTypeMustHaveRequiredShapeTitle">
<source>Marshaller type does not have the required shape</source>
<target state="translated">封送處理器類型沒有必要的圖形</target>
--- /dev/null
+// 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.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.Interop
+{
+ public static class CompilationExtensions
+ {
+ public static StubEnvironment CreateStubEnvironment(this Compilation compilation)
+ {
+ TargetFramework targetFramework = DetermineTargetFramework(compilation, out Version targetFrameworkVersion);
+ return new StubEnvironment(
+ compilation,
+ targetFramework,
+ targetFrameworkVersion,
+ compilation.SourceModule.GetAttributes().Any(attr => attr.AttributeClass?.ToDisplayString() == TypeNames.System_Runtime_CompilerServices_SkipLocalsInitAttribute));
+
+ static TargetFramework DetermineTargetFramework(Compilation compilation, out Version version)
+ {
+ IAssemblySymbol systemAssembly = compilation.GetSpecialType(SpecialType.System_Object).ContainingAssembly;
+ version = systemAssembly.Identity.Version;
+
+ return systemAssembly.Identity.Name switch
+ {
+ // .NET Framework
+ "mscorlib" => TargetFramework.Framework,
+ // .NET Standard
+ "netstandard" => TargetFramework.Standard,
+ // .NET Core (when version < 5.0) or .NET
+ "System.Runtime" or "System.Private.CoreLib" =>
+ (version.Major < 5) ? TargetFramework.Core : TargetFramework.Net,
+ _ => TargetFramework.Unknown,
+ };
+ }
+ }
+ }
+}
{
public static IncrementalValueProvider<StubEnvironment> CreateStubEnvironmentProvider(this IncrementalGeneratorInitializationContext context)
{
- return context.CompilationProvider
- .Select(static (compilation, ct) =>
- {
- TargetFramework targetFramework = DetermineTargetFramework(compilation, out Version targetFrameworkVersion);
- return (compilation, targetFramework, targetFrameworkVersion);
- })
- .Select(
- static (data, ct) =>
- new StubEnvironment(
- data.compilation,
- data.targetFramework,
- data.targetFrameworkVersion,
- data.compilation.SourceModule.GetAttributes().Any(attr => attr.AttributeClass?.ToDisplayString() == TypeNames.System_Runtime_CompilerServices_SkipLocalsInitAttribute))
- );
-
- static TargetFramework DetermineTargetFramework(Compilation compilation, out Version version)
- {
- IAssemblySymbol systemAssembly = compilation.GetSpecialType(SpecialType.System_Object).ContainingAssembly;
- version = systemAssembly.Identity.Version;
-
- return systemAssembly.Identity.Name switch
- {
- // .NET Framework
- "mscorlib" => TargetFramework.Framework,
- // .NET Standard
- "netstandard" => TargetFramework.Standard,
- // .NET Core (when version < 5.0) or .NET
- "System.Runtime" or "System.Private.CoreLib" =>
- (version.Major < 5) ? TargetFramework.Core : TargetFramework.Net,
- _ => TargetFramework.Unknown,
- };
- }
+ return context.CompilationProvider.Select(static (comp, ct) => comp.CreateStubEnvironment());
}
public static void RegisterDiagnostics(this IncrementalGeneratorInitializationContext context, IncrementalValuesProvider<Diagnostic> diagnostics)
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.Interop.Analyzers;
using Xunit;
using static Microsoft.Interop.Analyzers.ConvertToLibraryImportFixer;
[ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)]
public class ConvertToLibraryImportFixerTests
{
- private const string ConvertToLibraryImportKey = "ConvertToLibraryImport";
+ private const string ConvertToLibraryImportKey = "ConvertToLibraryImport,";
[Fact]
public async Task Basic()
[LibraryImport(""DoesNotExist"")]
public static partial int {{|CS8795:Method|}}(out int ret);
}}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
// < ... >
public static partial int {{|CS8795:Method2|}}(out int ret);
}}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
[return: MarshalAs(UnmanagedType.I4)]
public static partial int {{|CS8795:Method2|}}(out int ret);
}}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
[LibraryImport(""DoesNotExist"", EntryPoint = ""Entry"", StringMarshalling = StringMarshalling.Utf16)]
public static partial string {{|CS8795:Method2|}}(out int ret);
}}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
[LibraryImport(""DoesNotExist"")]
public static partial int {{|CS8795:Method4|}}(out int ret);
}}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
[LibraryImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static partial int {{|CS8795:Method1|}}(out int ret);
}}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
[UnmanagedCallConv(CallConvs = new System.Type[] {{ typeof({callConvType.FullName}) }})]
public static partial int {{|CS8795:Method1|}}(out int ret);
}}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
[LibraryImport(""DoesNotExist"", EntryPoint = ""Entry"", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
public static partial string {{|CS8795:Method|}}(out int ret);
}}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
[LibraryImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceNoSuffix, ConvertToLibraryImportKey);
+ await VerifyCodeFixAsync(source, fixedSourceNoSuffix, ConvertToLibraryImportKey);
string fixedSourceWithSuffix = $@"
using System.Runtime.InteropServices;
partial class Test
[LibraryImport(""DoesNotExist"", EntryPoint = ""Entry{suffix}"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceWithSuffix, $"{ConvertToLibraryImportKey}{suffix}");
+ await VerifyCodeFixAsync(source, fixedSourceWithSuffix, $"{ConvertToLibraryImportKey}{suffix},");
}
[Fact]
[LibraryImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceNoSuffix, ConvertToLibraryImportKey);
+ await VerifyCodeFixAsync(source, fixedSourceNoSuffix, ConvertToLibraryImportKey);
string fixedSourceWithASuffix = $@"
using System.Runtime.InteropServices;
partial class Test
[LibraryImport(""DoesNotExist"", EntryPoint = ""EntryA"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A");
+ await VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A,");
string fixedSourceWithWSuffix = $@"
using System.Runtime.InteropServices;
partial class Test
[LibraryImport(""DoesNotExist"", EntryPoint = ""EntryW"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceWithWSuffix, $"{ConvertToLibraryImportKey}W");
+ await VerifyCodeFixAsync(source, fixedSourceWithWSuffix, $"{ConvertToLibraryImportKey}W,");
}
[Fact]
[LibraryImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceNoSuffix, ConvertToLibraryImportKey);
+ await VerifyCodeFixAsync(source, fixedSourceNoSuffix, ConvertToLibraryImportKey);
string fixedSourceWithASuffix = $@"
using System.Runtime.InteropServices;
partial class Test
[LibraryImport(""DoesNotExist"", EntryPoint = ""EntryA"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A");
+ await VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A,");
}
[Fact]
[LibraryImport(""DoesNotExist"", EntryPoint = EntryPoint + ""A"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A");
+ await VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A,");
}
[Fact]
[LibraryImport(""DoesNotExist"", EntryPoint = ""MethodA"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A");
+ await VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A,");
}
[Fact]
[LibraryImport(""DoesNotExist"", EntryPoint = nameof(Foo) + ""A"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A");
+ await VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A,");
}
[Fact]
[LibraryImport(""DoesNotExist"", EntryPoint = ""MethodA"")]
public static partial void {{|CS8795:Method|}}();
}}";
- await VerifyCS.VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A");
+ await VerifyCodeFixAsync(source, fixedSourceWithASuffix, $"{ConvertToLibraryImportKey}A,");
}
[Fact]
Marshal.ThrowExceptionForHR(Test.Method(1, out value));
}
}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
public static partial int {|CS8795:Method3|}(out int ret);
}
}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool {{|CS8795:Method|}}([MarshalAs(UnmanagedType.Bool)] bool b);
}}";
- await VerifyCS.VerifyCodeFixAsync(
+ await VerifyCodeFixAsync(
source,
fixedSource);
}
+
+ // There's not a good way today to add a unit test for any changes to project settings from a code fix.
+ // Roslyn does special testing for their cases.
+ [Fact]
+ public async Task FixThatAddsUnsafeToProjectUpdatesLibraryImport()
+ {
+ string source = @$"
+using System.Runtime.InteropServices;
+partial class Test
+{{
+ [DllImport(""DoesNotExist"")]
+ public static extern int [|Method|](out int ret);
+}}";
+ // Fixed source will have CS8795 (Partial method must have an implementation) without generator run
+ string fixedSource = @$"
+using System.Runtime.InteropServices;
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ public static partial int {{|CS8795:Method|}}(out int ret);
+}}";
+ await VerifyCodeFixNoUnsafeAsync(
+ source,
+ fixedSource,
+ $"{ConvertToLibraryImportKey}AddUnsafe,");
+ }
+
+ [Fact]
+ public async Task UserBlittableStructConvertsWithNoWarningCodeAction()
+ {
+ string source = @$"
+using System.Runtime.InteropServices;
+
+struct Blittable
+{{
+ private short s;
+ private short t;
+}}
+
+partial class Test
+{{
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](Blittable b);
+}}";
+ // Fixed source will have CS8795 (Partial method must have an implementation) without generator run
+ string fixedSource = @$"
+using System.Runtime.InteropServices;
+
+struct Blittable
+{{
+ private short s;
+ private short t;
+}}
+
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void {{|CS8795:Method|}}(Blittable b);
+}}";
+ await VerifyCodeFixAsync(
+ source,
+ fixedSource,
+ ConvertToLibraryImportKey);
+ }
+
+ [Fact]
+ public async Task UserNonBlittableStructConvertsOnlyWithWarningCodeAction()
+ {
+ string source = @$"
+using System.Runtime.InteropServices;
+
+struct NonBlittable
+{{
+ private string s;
+ private short t;
+}}
+
+partial class Test
+{{
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](NonBlittable b);
+}}";
+ // Fixed source will have CS8795 (Partial method must have an implementation) without generator run
+ string fixedSource = @$"
+using System.Runtime.InteropServices;
+
+struct NonBlittable
+{{
+ private string s;
+ private short t;
+}}
+
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void {{|CS8795:Method|}}(NonBlittable b);
+}}";
+ // Verify that we don't update this signature with the "no additional work required" action.
+ await VerifyCodeFixAsync(
+ source,
+ source,
+ ConvertToLibraryImportKey);
+
+ await VerifyCodeFixAsync(
+ source,
+ fixedSource,
+ $"{ConvertToLibraryImportKey}{ConvertToLibraryImportAnalyzer.MayRequireAdditionalWork},");
+ }
+
+ [Fact]
+ public async Task UserBlittableStructFixAllConvertsOnlyNoWarningLocations()
+ {
+ string source = @$"
+using System.Runtime.InteropServices;
+
+struct Blittable
+{{
+ private short s;
+ private short t;
+}}
+
+struct NonBlittable
+{{
+ private string s;
+ private short t;
+}}
+
+partial class Test
+{{
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](int i);
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](Blittable b);
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](NonBlittable b);
+}}";
+ // Fixed sources will have CS8795 (Partial method must have an implementation) without generator run
+ string blittableOnlyFixedSource = @$"
+using System.Runtime.InteropServices;
+
+struct Blittable
+{{
+ private short s;
+ private short t;
+}}
+
+struct NonBlittable
+{{
+ private string s;
+ private short t;
+}}
+
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void {{|CS8795:Method|}}(int i);
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void {{|CS8795:Method|}}(Blittable b);
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](NonBlittable b);
+}}";
+ // Verify that we only fix the blittable cases, not the non-blittable cases.
+ await VerifyCodeFixAsync(
+ source,
+ blittableOnlyFixedSource,
+ ConvertToLibraryImportKey);
+ }
+
+ [Fact]
+ public async Task UserNonBlittableStructFixAllConvertsAllLocations()
+ {
+ string source = @$"
+using System.Runtime.InteropServices;
+
+struct Blittable
+{{
+ private short s;
+ private short t;
+}}
+
+struct NonBlittable
+{{
+ private string s;
+ private short t;
+}}
+
+partial class Test
+{{
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](int i);
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](Blittable b);
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](NonBlittable b);
+}}";
+ // Fixed sources will have CS8795 (Partial method must have an implementation) without generator run
+ string nonBlittableOnlyFixedSource = @$"
+using System.Runtime.InteropServices;
+
+struct Blittable
+{{
+ private short s;
+ private short t;
+}}
+
+struct NonBlittable
+{{
+ private string s;
+ private short t;
+}}
+
+partial class Test
+{{
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](int i);
+ [DllImport(""DoesNotExist"")]
+ public static extern void [|Method|](Blittable b);
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void {{|CS8795:Method|}}(NonBlittable b);
+}}";
+ // Fixed sources will have CS8795 (Partial method must have an implementation) without generator run
+ string allFixedSource = @$"
+using System.Runtime.InteropServices;
+
+struct Blittable
+{{
+ private short s;
+ private short t;
+}}
+
+struct NonBlittable
+{{
+ private string s;
+ private short t;
+}}
+
+partial class Test
+{{
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void {{|CS8795:Method|}}(int i);
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void {{|CS8795:Method|}}(Blittable b);
+ [LibraryImport(""DoesNotExist"")]
+ public static partial void {{|CS8795:Method|}}(NonBlittable b);
+}}";
+
+ // Verify that we fix all cases when we do fix-all on a P/Invoke with a non-blittable user type which
+ // will require the user to write additional code.
+ await VerifyCodeFixAsync(
+ source,
+ nonBlittableOnlyFixedSource,
+ allFixedSource,
+ $"{ConvertToLibraryImportKey}{ConvertToLibraryImportAnalyzer.MayRequireAdditionalWork},");
+ }
+
+ private static async Task VerifyCodeFixAsync(string source, string fixedSource)
+ {
+ var test = new VerifyCS.Test
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ };
+
+ await test.RunAsync();
+ }
+
+ private static async Task VerifyCodeFixAsync(string source, string fixedSource, string equivalenceKey)
+ {
+ var test = new VerifyCS.Test
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ CodeActionEquivalenceKey = equivalenceKey,
+ };
+
+ test.FixedState.MarkupHandling = Microsoft.CodeAnalysis.Testing.MarkupMode.Allow;
+
+ await test.RunAsync();
+ }
+
+ private static async Task VerifyCodeFixAsync(string source, string fixedSource, string batchFixedSource, string equivalenceKey)
+ {
+ var test = new VerifyCS.Test
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ BatchFixedCode = batchFixedSource,
+ CodeActionEquivalenceKey = equivalenceKey,
+ };
+
+ test.FixedState.MarkupHandling = Microsoft.CodeAnalysis.Testing.MarkupMode.Allow;
+
+ await test.RunAsync();
+ }
+
+ private static async Task VerifyCodeFixNoUnsafeAsync(string source, string fixedSource, string equivalenceKey)
+ {
+ var test = new TestNoUnsafe
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ CodeActionEquivalenceKey = equivalenceKey,
+ };
+
+ await test.RunAsync();
+ }
+
+ class TestNoUnsafe : VerifyCS.Test
+ {
+ protected override CompilationOptions CreateCompilationOptions() => ((CSharpCompilationOptions)base.CreateCompilationOptions()).WithAllowUnsafe(false);
+ }
}
}