From fc1cb19ab07fb597c5b0b9e0f11ab42b3a4d10da Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 8 May 2023 12:03:05 -0700 Subject: [PATCH] Use Roslyn Source Generator Testing SDK to test interop source generators (#84867) --- eng/Versions.props | 2 +- .../ComInterfaceGenerator.cs | 2 +- .../GeneratorDiagnostics.cs | 2 +- .../ManagedToNativeVTableMethodGenerator.cs | 2 +- .../CallingConventionForwarding.cs | 161 +-- .../CodeSnippets.cs | 4 +- .../ComClassGeneratorOutputShape.cs | 77 +- .../ComInterfaceGenerator.Unit.Tests.csproj | 5 +- .../ComInterfaceGeneratorOutputShape.cs | 126 +-- .../CompileFails.cs | 120 ++- .../Compiles.cs | 269 +++-- .../GeneratedComInterfaceAnalyzerTests.cs | 2 +- .../IVirtualMethodIndexSignatureProvider.cs | 6 +- ...CustomCollectionMarshallingCodeSnippets.cs | 2 +- .../CustomStructMarshallingCodeSnippets.cs | 8 +- .../tests/Common/TestUtils.cs | 2 - .../Verifiers/CSharpAnalyzerVerifier.cs | 2 +- .../Common/Verifiers/CSharpCodeFixVerifier.cs | 48 +- .../CSharpSourceGeneratorVerifier.cs | 170 +++ .../Common/Verifiers/CSharpVerifierHelper.cs | 89 +- ...leRuntimeMarshallingAttributeFixerTests.cs | 85 +- .../AdditionalAttributesOnStub.cs | 112 +- .../AttributeForwarding.cs | 324 +++--- .../CodeSnippets.cs | 181 ++-- .../CompileFails.cs | 977 +++++++++++++++--- .../Compiles.cs | 241 +++-- .../ConvertToLibraryImportAnalyzerTests.cs | 2 +- .../ConvertToLibraryImportFixerTests.cs | 3 +- .../CustomMarshallerAttributeFixerTest.cs | 2 +- ...allerAttributeFixerTests_AttributeUsage.cs | 2 +- ...StatefulLinearCollectionShapeValidation.cs | 2 +- ...FixerTests_StatefulValueShapeValidation.cs | 2 +- ...tatelessLinearCollectionShapeValidation.cs | 2 +- ...ixerTests_StatelessValueShapeValidation.cs | 2 +- .../Diagnostics.cs | 385 +++---- .../IncrementalGenerationTests.cs | 34 +- .../LibraryImportGenerator.Unit.Tests.csproj | 3 + ...NativeMarshallingAttributeAnalyzerTests.cs | 2 +- .../ShapeBreakingDiagnosticSuppressorTests.cs | 2 +- 39 files changed, 2215 insertions(+), 1247 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs diff --git a/eng/Versions.props b/eng/Versions.props index c97134bff4f7c..feab8f380f2fc 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -206,7 +206,7 @@ 2.45.0 - 1.1.2-beta1.22403.2 + 1.1.2-beta1.23205.1 7.0.0-preview-20221010.1 diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs index 4ca18b10a5e5f..77b40b5dc3d2a 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs @@ -550,7 +550,7 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M { if (baseInterface is not null) { - return Diagnostic.Create(GeneratorDiagnostics.MultipleComInterfaceBaseTypesAttribute, syntax.Identifier.GetLocation(), type.ToDisplayString()); + return Diagnostic.Create(GeneratorDiagnostics.MultipleComInterfaceBaseTypes, syntax.Identifier.GetLocation(), type.ToDisplayString()); } baseInterface = implemented; } diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs index 5d563b290942f..9df719d19d51f 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs @@ -187,7 +187,7 @@ public class Ids isEnabledByDefault: true, description: GetResourceString(nameof(SR.InvalidGeneratedComInterfaceAttributeUsageDescription))); - public static readonly DiagnosticDescriptor MultipleComInterfaceBaseTypesAttribute = + public static readonly DiagnosticDescriptor MultipleComInterfaceBaseTypes = new DiagnosticDescriptor( Ids.MultipleComInterfaceBaseTypes, GetResourceString(nameof(SR.MultipleComInterfaceBaseTypesTitle)), diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs index 3792231bfadd7..d6c6564cb2afe 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs @@ -238,7 +238,7 @@ private ParenthesizedExpressionSyntax CreateFunctionPointerExpression( { List functionPointerParameters = new(); var (paramList, retType, _) = _marshallers.GenerateTargetMethodSignatureData(_context); - functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(p.Type))); + functionPointerParameters.AddRange(paramList.Parameters.Select(p => FunctionPointerParameter(attributeLists: default, p.Modifiers, p.Type))); functionPointerParameters.Add(FunctionPointerParameter(retType)); // ((delegate* unmanaged<...>)) diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs index a5e44816cb562..627d5f860eaf5 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs @@ -2,16 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata; -using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Operations; -using Microsoft.Interop.UnitTests; +using Microsoft.CodeAnalysis.Testing; using Xunit; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; + namespace ComInterfaceGenerator.Unit.Tests { public class CallingConventionForwarding @@ -31,16 +31,12 @@ partial interface INativeAPI : IUnmanagedInterfaceType void Method(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); - var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method"); - - Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); - Assert.Empty(signature.UnmanagedCallingConventionTypes); + await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (compilation, signature) => + { + Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); + Assert.Empty(signature.UnmanagedCallingConventionTypes); + }); } [Fact] @@ -59,16 +55,12 @@ partial interface INativeAPI : IUnmanagedInterfaceType void Method(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); - - var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method"); - - Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); - Assert.Equal(newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition"), Assert.Single(signature.UnmanagedCallingConventionTypes), SymbolEqualityComparer.Default); + await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (newComp, signature) => + { + Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); + Assert.Equal(newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition"), Assert.Single(signature.UnmanagedCallingConventionTypes), SymbolEqualityComparer.Default); + }); } [Fact] @@ -87,22 +79,19 @@ partial interface INativeAPI : IUnmanagedInterfaceType void Method(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); - - var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method"); - - Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); - Assert.Empty(signature.UnmanagedCallingConventionTypes); + await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (_, signature) => + { + Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); + Assert.Empty(signature.UnmanagedCallingConventionTypes); + }); } [Fact] public async Task SimpleUnmanagedCallConvAttributeForwarded() { string source = $$""" + using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -115,22 +104,19 @@ partial interface INativeAPI : IUnmanagedInterfaceType void Method(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); - var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method"); - - Assert.Equal(SignatureCallingConvention.CDecl, signature.CallingConvention); - Assert.Empty(signature.UnmanagedCallingConventionTypes); + await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (_, signature) => + { + Assert.Equal(SignatureCallingConvention.CDecl, signature.CallingConvention); + Assert.Empty(signature.UnmanagedCallingConventionTypes); + }); } [Fact] public async Task ComplexUnmanagedCallConvAttributeForwarded() { string source = $$""" + using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -143,28 +129,25 @@ partial interface INativeAPI : IUnmanagedInterfaceType void Method(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); - var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method"); - - Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); - Assert.Equal(new[] + await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (newComp, signature) => { - newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl"), - newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvMemberFunction"), - }, - signature.UnmanagedCallingConventionTypes, - SymbolEqualityComparer.Default); + Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); + Assert.Equal(new[] + { + newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl"), + newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvMemberFunction"), + }, + signature.UnmanagedCallingConventionTypes, + SymbolEqualityComparer.Default); + }); } [Fact] public async Task ComplexUnmanagedCallConvAttributeWithSuppressGCTransitionForwarded() { string source = $$""" + using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -178,41 +161,67 @@ partial interface INativeAPI : IUnmanagedInterfaceType void Method(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.VtableIndexStubGenerator()); - - var signature = await FindFunctionPointerInvocationSignature(newComp, "INativeAPI", "Method"); + await VerifySourceGeneratorAsync(source, "INativeAPI", "Method", (newComp, signature) => + { + Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); + Assert.Equal(new[] + { + newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition"), + newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl"), + newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvMemberFunction"), + }, + signature.UnmanagedCallingConventionTypes, + SymbolEqualityComparer.Default); + }); + } - Assert.Equal(SignatureCallingConvention.Unmanaged, signature.CallingConvention); - Assert.Equal(new[] + private static async Task VerifySourceGeneratorAsync(string source, string interfaceName, string methodName, Action signatureValidator) + { + CallingConventionForwardingTest test = new(interfaceName, methodName, signatureValidator) { - newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition"), - newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvCdecl"), - newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvMemberFunction"), - }, - signature.UnmanagedCallingConventionTypes, - SymbolEqualityComparer.Default); + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; + + await test.RunAsync(); } - private static async Task FindFunctionPointerInvocationSignature(Compilation compilation, string userDefinedInterfaceName, string methodName) + class CallingConventionForwardingTest : VerifyCS.Test { - INamedTypeSymbol? userDefinedInterface = compilation.Assembly.GetTypeByMetadataName(userDefinedInterfaceName); - Assert.NotNull(userDefinedInterface); + private readonly Action _signatureValidator; + private readonly string _interfaceName; + private readonly string _methodName; + + public CallingConventionForwardingTest(string interfaceName, string methodName, Action signatureValidator) + : base(referenceAncillaryInterop: true) + { + _signatureValidator = signatureValidator; + _interfaceName = interfaceName; + _methodName = methodName; + } + + protected override void VerifyFinalCompilation(Compilation compilation) + { + _signatureValidator(compilation, FindFunctionPointerInvocationSignature(compilation)); + } + private IMethodSymbol FindFunctionPointerInvocationSignature(Compilation compilation) + { + INamedTypeSymbol? userDefinedInterface = compilation.Assembly.GetTypeByMetadataName(_interfaceName); + Assert.NotNull(userDefinedInterface); - INamedTypeSymbol generatedInterfaceImplementation = Assert.Single(userDefinedInterface.GetTypeMembers("Native")); + INamedTypeSymbol generatedInterfaceImplementation = Assert.Single(userDefinedInterface.GetTypeMembers("Native")); - IMethodSymbol methodImplementation = Assert.Single(generatedInterfaceImplementation.GetMembers($"global::{userDefinedInterfaceName}.{methodName}").OfType()); + IMethodSymbol methodImplementation = Assert.Single(generatedInterfaceImplementation.GetMembers($"global::{_interfaceName}.{_methodName}").OfType()); - SyntaxNode emittedImplementationSyntax = await methodImplementation.DeclaringSyntaxReferences[0].GetSyntaxAsync(); + SyntaxNode emittedImplementationSyntax = methodImplementation.DeclaringSyntaxReferences[0].GetSyntax(); - SemanticModel model = compilation.GetSemanticModel(emittedImplementationSyntax.SyntaxTree); + SemanticModel model = compilation.GetSemanticModel(emittedImplementationSyntax.SyntaxTree); - IOperation body = model.GetOperation(emittedImplementationSyntax)!; + IOperation body = model.GetOperation(emittedImplementationSyntax)!; - return Assert.Single(body.Descendants().OfType()).GetFunctionPointerSignature(); + return Assert.Single(body.Descendants().OfType()).GetFunctionPointerSignature(); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs index 459fd323c6319..9a1ab29d00219 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs @@ -126,7 +126,7 @@ public string BasicParametersAndModifiers(string typeName, string methodModifier partial interface INativeAPI { {{VirtualMethodIndex(0)}} - {{methodModifiers}} {{typeName}} Method({{typeName}} value, in {{typeName}} inValue, ref {{typeName}} refValue, out {{typeName}} outValue); + {{methodModifiers}} {{typeName}} {|#0:Method|}({{typeName}} {|#1:value|}, in {{typeName}} {|#2:inValue|}, ref {{typeName}} {|#3:refValue|}, out {{typeName}} {|#4:outValue|}); } {{_attributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}} """; @@ -277,7 +277,7 @@ partial interface IOtherComInterface void MethodA(); } {{GeneratedComInterface}} - partial interface IComInterface2 : IComInterface, IOtherComInterface + partial interface {|#0:IComInterface2|} : IComInterface, IOtherComInterface { void Method2(); } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComClassGeneratorOutputShape.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComClassGeneratorOutputShape.cs index 2bb8d5e187ea3..cc3dc85bbe5e4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComClassGeneratorOutputShape.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComClassGeneratorOutputShape.cs @@ -5,9 +5,11 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.Interop.UnitTests; +using Microsoft.CodeAnalysis.Testing; using Xunit; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; + namespace ComInterfaceGenerator.Unit.Tests { public class ComClassGeneratorOutputShape @@ -27,15 +29,8 @@ partial interface INativeAPI [GeneratedComClass] partial class C : INativeAPI {} """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComClassGenerator()); - TestUtils.AssertPostSourceGeneratorCompilation(newComp); - // We'll create one syntax tree for the new interface. - Assert.Equal(comp.SyntaxTrees.Count() + 1, newComp.SyntaxTrees.Count()); - VerifyShape(newComp, "C"); + await VerifySourceGeneratorAsync(source, "C"); } [Fact] @@ -69,36 +64,56 @@ partial class E : C { } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComClassGenerator()); - TestUtils.AssertPostSourceGeneratorCompilation(newComp); - // We'll create one syntax tree per user-defined interface. - Assert.Equal(comp.SyntaxTrees.Count() + 3, newComp.SyntaxTrees.Count()); + await VerifySourceGeneratorAsync(source, "C", "D", "E"); + } + + private static async Task VerifySourceGeneratorAsync(string source, params string[] typeNames) + { + GeneratedShapeTest test = new(typeNames) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; - VerifyShape(newComp, "C"); - VerifyShape(newComp, "D"); - VerifyShape(newComp, "E"); + await test.RunAsync(); } - private static void VerifyShape(Compilation comp, string userDefinedClassMetadataName) + class GeneratedShapeTest : VerifyCS.Test { - INamedTypeSymbol? userDefinedClass = comp.Assembly.GetTypeByMetadataName(userDefinedClassMetadataName); - Assert.NotNull(userDefinedClass); + private readonly string[] _typeNames; - INamedTypeSymbol? comExposedClassAttribute = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.ComExposedClassAttribute`1"); + public GeneratedShapeTest(params string[] typeNames) + :base(referenceAncillaryInterop: false) + { + _typeNames = typeNames; + } - Assert.NotNull(comExposedClassAttribute); + protected override void VerifyFinalCompilation(Compilation compilation) + { + // Generate one source file per attributed interface. + Assert.Equal(TestState.Sources.Count + _typeNames.Length, compilation.SyntaxTrees.Count()); + Assert.All(_typeNames, name => VerifyShape(compilation, name)); + } - AttributeData iUnknownDerivedAttribute = Assert.Single( - userDefinedClass.GetAttributes(), - attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, comExposedClassAttribute)); + private static void VerifyShape(Compilation comp, string userDefinedClassMetadataName) + { + INamedTypeSymbol? userDefinedClass = comp.Assembly.GetTypeByMetadataName(userDefinedClassMetadataName); + Assert.NotNull(userDefinedClass); - Assert.Collection(Assert.IsAssignableFrom(iUnknownDerivedAttribute.AttributeClass).TypeArguments, - infoType => - { - Assert.True(Assert.IsAssignableFrom(infoType).IsFileLocal); - }); + INamedTypeSymbol? comExposedClassAttribute = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.ComExposedClassAttribute`1"); + + Assert.NotNull(comExposedClassAttribute); + + AttributeData iUnknownDerivedAttribute = Assert.Single( + userDefinedClass.GetAttributes(), + attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, comExposedClassAttribute)); + + Assert.Collection(Assert.IsAssignableFrom(iUnknownDerivedAttribute.AttributeClass).TypeArguments, + infoType => + { + Assert.True(Assert.IsAssignableFrom(infoType).IsFileLocal); + }); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj index 64e7dd10a070d..6766f8e39fb05 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppCurrent) @@ -27,6 +27,8 @@ Link="Verifiers\CSharpAnalyzerVerifier.cs"/> + @@ -35,6 +37,7 @@ + diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs index 4b1eafc9c8240..d9063c07a7860 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs @@ -8,12 +8,16 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; +using Microsoft; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Testing; using Microsoft.Interop.UnitTests; using Xunit; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; + namespace ComInterfaceGenerator.Unit.Tests { public class ComInterfaceGeneratorOutputShape @@ -32,15 +36,8 @@ partial interface INativeAPI void Method2(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator()); - TestUtils.AssertPostSourceGeneratorCompilation(newComp); - // We'll create one syntax tree for the new interface. - Assert.Equal(comp.SyntaxTrees.Count() + 1, newComp.SyntaxTrees.Count()); - VerifyShape(newComp, "INativeAPI"); + await VerifySourceGeneratorAsync(source, "INativeAPI"); } [Fact] @@ -63,16 +60,8 @@ partial interface J void Method2(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator()); - TestUtils.AssertPostSourceGeneratorCompilation(newComp); - // We'll create one syntax tree per user-defined interface. - Assert.Equal(comp.SyntaxTrees.Count() + 2, newComp.SyntaxTrees.Count()); - VerifyShape(newComp, "I"); - VerifyShape(newComp, "J"); + await VerifySourceGeneratorAsync(source, "I", "J"); } [Fact] @@ -99,17 +88,8 @@ partial interface J void Method2(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator()); - TestUtils.AssertPostSourceGeneratorCompilation(newComp); - // We'll create one syntax tree per user-defined interface. - Assert.Equal(comp.SyntaxTrees.Count() + 3, newComp.SyntaxTrees.Count()); - - VerifyShape(newComp, "I"); - VerifyShape(newComp, "Empty"); - VerifyShape(newComp, "J"); + await VerifySourceGeneratorAsync(source, "I", "Empty", "J"); } [Fact] @@ -132,47 +112,67 @@ partial interface J : I void MethodB(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.ComInterfaceGenerator()); - TestUtils.AssertPostSourceGeneratorCompilation(newComp); - // We'll create one syntax tree per user-defined interface. - Assert.Equal(comp.SyntaxTrees.Count() + 2, newComp.SyntaxTrees.Count()); - VerifyShape(newComp, "I"); - VerifyShape(newComp, "J"); + await VerifySourceGeneratorAsync(source, "I", "J"); } - private static void VerifyShape(Compilation comp, string userDefinedInterfaceMetadataName) + private static async Task VerifySourceGeneratorAsync(string source, params string[] typeNames) { - INamedTypeSymbol? userDefinedInterface = comp.Assembly.GetTypeByMetadataName(userDefinedInterfaceMetadataName); - Assert.NotNull(userDefinedInterface); - - INamedTypeSymbol? iUnknownDerivedAttributeType = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute`2"); + GeneratedShapeTest test = new(typeNames) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; - Assert.NotNull(iUnknownDerivedAttributeType); - - AttributeData iUnknownDerivedAttribute = Assert.Single( - userDefinedInterface.GetAttributes(), - attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, iUnknownDerivedAttributeType)); - - Assert.Collection(Assert.IsAssignableFrom(iUnknownDerivedAttribute.AttributeClass).TypeArguments, - infoType => - { - Assert.True(Assert.IsAssignableFrom(infoType).IsFileLocal); - }, - implementationType => - { - Assert.True(Assert.IsAssignableFrom(implementationType).IsFileLocal); - Assert.Contains(userDefinedInterface, implementationType.Interfaces, SymbolEqualityComparer.Default); - Assert.Contains(implementationType.GetAttributes(), attr => attr.AttributeClass?.ToDisplayString() == typeof(DynamicInterfaceCastableImplementationAttribute).FullName); - Assert.All(userDefinedInterface.GetMembers().OfType().Where(method => method.IsAbstract && !method.IsStatic), - method => - { - Assert.NotNull(implementationType.FindImplementationForInterfaceMember(method)); - }); - }); + await test.RunAsync(); + } + class GeneratedShapeTest : VerifyCS.Test + { + private readonly string[] _typeNames; + + public GeneratedShapeTest(params string[] typeNames) + : base(referenceAncillaryInterop: false) + { + _typeNames = typeNames; + } + + protected override void VerifyFinalCompilation(Compilation compilation) + { + // Generate one source file per attributed interface. + Assert.Equal(TestState.Sources.Count + _typeNames.Length, compilation.SyntaxTrees.Count()); + Assert.All(_typeNames, name => VerifyShape(compilation, name)); + } + + private static void VerifyShape(Compilation comp, string userDefinedInterfaceMetadataName) + { + INamedTypeSymbol? userDefinedInterface = comp.Assembly.GetTypeByMetadataName(userDefinedInterfaceMetadataName); + Assert.NotNull(userDefinedInterface); + + INamedTypeSymbol? iUnknownDerivedAttributeType = comp.GetTypeByMetadataName("System.Runtime.InteropServices.Marshalling.IUnknownDerivedAttribute`2"); + + Assert.NotNull(iUnknownDerivedAttributeType); + + AttributeData iUnknownDerivedAttribute = Assert.Single( + userDefinedInterface.GetAttributes(), + attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass?.OriginalDefinition, iUnknownDerivedAttributeType)); + + Assert.Collection(Assert.IsAssignableFrom(iUnknownDerivedAttribute.AttributeClass).TypeArguments, + infoType => + { + Assert.True(Assert.IsAssignableFrom(infoType).IsFileLocal); + }, + implementationType => + { + Assert.True(Assert.IsAssignableFrom(implementationType).IsFileLocal); + Assert.Contains(userDefinedInterface, implementationType.Interfaces, SymbolEqualityComparer.Default); + Assert.Contains(implementationType.GetAttributes(), attr => attr.AttributeClass?.ToDisplayString() == typeof(DynamicInterfaceCastableImplementationAttribute).FullName); + Assert.All(userDefinedInterface.GetMembers().OfType().Where(method => method.IsAbstract && !method.IsStatic), + method => + { + Assert.NotNull(implementationType.FindImplementationForInterfaceMember(method)); + }); + }); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs index d69ce9e2c7941..e4db5b789ec8a 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs @@ -11,9 +11,16 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; using Microsoft.Interop.UnitTests; using Xunit; +using System.Diagnostics; + + +using VerifyComInterfaceGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; +using Microsoft.Interop; + namespace ComInterfaceGenerator.Unit.Tests { public class CompileFails @@ -27,31 +34,106 @@ public static IEnumerable ComInterfaceGeneratorSnippetsToCompile() { CodeSnippets codeSnippets = new(new GeneratedComInterfaceAttributeProvider()); // Inheriting from multiple GeneratedComInterface-marked interfaces. - yield return new object[] { ID(), codeSnippets.DerivedComInterfaceTypeMultipleComInterfaceBases, 1, 0 }; + yield return new object[] { ID(), codeSnippets.DerivedComInterfaceTypeMultipleComInterfaceBases, new[] { + VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.MultipleComInterfaceBaseTypes) + .WithLocation(0) + .WithArguments("IComInterface2") + } }; } [Theory] [MemberData(nameof(ComInterfaceGeneratorSnippetsToCompile))] - public async Task ValidateComInterfaceGeneratorSnippets(string id, string source, int expectedGeneratorErrors, int expectedCompilerErrors) + public async Task ValidateComInterfaceGeneratorSnippets(string id, string source, DiagnosticResult[] expectedDiagnostics) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.ComInterfaceGenerator()); - - // Verify the compilation failed with errors. - IEnumerable generatorErrors = generatorDiags.Where(d => d.Severity == DiagnosticSeverity.Error); - int generatorErrorCount = generatorErrors.Count(); - Assert.True( - expectedGeneratorErrors == generatorErrorCount, - $"Expected {expectedGeneratorErrors} errors, but encountered {generatorErrorCount}. Errors: {string.Join(Environment.NewLine, generatorErrors.Select(d => d.ToString()))}"); - - IEnumerable compilerErrors = newComp.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error); - int compilerErrorCount = compilerErrors.Count(); - Assert.True( - expectedCompilerErrors == compilerErrorCount, - $"Expected {expectedCompilerErrors} errors, but encountered {compilerErrorCount}. Errors: {string.Join(Environment.NewLine, compilerErrors.Select(d => d.ToString()))}"); + await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostics); + } + + private static IComInterfaceAttributeProvider GetAttributeProvider(GeneratorKind generator) + => generator switch + { + GeneratorKind.VTableIndexStubGenerator => new VirtualMethodIndexAttributeProvider(), + GeneratorKind.ComInterfaceGenerator => new GeneratedComInterfaceAttributeProvider(), + _ => throw new UnreachableException(), + }; + + public static IEnumerable InvalidUnmanagedToManagedCodeSnippetsToCompile(GeneratorKind generator) + { + CodeSnippets codeSnippets = new(GetAttributeProvider(generator)); + + // SafeHandles + yield return new object[] { ID(), codeSnippets.BasicParametersAndModifiers("Microsoft.Win32.SafeHandles.SafeFileHandle"), new[] + { + VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails).WithLocation(0).WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "Method"), + VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails).WithLocation(1).WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "value"), + // /0/Test0.cs(13,151): error SYSLIB1051: The type 'Microsoft.Win32.SafeHandles.SafeFileHandle' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter 'inValue'. + VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails).WithLocation(2).WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "inValue"), + // /0/Test0.cs(13,207): error SYSLIB1051: The type 'Microsoft.Win32.SafeHandles.SafeFileHandle' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter 'refValue'. + VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails).WithLocation(3).WithArguments("The specified parameter needs to be marshalled from managed to unmanaged and unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "refValue"), + // /0/Test0.cs(13,264): error SYSLIB1051: The type 'Microsoft.Win32.SafeHandles.SafeFileHandle' is not supported by source-generated P/Invokes. The generated source will not handle marshalling of parameter 'outValue'. + VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails).WithLocation(4).WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "outValue"), + } }; + + + // Marshallers with only support for their expected places in the signatures in + // ManagedToUnmanaged marshal modes. + + DiagnosticResult invalidManagedToUnmanagedParameterDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::Marshaller' does not support it.", "value"); + DiagnosticResult invalidUnmanagedToManagedParameterDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::Marshaller' does not support it.", "value"); + DiagnosticResult invalidReturnTypeDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::Marshaller' does not support it.", "Method"); + CustomStructMarshallingCodeSnippets customStructMarshallingCodeSnippets = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator))); + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.NativeToManagedOnlyOutParameter, new[] { invalidManagedToUnmanagedParameterDiagnostic } }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.NativeToManagedOnlyReturnValue, new[] { invalidReturnTypeDiagnostic } }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.ByValueInParameter, new[] { invalidUnmanagedToManagedParameterDiagnostic } }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.NativeToManagedOnlyOutParameter, new[] { invalidManagedToUnmanagedParameterDiagnostic } }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.NativeToManagedOnlyReturnValue, new[] { invalidReturnTypeDiagnostic } }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.ByValueInParameter, new[] { invalidUnmanagedToManagedParameterDiagnostic } }; + } + + public static IEnumerable InvalidManagedToUnmanagedCodeSnippetsToCompile(GeneratorKind generator) + { + // Marshallers with only support for their expected places in the signatures in + // UnmanagedToManaged marshal modes. + CustomStructMarshallingCodeSnippets customStructMarshallingCodeSnippets = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator))); + + yield return new[] { ID(), customStructMarshallingCodeSnippets.Stateless.NativeToManagedOnlyInParameter }; + yield return new[] { ID(), customStructMarshallingCodeSnippets.Stateless.ByValueOutParameter }; + yield return new[] { ID(), customStructMarshallingCodeSnippets.Stateful.NativeToManagedOnlyInParameter }; + yield return new[] { ID(), customStructMarshallingCodeSnippets.Stateful.ByValueOutParameter }; + } + + [Theory] + [MemberData(nameof(InvalidUnmanagedToManagedCodeSnippetsToCompile), GeneratorKind.ComInterfaceGenerator)] + public async Task ValidateInvalidUnmanagedToManagedCodeSnippets(string id, string source, DiagnosticResult[] expectedDiagnostics) + { + _ = id; + VerifyComInterfaceGenerator.Test test = new(referenceAncillaryInterop: false) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck, + // Our fallback mechanism for invalid code for unmanaged->managed stubs sometimes generates invalid code. + CompilerDiagnostics = CompilerDiagnostics.None, + }; + test.ExpectedDiagnostics.AddRange(expectedDiagnostics); + await test.RunAsync(); + } + + [Theory] + [MemberData(nameof(InvalidManagedToUnmanagedCodeSnippetsToCompile), GeneratorKind.ComInterfaceGenerator)] + public async Task ValidateInvalidManagedToUnmanagedCodeSnippets(string id, string source) + { + _ = id; + + DiagnosticResult expectedDiagnostic = VerifyComInterfaceGenerator.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::Marshaller' does not support it.", "value"); + await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source, expectedDiagnostic); } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs index 4a0dd07143bd1..89c1e84fd5ab8 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs @@ -11,6 +11,10 @@ using Microsoft.Interop.UnitTests; using Xunit; +using VerifyVTableGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; +using VerifyComInterfaceGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; +using Microsoft.CodeAnalysis.Testing; + namespace ComInterfaceGenerator.Unit.Tests { public class Compiles @@ -65,6 +69,39 @@ public static IEnumerable CodeSnippetsToCompile(GeneratorKind generato yield return new[] { ID(), codeSnippets.BasicParametersAndModifiersNoImplicitThis() }; yield return new[] { ID(), codeSnippets.BasicParametersAndModifiersNoImplicitThis() }; + // Custom type marshalling bidirectional + CustomStructMarshallingCodeSnippets customStructMarshallingCodeSnippetsBidirectional = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator))); + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateless.ParametersAndModifiers }; + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateless.MarshalUsingParametersAndModifiers }; + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateless.RefParameter }; + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateless.OptionalStackallocParametersAndModifiers }; + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.ParametersAndModifiers }; + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.ParametersAndModifiersWithFree }; + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.ParametersAndModifiersWithOnInvoked }; + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.MarshalUsingParametersAndModifiers }; + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.RefParameter }; + yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.OptionalStackallocParametersAndModifiers }; + + // Exception Handling + // HResult + yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("int") }; + yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("uint") }; + // NaN + yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("float") }; + yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("double") }; + // Default Value + yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("nint") }; + // Void + yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("void") }; + } + + public static IEnumerable ManagedToUnmanagedCodeSnippetsToCompile(GeneratorKind generator) + { + CodeSnippets codeSnippets = new(GetAttributeProvider(generator)); + + // SafeHandles + yield return new[] { ID(), codeSnippets.BasicParametersAndModifiersManagedToUnmanaged("Microsoft.Win32.SafeHandles.SafeFileHandle") }; + // Custom type marshalling managed-to-unmanaged CustomStructMarshallingCodeSnippets customStructMarshallingCodeSnippetsManagedToUnmanaged = new(new CodeSnippets.ManagedToUnmanaged(GetAttributeProvider(generator))); yield return new[] { ID(), customStructMarshallingCodeSnippetsManagedToUnmanaged.Stateless.ParametersAndModifiers }; @@ -98,7 +135,10 @@ public static IEnumerable CodeSnippetsToCompile(GeneratorKind generato yield return new[] { ID(), customStructMarshallingCodeSnippetsManagedToUnmanaged.Stateful.OptionalStackallocParametersAndModifiers }; yield return new[] { ID(), customStructMarshallingCodeSnippetsManagedToUnmanaged.Stateful.DefaultModeByValueInParameter }; yield return new[] { ID(), customStructMarshallingCodeSnippetsManagedToUnmanaged.Stateful.DefaultModeReturnValue }; + } + public static IEnumerable UnmanagedToManagedCodeSnippetsToCompile(GeneratorKind generator) + { // Custom type marshalling unmanaged-to-managed CustomStructMarshallingCodeSnippets customStructMarshallingCodeSnippetsUnmanagedToManaged = new(new CodeSnippets.UnmanagedToManaged(GetAttributeProvider(generator))); yield return new[] { ID(), customStructMarshallingCodeSnippetsUnmanagedToManaged.Stateless.ParametersAndModifiers }; @@ -117,39 +157,85 @@ public static IEnumerable CodeSnippetsToCompile(GeneratorKind generato yield return new[] { ID(), customStructMarshallingCodeSnippetsUnmanagedToManaged.Stateful.ByValueOutParameter }; yield return new[] { ID(), customStructMarshallingCodeSnippetsUnmanagedToManaged.Stateful.RefParameter }; yield return new[] { ID(), customStructMarshallingCodeSnippetsUnmanagedToManaged.Stateful.OptionalStackallocParametersAndModifiers }; - - // Custom type marshalling bidirectional - CustomStructMarshallingCodeSnippets customStructMarshallingCodeSnippetsBidirectional = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator))); - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateless.ParametersAndModifiers }; - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateless.MarshalUsingParametersAndModifiers }; - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateless.RefParameter }; - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateless.OptionalStackallocParametersAndModifiers }; - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.ParametersAndModifiers }; - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.ParametersAndModifiersWithFree }; - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.ParametersAndModifiersWithOnInvoked }; - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.MarshalUsingParametersAndModifiers }; - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.RefParameter }; - yield return new[] { ID(), customStructMarshallingCodeSnippetsBidirectional.Stateful.OptionalStackallocParametersAndModifiers }; - - // SafeHandles - yield return new[] { ID(), codeSnippets.BasicParametersAndModifiersManagedToUnmanaged("Microsoft.Win32.SafeHandles.SafeFileHandle") }; - - // Exception Handling - // HResult - yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("int") }; - yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("uint") }; - // NaN - yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("float") }; - yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("double") }; - // Default Value - yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("nint") }; - // Void - yield return new[] { ID(), codeSnippets.BasicReturnTypeComExceptionHandling("void") }; } public static IEnumerable CustomCollections(GeneratorKind generator) { // Custom collection marshalling + CodeSnippets codeSnippets = new(GetAttributeProvider(generator)); + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; + + CustomCollectionMarshallingCodeSnippets customCollectionMarshallingCodeSnippetsBidirectional = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator))); + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerReturnValueLength() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.NestedMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.NonBlittableElementParametersAndModifiers }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomElementMarshalling }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerReturnValueLength() }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.NonBlittableElementParametersAndModifiers }; + yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomElementMarshalling }; + } + + public static IEnumerable CustomCollectionsManagedToUnmanaged(GeneratorKind generator) + { CustomCollectionMarshallingCodeSnippets customCollectionMarshallingCodeSnippetsManagedToUnmanaged = new(new CodeSnippets.ManagedToUnmanaged(GetAttributeProvider(generator))); yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.ByValue() }; yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.ByValue() }; @@ -233,94 +319,19 @@ public static IEnumerable CustomCollections(GeneratorKind generator) yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NonBlittableElementByValue }; yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NonBlittableElementNativeToManagedOnlyOutParameter }; yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateful.NonBlittableElementNativeToManagedOnlyReturnValue }; - - CodeSnippets codeSnippets = new(GetAttributeProvider(generator)); - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers() }; - - CustomCollectionMarshallingCodeSnippets customCollectionMarshallingCodeSnippetsBidirectional = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator))); - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerReturnValueLength() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.NestedMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.NonBlittableElementParametersAndModifiers }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomElementMarshalling }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerReturnValueLength() }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.NonBlittableElementParametersAndModifiers }; - yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomElementMarshalling }; } [Theory] [MemberData(nameof(CodeSnippetsToCompile), GeneratorKind.VTableIndexStubGenerator)] + [MemberData(nameof(ManagedToUnmanagedCodeSnippetsToCompile), GeneratorKind.VTableIndexStubGenerator)] + [MemberData(nameof(UnmanagedToManagedCodeSnippetsToCompile), GeneratorKind.VTableIndexStubGenerator)] + [MemberData(nameof(CustomCollectionsManagedToUnmanaged), GeneratorKind.VTableIndexStubGenerator)] + [MemberData(nameof(CustomCollections), GeneratorKind.VTableIndexStubGenerator)] [MemberData(nameof(CustomCollections), GeneratorKind.VTableIndexStubGenerator)] public async Task ValidateVTableIndexSnippets(string id, string source) { _ = id; - Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation - // We allow duplicate usings here since some of the shared snippets add a using for System.Runtime.InteropServices.Marshalling when we already have one in our base snippets. - TestUtils.AssertPreSourceGeneratorCompilation(comp, "CS0426", "CS0105"); - - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.VtableIndexStubGenerator()); - Assert.Empty(generatorDiags); - - TestUtils.AssertPostSourceGeneratorCompilation(newComp, "CS0105"); + await VerifyVTableGenerator.VerifySourceGeneratorWithAncillaryInteropAsync(source); } public static IEnumerable ComInterfaceSnippetsToCompile() @@ -336,48 +347,8 @@ public static IEnumerable ComInterfaceSnippetsToCompile() public async Task ValidateComInterfaceSnippets(string id, string source) { _ = id; - Compilation comp = await TestUtils.CreateCompilation(source); - // Allow the Native nested type name to be missing in the pre-source-generator compilation - // We allow duplicate usings here since some of the shared snippets add a using for System.Runtime.InteropServices.Marshalling when we already have one in our base snippets. - TestUtils.AssertPreSourceGeneratorCompilation(comp, "CS0426", "CS0105"); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.ComInterfaceGenerator()); - Assert.Empty(generatorDiags.Where(IsValidGeneratorDiagnostic)); - - List allowedDiagnostics = new() - { - // Duplicate 'using' - "CS0105", - // Variable assigned to but never read - "CS0219" - }; - // There are valid warnings from the generator -- - if (generatorDiags.Length != 0) - { - List additionalDiags = new() { - // No overload for 'ABI_Method' matches function pointer 'delegate* unmanaged<...>' - "CS8757", - // Cannot use 'parameterType' as a parameter type on a method attributed with 'UnmanagedCallersOnly'. - "CS8894", - // The out parameter 'paramName' must be assigned to before control leaves the current method - "CS0177", - // Cannot use 'ref', 'in', or 'out' in the signature of a method attributed with 'UnmanagedCallersOnly'. - "CS8977", - // The type 'SafeFileHandle' must be a non-nullable value type, along with all fields at any level of nesting, - // in order to use it as parameter 'T' in the generic type or method 'ExceptionAsDefaultMarshaller' - "CS8377", - // Argument N may not be passed with the 'in' keyword - "CS1615" - }; - allowedDiagnostics.AddRange(additionalDiags); - } - - TestUtils.AssertPostSourceGeneratorCompilation(newComp, allowedDiagnostics.ToArray()); + await VerifyComInterfaceGenerator.VerifySourceGeneratorAsync(source); } - - private bool IsValidGeneratorDiagnostic(Diagnostic diag) - => diag.Id != "SYSLIB1051" - && diag.GetMessage().Contains("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type '") - && diag.GetMessage().Contains("' does not support it. The generated source will not handle marshalling of parameter"); } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs index a77166a211d7c..1258854a44c2c 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs @@ -7,7 +7,7 @@ using Microsoft.Interop.Analyzers; using Xunit; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpAnalyzerVerifier; namespace ComInterfaceGenerator.Unit.Tests { diff --git a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs index 5d4587a04d19b..04746077763d2 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs @@ -67,7 +67,7 @@ string ICustomMarshallingSignatureTestProvider.BasicParameterByValue(string type partial interface INativeAPI { {{AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}} - void Method({{typeName}} value); + void Method({{typeName}} {|#0:value|}); } {{AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}} """; @@ -85,7 +85,7 @@ string ICustomMarshallingSignatureTestProvider.BasicParameterWithByRefModifier(s partial interface INativeAPI { {{AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}} - void Method({{modifier}} {{typeName}} value); + void Method({{modifier}} {{typeName}} {|#0:value|}); } {{AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}} """; @@ -100,7 +100,7 @@ string ICustomMarshallingSignatureTestProvider.BasicReturnType(string typeName, partial interface INativeAPI { {{AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}} - {{typeName}} Method(); + {{typeName}} {|#0:Method|}(); } {{AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}} """; diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/CustomCollectionMarshallingCodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/CustomCollectionMarshallingCodeSnippets.cs index 4144748d319fa..f25a50341e9d0 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Common/CustomCollectionMarshallingCodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/CustomCollectionMarshallingCodeSnippets.cs @@ -278,7 +278,7 @@ public string NestedMarshallerParametersAndModifiers(string elementType) => _pro public string GenericCollectionMarshallingArityMismatch => _provider.BasicParameterByValue("TestCollection", DisableRuntimeMarshalling) + """ - [NativeMarshalling(typeof(Marshaller<,,>))] + [{|#10:NativeMarshalling(typeof(Marshaller<,,>))|}] class TestCollection {} [CustomMarshaller(typeof(TestCollection<>), MarshalMode.Default, typeof(Marshaller<,,>))] diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/CustomStructMarshallingCodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/CustomStructMarshallingCodeSnippets.cs index b3ea19245914e..9b77d5f331153 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Common/CustomStructMarshallingCodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/CustomStructMarshallingCodeSnippets.cs @@ -18,7 +18,7 @@ public CustomStructMarshallingCodeSnippets(ICustomMarshallingSignatureTestProvid private static readonly string UsingSystemRuntimeInteropServicesMarshalling = "using System.Runtime.InteropServices.Marshalling;"; public static string NonBlittableUserDefinedType(bool defineNativeMarshalling = true) => $$""" - {{(defineNativeMarshalling ? "[NativeMarshalling(typeof(Marshaller))]" : string.Empty)}} + {{(defineNativeMarshalling ? "[{|#10:NativeMarshalling(typeof(Marshaller))|}]" : string.Empty)}} public struct S { #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value @@ -240,9 +240,9 @@ public struct Native { } + NonBlittableUserDefinedType() + Ref; - public string StackallocOnlyRefParameter => _provider.BasicParameterWithByRefModifier("ref", "S") - + NonBlittableUserDefinedType() - + InOutBuffer; + public string StackallocOnlyRefParameter => _provider.BasicParameterWithByRefModifier("ref", "S") + + NonBlittableUserDefinedType() + + InOutBuffer; public string OptionalStackallocParametersAndModifiers => _provider.BasicParametersAndModifiers("S", UsingSystemRuntimeInteropServicesMarshalling) + NonBlittableUserDefinedType() diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/TestUtils.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/TestUtils.cs index 81543e8b3d5d8..4d1ec3a5d2c7d 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Common/TestUtils.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/TestUtils.cs @@ -5,8 +5,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.DotNet.XUnitExtensions; -using SourceGenerators.Tests; using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs index 2ce99d0081a0f..08e29e3a5a2fb 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs @@ -8,7 +8,7 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -namespace LibraryImportGenerator.UnitTests.Verifiers +namespace Microsoft.Interop.UnitTests.Verifiers { public static class CSharpAnalyzerVerifier where TAnalyzer : DiagnosticAnalyzer, new() diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs index 00f03f13ec2fa..b63dffca7fa66 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs @@ -14,9 +14,8 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Testing.Verifiers; -using Microsoft.Interop.UnitTests; -namespace LibraryImportGenerator.UnitTests.Verifiers +namespace Microsoft.Interop.UnitTests.Verifiers { public static class CSharpCodeFixVerifier where TAnalyzer : DiagnosticAnalyzer, new() @@ -120,45 +119,7 @@ public Test() TestState.AdditionalReferences.AddRange(SourceGenerators.Tests.LiveReferencePack.GetMetadataReferences()); TestState.AdditionalReferences.Add(TestUtils.GetAncillaryReference()); - SolutionTransforms.Add((solution, projectId) => - { - var project = solution.GetProject(projectId)!; - var compilationOptions = project.CompilationOptions!; - var diagnosticOptions = compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings); - - // Explicitly enable diagnostics that are not enabled by default - var enableAnalyzersOptions = new System.Collections.Generic.Dictionary(); - foreach (var analyzer in GetDiagnosticAnalyzers().ToImmutableArray()) - { - foreach (var diagnostic in analyzer.SupportedDiagnostics) - { - if (diagnostic.IsEnabledByDefault) - continue; - - // Map the default severity to the reporting behaviour. - // We cannot simply use ReportDiagnostic.Default here, as diagnostics that are not enabled by default - // are treated as suppressed (regardless of their default severity). - var report = diagnostic.DefaultSeverity switch - { - DiagnosticSeverity.Error => ReportDiagnostic.Error, - DiagnosticSeverity.Warning => ReportDiagnostic.Warn, - DiagnosticSeverity.Info => ReportDiagnostic.Info, - DiagnosticSeverity.Hidden => ReportDiagnostic.Hidden, - _ => ReportDiagnostic.Default - }; - enableAnalyzersOptions.Add(diagnostic.Id, report); - } - } - - compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( - compilationOptions.SpecificDiagnosticOptions - .SetItems(CSharpVerifierHelper.NullableWarnings) - .AddRange(enableAnalyzersOptions) - .AddRange(TestUtils.BindingRedirectWarnings)); - solution = solution.WithProjectCompilationOptions(projectId, compilationOptions); - solution = solution.WithProjectParseOptions(projectId, ((CSharpParseOptions)project.ParseOptions!).WithLanguageVersion(LanguageVersion.Preview)); - return solution; - }); + SolutionTransforms.Add(CSharpVerifierHelper.GetAllDiagonsticsEnabledTransform(GetDiagnosticAnalyzers())); } protected override CompilationWithAnalyzers CreateCompilationWithAnalyzers(Compilation compilation, ImmutableArray analyzers, AnalyzerOptions options, CancellationToken cancellationToken) @@ -187,6 +148,11 @@ protected override CompilationWithAnalyzers CreateCompilationWithAnalyzers(Compi return true; })); } + + protected override ParseOptions CreateParseOptions() + { + return new CSharpParseOptions(LanguageVersion.Preview, DocumentationMode.Diagnose); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs new file mode 100644 index 0000000000000..167f00029ed45 --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs @@ -0,0 +1,170 @@ +// 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.Immutable; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.CSharp.Testing.XUnit; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Testing.Verifiers; + +namespace Microsoft.Interop.UnitTests.Verifiers +{ + public static class CSharpSourceGeneratorVerifier + where TSourceGenerator : IIncrementalGenerator, new() + { + public static DiagnosticResult Diagnostic(string diagnosticId) + => new DiagnosticResult(diagnosticId, DiagnosticSeverity.Error); + + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => new DiagnosticResult(descriptor); + + /// + /// Create a with the diagnostic message created with the provided arguments. + /// A with the property set instead of just the property + /// binds more strongly to the "correct" diagnostic as the test harness will match the diagnostic on the exact message instead of just on the message arguments. + /// + /// The diagnostic descriptor + /// The arguments to use to format the diagnostic message + /// A with a set with the 's message format and the . + public static DiagnosticResult DiagnosticWithArguments(DiagnosticDescriptor descriptor, params object[] arguments) + { + // Generate the specific message here to ensure a stronger match with the correct diagnostic. + return Diagnostic(descriptor).WithMessage(string.Format(descriptor.MessageFormat.ToString(), arguments)).WithArguments(arguments); + } + + public static async Task VerifySourceGeneratorAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test(referenceAncillaryInterop: false) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + + public static async Task VerifySourceGeneratorWithAncillaryInteropAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test(referenceAncillaryInterop: true) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + + public static async Task VerifySourceGeneratorAsync(string[] sources, params DiagnosticResult[] expected) + { + var test = new Test(referenceAncillaryInterop: false) + { + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; + + foreach (var source in sources) + { + test.TestState.Sources.Add(source); + } + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(CancellationToken.None); + } + + internal class Test : CSharpSourceGeneratorTest + { + public Test(TestTargetFramework targetFramework) + { + if (targetFramework == TestTargetFramework.Net) + { + // Clear out the default reference assemblies. We explicitly add references from the live ref pack, + // so we don't want the Roslyn test infrastructure to resolve/add any default reference assemblies + ReferenceAssemblies = new ReferenceAssemblies(string.Empty); + TestState.AdditionalReferences.AddRange(SourceGenerators.Tests.LiveReferencePack.GetMetadataReferences()); + } + else + { + ReferenceAssemblies = targetFramework switch + { + TestTargetFramework.Framework => ReferenceAssemblies.NetFramework.Net48.Default, + TestTargetFramework.Standard => ReferenceAssemblies.NetStandard.NetStandard21, + TestTargetFramework.Core => ReferenceAssemblies.NetCore.NetCoreApp31, + TestTargetFramework.Net6 => ReferenceAssemblies.Net.Net60, + _ => ReferenceAssemblies.Default + }; + } + SolutionTransforms.Add(CSharpVerifierHelper.GetTargetFrameworkAnalyzerOptionsProviderTransform(targetFramework)); + } + public Test(bool referenceAncillaryInterop) + :this(TestTargetFramework.Net) + { + if (referenceAncillaryInterop) + { + TestState.AdditionalReferences.Add(TestUtils.GetAncillaryReference()); + } + + SolutionTransforms.Add(CSharpVerifierHelper.GetAllDiagonsticsEnabledTransform(GetDiagnosticAnalyzers())); + } + + protected override CompilationWithAnalyzers CreateCompilationWithAnalyzers(Compilation compilation, ImmutableArray analyzers, AnalyzerOptions options, CancellationToken cancellationToken) + { + return new CompilationWithAnalyzers( + compilation, + analyzers, + new CompilationWithAnalyzersOptions( + options, + onAnalyzerException: null, + concurrentAnalysis: !Debugger.IsAttached, + logAnalyzerExecutionTime: true, + reportSuppressedDiagnostics: false, + analyzerExceptionFilter: ex => + { + // We're hunting down a intermittent issue that causes NullReferenceExceptions deep in Roslyn. To ensure that we get an actionable dump, we're going to FailFast here to force a process dump. + if (ex is NullReferenceException) + { + // Break a debugger here so there's a chance to investigate if someone is already attached. + if (System.Diagnostics.Debugger.IsAttached) + { + System.Diagnostics.Debugger.Break(); + } + Environment.FailFast($"Encountered a NullReferenceException while running an analyzer. Taking the process down to get an actionable crash dump. Exception information:{ex.ToString()}"); + } + return true; + })); + } + + protected override ParseOptions CreateParseOptions() + { + return new CSharpParseOptions(LanguageVersion.Preview, DocumentationMode.Diagnose); + } + + protected async override Task<(Compilation compilation, ImmutableArray generatorDiagnostics)> GetProjectCompilationAsync(Project project, IVerifier verifier, CancellationToken cancellationToken) + { + var (compilation, diagnostics) = await base.GetProjectCompilationAsync(project, verifier, cancellationToken); + VerifyFinalCompilation(compilation); + return (compilation, diagnostics); + } + + /// + /// Verify any expected invariants on the final compilation after the source generators have been applied. + /// + /// The compilation. + /// + /// This function is useful for basic semantic testing of the generated code and can be used instead of verification testing of an exact match to the expected source output. + /// + protected virtual void VerifyFinalCompilation(Compilation compilation) + { + } + } + } +} diff --git a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpVerifierHelper.cs b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpVerifierHelper.cs index 3d057d8f0139f..8df1cbb573e90 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpVerifierHelper.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpVerifierHelper.cs @@ -2,11 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; -namespace LibraryImportGenerator.UnitTests.Verifiers +namespace Microsoft.Interop.UnitTests.Verifiers { internal static class CSharpVerifierHelper { @@ -25,5 +29,88 @@ private static ImmutableDictionary GetNullableWarnings var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory); return commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; } + + internal static Func GetAllDiagonsticsEnabledTransform(IEnumerable analyzers) + { + return (solution, projectId) => + { + var project = solution.GetProject(projectId)!; + var compilationOptions = project.CompilationOptions!; + var diagnosticOptions = compilationOptions.SpecificDiagnosticOptions.SetItems(NullableWarnings); + + // Explicitly enable diagnostics that are not enabled by default + var enableAnalyzersOptions = new Dictionary(); + foreach (var analyzer in analyzers) + { + foreach (var diagnostic in analyzer.SupportedDiagnostics) + { + if (diagnostic.IsEnabledByDefault) + continue; + + // Map the default severity to the reporting behaviour. + // We cannot simply use ReportDiagnostic.Default here, as diagnostics that are not enabled by default + // are treated as suppressed (regardless of their default severity). + var report = diagnostic.DefaultSeverity switch + { + DiagnosticSeverity.Error => ReportDiagnostic.Error, + DiagnosticSeverity.Warning => ReportDiagnostic.Warn, + DiagnosticSeverity.Info => ReportDiagnostic.Info, + DiagnosticSeverity.Hidden => ReportDiagnostic.Hidden, + _ => ReportDiagnostic.Default + }; + enableAnalyzersOptions.Add(diagnostic.Id, report); + } + } + + compilationOptions = compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions + .SetItems(NullableWarnings) + .AddRange(enableAnalyzersOptions) + .AddRange(TestUtils.BindingRedirectWarnings)); + solution = solution.WithProjectCompilationOptions(projectId, compilationOptions); + return solution; + }; + } + + internal static Func GetTargetFrameworkAnalyzerOptionsProviderTransform(TestTargetFramework targetFramework) + { + return (solution, projectId) => + { + var project = solution.GetProject(projectId)!; + string tfmEditorConfig = targetFramework switch + { + TestTargetFramework.Framework => """ + is_global = true + build_property.TargetFrameworkIdentifier = .NETFramework + build_property.TargetFrameworkVersion = v4.8 + """, + TestTargetFramework.Standard => """ + is_global = true + build_property.TargetFrameworkIdentifier = .NETStandard + build_property.TargetFrameworkVersion = v2.0 + """, + TestTargetFramework.Core => """ + is_global = true + build_property.TargetFrameworkIdentifier = .NETCoreApp + build_property.TargetFrameworkVersion = v3.1 + """, + TestTargetFramework.Net6 => """ + is_global = true + build_property.TargetFrameworkIdentifier = .NETCoreApp + build_property.TargetFrameworkVersion = v6.0 + """, + // Replicate the product case where we don't have these properties + // since we don't have a good mechanism to ship MSBuild files from dotnet/runtime + // in the SDK. + TestTargetFramework.Net => string.Empty, + _ => throw new System.Diagnostics.UnreachableException() + }; + return solution.AddAnalyzerConfigDocument( + DocumentId.CreateNewId(projectId), + "TargetFrameworkConfig.editorconfig", + SourceText.From(tfmEditorConfig, encoding: System.Text.Encoding.UTF8), + filePath: "/TargetFrameworkConfig.editorconfig"); + }; + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AddDisableRuntimeMarshallingAttributeFixerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AddDisableRuntimeMarshallingAttributeFixerTests.cs index 8ae6c15d27924..c93cd35229d71 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AddDisableRuntimeMarshallingAttributeFixerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AddDisableRuntimeMarshallingAttributeFixerTests.cs @@ -4,28 +4,19 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; -using System.Text; -using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.CSharp.Testing; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Testing.Model; -using Microsoft.CodeAnalysis.Testing.Verifiers; -using Microsoft.Interop.Analyzers; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.Interop; - -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier< - LibraryImportGenerator.UnitTests.AddDisableRuntimeMarshallingAttributeFixerTests.MockAnalyzer, - Microsoft.Interop.Analyzers.AddDisableRuntimeMarshallingAttributeFixer>; using Xunit; using System.IO; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer, + Microsoft.Interop.Analyzers.AddDisableRuntimeMarshallingAttributeFixer>; + namespace LibraryImportGenerator.UnitTests { + [ActiveIssue("https://github.com/dotnet/runtime/issues/60650", TestRuntimes.Mono)] public class AddDisableRuntimeMarshallingAttributeFixerTests { [Fact] @@ -38,7 +29,7 @@ public static async Task Adds_NewFile_With_Attribute() partial class Foo { [LibraryImport("Foo")] - public static partial void {|CS8795:PInvoke|}(S {|#0:s|}); + public static partial void PInvoke(S {|#0:s|}); } [NativeMarshalling(typeof(Marshaller))] @@ -48,6 +39,7 @@ struct S struct Native { + public bool b; } [CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))] @@ -60,7 +52,9 @@ static class Marshaller """; var expectedPropertiesFile = "[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling]" + Environment.NewLine; - var diagnostic = VerifyCS.Diagnostic(GeneratorDiagnostics.Ids.TypeNotSupported).WithLocation(0).WithArguments("S", "s"); + var diagnostic = VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "s"); await VerifyCodeFixAsync(source, propertiesFile: null, expectedPropertiesFile, diagnostic); } @@ -73,7 +67,7 @@ public static async Task Appends_Attribute_To_Existing_AssemblyInfo_File() partial class Foo { [LibraryImport("Foo")] - public static partial void {|CS8795:PInvoke|}(S {|#0:s|}); + public static partial void PInvoke(S {|#0:s|}); } [NativeMarshalling(typeof(Marshaller))] @@ -83,6 +77,7 @@ struct S struct Native { + public bool b; } [CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))] @@ -106,19 +101,21 @@ static class Marshaller """; - var diagnostic = VerifyCS.Diagnostic(GeneratorDiagnostics.Ids.TypeNotSupported).WithLocation(0).WithArguments("S", "s"); + var diagnostic = VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "s"); await VerifyCodeFixAsync(source, propertiesFile, expectedPropertiesFile, diagnostic); } private static async Task VerifyCodeFixAsync(string source, string? propertiesFile, string? expectedPropertiesFile, DiagnosticResult diagnostic) { - var test = new Test(); - // We don't care about validating the settings for the MockAnalyzer and we're also hitting failures on Mono - // with this check in this case, so skip the check for now. - test.TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck; - test.TestCode = source; - test.FixedCode = source; - test.BatchFixedCode = source; + var test = new Test + { + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck, + TestCode = source, + FixedCode = source, + BatchFixedCode = source + }; test.ExpectedDiagnostics.Add(diagnostic); if (propertiesFile is not null) { @@ -134,45 +131,15 @@ private static async Task VerifyCodeFixAsync(string source, string? propertiesFi class Test : VerifyCS.Test { + private static readonly ImmutableArray GeneratorTypes = ImmutableArray.Create(typeof(Microsoft.Interop.LibraryImportGenerator)); + public const string FilePathPrefix = "/Project/"; protected override string DefaultFilePathPrefix => FilePathPrefix; - } - - // The Roslyn SDK doesn't provide a good test harness for testing a code fix that triggers - // on a source-generator-introduced diagnostic. This analyzer does a decent enough job of triggering - // the specific diagnostic in the right place for us to test the code fix. - [DiagnosticAnalyzer(LanguageNames.CSharp)] - public class MockAnalyzer : DiagnosticAnalyzer - { - private static readonly DiagnosticDescriptor AddDisableRuntimeMarshallingAttributeRule = GeneratorDiagnostics.ParameterTypeNotSupported; - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(AddDisableRuntimeMarshallingAttributeRule); - - public override void Initialize(AnalysisContext context) + protected override IEnumerable GetSourceGenerators() { - context.EnableConcurrentExecution(); - context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); - context.RegisterSymbolAction(context => - { - var symbol = (IParameterSymbol)context.Symbol; - - if (context.Symbol.ContainingAssembly.GetAttributes().Any(attr => attr.AttributeClass!.ToDisplayString() == TypeNames.System_Runtime_CompilerServices_DisableRuntimeMarshallingAttribute)) - { - return; - } - - if (symbol.ContainingSymbol is IMethodSymbol { IsStatic: true, IsPartialDefinition: true }) - { - context.ReportDiagnostic(context.Symbol.CreateDiagnostic( - AddDisableRuntimeMarshallingAttributeRule, - ImmutableDictionary.Empty - .Add( - GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute, - GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute), - symbol.Type.ToDisplayString(), symbol.Name)); - } - }, SymbolKind.Parameter); + return GeneratorTypes; } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs index 484901a71fe99..08d7c0e1a74be 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs @@ -4,13 +4,14 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using Microsoft.Interop.UnitTests; -using SourceGenerators.Tests; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Xunit; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; + namespace LibraryImportGenerator.UnitTests { public class AdditionalAttributesOnStub @@ -46,13 +47,7 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation comp = await TestUtils.CreateCompilation(source); - - Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator()); - - ITypeSymbol c = newComp.GetTypeByMetadataName("C")!; - IMethodSymbol stubMethod = c.GetMembers().OfType().Single(m => m.Name == "Method"); - Assert.Contains(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName); + await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: true, TestTargetFramework.Net); } [Fact] @@ -66,13 +61,7 @@ partial class C public static partial void Method(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - - Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator()); - - ITypeSymbol c = newComp.GetTypeByMetadataName("C")!; - IMethodSymbol stubMethod = c.GetMembers().OfType().Single(m => m.Name == "Method"); - Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName); + await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: false, TestTargetFramework.Net); } [Fact] @@ -106,13 +95,7 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation comp = await TestUtils.CreateCompilation(source); - - Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator()); - - ITypeSymbol c = newComp.GetTypeByMetadataName("C")!; - IMethodSymbol stubMethod = c.GetMembers().OfType().Single(m => m.Name == "Method"); - Assert.Contains(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).FullName); + await VerifySourceGeneratorAsync(source, "C", "Method", typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).FullName, attributeAdded: true, TestTargetFramework.Net); } [Fact] @@ -126,13 +109,7 @@ partial class C public static partial void Method(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - - Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator()); - - ITypeSymbol c = newComp.GetTypeByMetadataName("C")!; - IMethodSymbol stubMethod = c.GetMembers().OfType().Single(m => m.Name == "Method"); - Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).FullName); + await VerifySourceGeneratorAsync(source, "C", "Method", typeof(System.CodeDom.Compiler.GeneratedCodeAttribute).FullName, attributeAdded: false, TestTargetFramework.Net); } public static IEnumerable GetDownlevelTargetFrameworks() @@ -159,20 +136,7 @@ partial class C public static partial bool Method(); } """; - Compilation comp = await TestUtils.CreateCompilation(source, targetFramework); - - Compilation newComp = TestUtils.RunGenerators(comp, new GlobalOptionsOnlyProvider(new TargetFrameworkConfigOptions(targetFramework)), out _, new Microsoft.Interop.LibraryImportGenerator()); - - ITypeSymbol c = newComp.GetTypeByMetadataName("C")!; - IMethodSymbol stubMethod = c.GetMembers().OfType().Single(m => m.Name == "Method"); - if (expectSkipLocalsInit) - { - Assert.Contains(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName); - } - else - { - Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName); - } + await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: expectSkipLocalsInit, targetFramework); } [Fact] @@ -206,13 +170,7 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation comp = await TestUtils.CreateCompilation(source); - - Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator()); - - ITypeSymbol c = newComp.GetTypeByMetadataName("C")!; - IMethodSymbol stubMethod = c.GetMembers().OfType().Single(m => m.Name == "Method"); - Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName); + await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: false, TestTargetFramework.Net); } [Fact] @@ -246,13 +204,7 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation comp = await TestUtils.CreateCompilation(source); - - Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator()); - - ITypeSymbol c = newComp.GetTypeByMetadataName("C")!; - IMethodSymbol stubMethod = c.GetMembers().OfType().Single(m => m.Name == "Method"); - Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == typeof(SkipLocalsInitAttribute).FullName); + await VerifySourceGeneratorAsync(source, "C", "Method", typeof(SkipLocalsInitAttribute).FullName, attributeAdded: false, TestTargetFramework.Net); } [Fact] @@ -286,13 +238,49 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation comp = await TestUtils.CreateCompilation(source); + // Verify that we get no diagnostics from applying the attribute twice. + await VerifyCS.VerifySourceGeneratorAsync(source); + } - Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.LibraryImportGenerator()); + private static Task VerifySourceGeneratorAsync(string source, string typeName, string methodName, string? attributeName, bool attributeAdded, TestTargetFramework targetFramework) + { + AttributeAddedTest test = new(typeName, methodName, attributeName, attributeAdded, targetFramework) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; + return test.RunAsync(); + } + + class AttributeAddedTest : VerifyCS.Test + { + private readonly string _typeName; + private readonly string _methodName; + private readonly string? _attributeName; + private readonly bool _expectSkipLocalsInit; - ITypeSymbol c = newComp.GetTypeByMetadataName("C")!; - IMethodSymbol stubMethod = c.GetMembers().OfType().Single(m => m.Name == "Method"); - Assert.DoesNotContain(newComp.GetDiagnostics(), d => d.Id != "CS0579"); // No duplicate attribute error + public AttributeAddedTest(string typeName, string methodName, string? attributeName, bool expectSkipLocalsInitOnMethod, TestTargetFramework targetFramework) + : base(targetFramework) + { + _typeName = typeName; + _methodName = methodName; + _attributeName = attributeName; + _expectSkipLocalsInit = expectSkipLocalsInitOnMethod; + } + + protected override void VerifyFinalCompilation(Compilation compilation) + { + ITypeSymbol c = compilation.GetTypeByMetadataName(_typeName)!; + IMethodSymbol stubMethod = c.GetMembers().OfType().Single(m => m.Name == _methodName); + if (_expectSkipLocalsInit) + { + Assert.Contains(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == _attributeName); + } + else + { + Assert.DoesNotContain(stubMethod.GetAttributes(), attr => attr.AttributeClass!.ToDisplayString() == _attributeName); + } + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs index 2f055e1caceb6..1a5322bd85956 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs @@ -3,17 +3,16 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Testing; using Microsoft.Interop; using Microsoft.Interop.UnitTests; using SourceGenerators.Tests; using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Runtime.InteropServices; -using System.Text; using System.Threading.Tasks; using Xunit; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; namespace LibraryImportGenerator.UnitTests { @@ -53,19 +52,18 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation origComp = await TestUtils.CreateCompilation(source); - Compilation newComp = TestUtils.RunGenerators(origComp, out _, new Microsoft.Interop.LibraryImportGenerator()); - Assert.Empty(newComp.GetDiagnostics()); - ITypeSymbol attributeType = newComp.GetTypeByMetadataName(attributeMetadataName)!; - - Assert.NotNull(attributeType); - - IMethodSymbol targetMethod = GetGeneratedPInvokeTargetFromCompilation(newComp); + await VerifySourceGeneratorAsync( + source, + (targetMethod, newComp) => + { + ITypeSymbol attributeType = newComp.GetTypeByMetadataName(attributeMetadataName)!; + Assert.NotNull(attributeType); - Assert.Contains( - targetMethod.GetAttributes(), - attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType)); + Assert.Contains( + targetMethod.GetAttributes(), + attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType)); + }); } [Fact] @@ -101,22 +99,21 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation origComp = await TestUtils.CreateCompilation(source); - Compilation newComp = TestUtils.RunGenerators(origComp, out _, new Microsoft.Interop.LibraryImportGenerator()); - Assert.Empty(newComp.GetDiagnostics()); - - ITypeSymbol attributeType = newComp.GetTypeByMetadataName("System.Runtime.InteropServices.UnmanagedCallConvAttribute")!; - - Assert.NotNull(attributeType); - - IMethodSymbol targetMethod = GetGeneratedPInvokeTargetFromCompilation(newComp); - Assert.Contains( - targetMethod.GetAttributes(), - attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType) - && attr.NamedArguments.Length == 1 - && attr.NamedArguments[0].Key == "CallConvs" - && attr.NamedArguments[0].Value.Values.Length == 0); + await VerifySourceGeneratorAsync( + source, + (targetMethod, newComp) => + { + ITypeSymbol attributeType = newComp.GetTypeByMetadataName("System.Runtime.InteropServices.UnmanagedCallConvAttribute")!; + Assert.NotNull(attributeType); + + Assert.Contains( + targetMethod.GetAttributes(), + attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType) + && attr.NamedArguments.Length == 1 + && attr.NamedArguments[0].Key == "CallConvs" + && attr.NamedArguments[0].Value.Values.Length == 0); + }); } [Fact] @@ -151,26 +148,26 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation origComp = await TestUtils.CreateCompilation(source); - Compilation newComp = TestUtils.RunGenerators(origComp, out _, new Microsoft.Interop.LibraryImportGenerator()); - Assert.Empty(newComp.GetDiagnostics()); - - ITypeSymbol attributeType = newComp.GetTypeByMetadataName("System.Runtime.InteropServices.UnmanagedCallConvAttribute")!; - ITypeSymbol callConvType = newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvStdcall")!; - - Assert.NotNull(attributeType); - - IMethodSymbol targetMethod = GetGeneratedPInvokeTargetFromCompilation(newComp); - - Assert.Contains( - targetMethod.GetAttributes(), - attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType) - && attr.NamedArguments.Length == 1 - && attr.NamedArguments[0].Key == "CallConvs" - && attr.NamedArguments[0].Value.Values.Length == 1 - && SymbolEqualityComparer.Default.Equals( - (INamedTypeSymbol?)attr.NamedArguments[0].Value.Values[0].Value!, - callConvType)); + + await VerifySourceGeneratorAsync( + source, + (targetMethod, newComp) => + { + ITypeSymbol attributeType = newComp.GetTypeByMetadataName("System.Runtime.InteropServices.UnmanagedCallConvAttribute")!; + ITypeSymbol callConvType = newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvStdcall")!; + + Assert.NotNull(attributeType); + + Assert.Contains( + targetMethod.GetAttributes(), + attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType) + && attr.NamedArguments.Length == 1 + && attr.NamedArguments[0].Key == "CallConvs" + && attr.NamedArguments[0].Value.Values.Length == 1 + && SymbolEqualityComparer.Default.Equals( + (INamedTypeSymbol?)attr.NamedArguments[0].Value.Values[0].Value!, + callConvType)); + }); } [Fact] @@ -205,30 +202,30 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation origComp = await TestUtils.CreateCompilation(source); - Compilation newComp = TestUtils.RunGenerators(origComp, out _, new Microsoft.Interop.LibraryImportGenerator()); - Assert.Empty(newComp.GetDiagnostics()); - - ITypeSymbol attributeType = newComp.GetTypeByMetadataName("System.Runtime.InteropServices.UnmanagedCallConvAttribute")!; - ITypeSymbol callConvType = newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvStdcall")!; - ITypeSymbol callConvType2 = newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition")!; - - Assert.NotNull(attributeType); - - IMethodSymbol targetMethod = GetGeneratedPInvokeTargetFromCompilation(newComp); - - Assert.Contains( - targetMethod.GetAttributes(), - attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType) - && attr.NamedArguments.Length == 1 - && attr.NamedArguments[0].Key == "CallConvs" - && attr.NamedArguments[0].Value.Values.Length == 2 - && SymbolEqualityComparer.Default.Equals( - (INamedTypeSymbol?)attr.NamedArguments[0].Value.Values[0].Value!, - callConvType) - && SymbolEqualityComparer.Default.Equals( - (INamedTypeSymbol?)attr.NamedArguments[0].Value.Values[1].Value!, - callConvType2)); + + await VerifySourceGeneratorAsync( + source, + (targetMethod, newComp) => + { + ITypeSymbol attributeType = newComp.GetTypeByMetadataName("System.Runtime.InteropServices.UnmanagedCallConvAttribute")!; + ITypeSymbol callConvType = newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvStdcall")!; + ITypeSymbol callConvType2 = newComp.GetTypeByMetadataName("System.Runtime.CompilerServices.CallConvSuppressGCTransition")!; + + Assert.NotNull(attributeType); + + Assert.Contains( + targetMethod.GetAttributes(), + attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType) + && attr.NamedArguments.Length == 1 + && attr.NamedArguments[0].Key == "CallConvs" + && attr.NamedArguments[0].Value.Values.Length == 2 + && SymbolEqualityComparer.Default.Equals( + (INamedTypeSymbol?)attr.NamedArguments[0].Value.Values[0].Value!, + callConvType) + && SymbolEqualityComparer.Default.Equals( + (INamedTypeSymbol?)attr.NamedArguments[0].Value.Values[1].Value!, + callConvType2)); + }); } [Fact] @@ -263,23 +260,23 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation origComp = await TestUtils.CreateCompilation(source); - Compilation newComp = TestUtils.RunGenerators(origComp, out _, new Microsoft.Interop.LibraryImportGenerator()); - Assert.Empty(newComp.GetDiagnostics()); - - ITypeSymbol attributeType = newComp.GetTypeByMetadataName("System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute")!; - Assert.NotNull(attributeType); + await VerifySourceGeneratorAsync( + source, + (targetMethod, newComp) => + { + ITypeSymbol attributeType = newComp.GetTypeByMetadataName("System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute")!; - IMethodSymbol targetMethod = GetGeneratedPInvokeTargetFromCompilation(newComp); + Assert.NotNull(attributeType); - DllImportSearchPath expected = DllImportSearchPath.System32 | DllImportSearchPath.UserDirectories; + DllImportSearchPath expected = DllImportSearchPath.System32 | DllImportSearchPath.UserDirectories; - Assert.Contains( - targetMethod.GetAttributes(), - attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType) - && attr.ConstructorArguments.Length == 1 - && expected == (DllImportSearchPath)attr.ConstructorArguments[0].Value!); + Assert.Contains( + targetMethod.GetAttributes(), + attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType) + && attr.ConstructorArguments.Length == 1 + && expected == (DllImportSearchPath)attr.ConstructorArguments[0].Value!); + }); } [Fact] @@ -318,20 +315,20 @@ static class Marshaller public static S ConvertToManaged(Native n) => default; } """; - Compilation origComp = await TestUtils.CreateCompilation(source); - Compilation newComp = TestUtils.RunGenerators(origComp, out _, new Microsoft.Interop.LibraryImportGenerator()); - - Assert.Empty(newComp.GetDiagnostics()); - ITypeSymbol attributeType = newComp.GetTypeByMetadataName("OtherAttribute")!; + await VerifySourceGeneratorAsync( + source, + (targetMethod, newComp) => + { + ITypeSymbol attributeType = newComp.GetTypeByMetadataName("OtherAttribute")!; - Assert.NotNull(attributeType); + Assert.NotNull(attributeType); - IMethodSymbol targetMethod = GetGeneratedPInvokeTargetFromCompilation(newComp); - Assert.DoesNotContain( - targetMethod.GetAttributes(), - attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType)); + Assert.DoesNotContain( + targetMethod.GetAttributes(), + attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, attributeType)); + }); } [Fact] @@ -347,35 +344,33 @@ partial class C { [LibraryImportAttribute("DoesNotExist")] [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool Method1([In, Out] int a); + public static partial bool Method1([In, Out] int {|SYSLIB1051:a|}); } """ + CodeSnippets.LibraryImportAttributeDeclaration; - Compilation origComp = await TestUtils.CreateCompilation(source, TestTargetFramework.Standard); - Compilation newComp = TestUtils.RunGenerators( - origComp, - new GlobalOptionsOnlyProvider(new TargetFrameworkConfigOptions(TestTargetFramework.Standard)), - out _, - new Microsoft.Interop.LibraryImportGenerator()); - - IMethodSymbol targetMethod = GetGeneratedPInvokeTargetFromCompilation(newComp); - - INamedTypeSymbol marshalAsAttribute = newComp.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute)!; - INamedTypeSymbol inAttribute = newComp.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_InAttribute)!; - INamedTypeSymbol outAttribute = newComp.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_OutAttribute)!; - Assert.Collection(targetMethod.Parameters, - param => Assert.Collection(param.GetAttributes(), - attr => - { - Assert.Equal(inAttribute, attr.AttributeClass, SymbolEqualityComparer.Default); - Assert.Empty(attr.ConstructorArguments); - Assert.Empty(attr.NamedArguments); - }, - attr => - { - Assert.Equal(outAttribute, attr.AttributeClass, SymbolEqualityComparer.Default); - Assert.Empty(attr.ConstructorArguments); - Assert.Empty(attr.NamedArguments); - })); + + await VerifySourceGeneratorAsync( + source, + (targetMethod, newComp) => + { + INamedTypeSymbol marshalAsAttribute = newComp.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute)!; + INamedTypeSymbol inAttribute = newComp.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_InAttribute)!; + INamedTypeSymbol outAttribute = newComp.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_OutAttribute)!; + Assert.Collection(targetMethod.Parameters, + param => Assert.Collection(param.GetAttributes(), + attr => + { + Assert.Equal(inAttribute, attr.AttributeClass, SymbolEqualityComparer.Default); + Assert.Empty(attr.ConstructorArguments); + Assert.Empty(attr.NamedArguments); + }, + attr => + { + Assert.Equal(outAttribute, attr.AttributeClass, SymbolEqualityComparer.Default); + Assert.Empty(attr.ConstructorArguments); + Assert.Empty(attr.NamedArguments); + })); + }, + TestTargetFramework.Standard); } [Fact] @@ -392,38 +387,63 @@ partial class C public static partial bool Method1([MarshalAs(UnmanagedType.I2)] int a); } """ + CodeSnippets.LibraryImportAttributeDeclaration; - Compilation origComp = await TestUtils.CreateCompilation(source, TestTargetFramework.Standard); - Compilation newComp = TestUtils.RunGenerators( - origComp, - new GlobalOptionsOnlyProvider(new TargetFrameworkConfigOptions(TestTargetFramework.Standard)), - out _, - new Microsoft.Interop.LibraryImportGenerator()); - - IMethodSymbol targetMethod = GetGeneratedPInvokeTargetFromCompilation(newComp); - - INamedTypeSymbol marshalAsAttribute = newComp.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute)!; - Assert.Collection(targetMethod.Parameters, - param => Assert.Collection(param.GetAttributes(), - attr => - { - Assert.Equal(marshalAsAttribute, attr.AttributeClass, SymbolEqualityComparer.Default); - Assert.Equal(UnmanagedType.I2, (UnmanagedType)attr.ConstructorArguments[0].Value!); - Assert.Empty(attr.NamedArguments); - })); + + await VerifySourceGeneratorAsync( + source, + (targetMethod, newComp) => + { + INamedTypeSymbol marshalAsAttribute = newComp.GetTypeByMetadataName(TypeNames.System_Runtime_InteropServices_MarshalAsAttribute)!; + Assert.Collection(targetMethod.Parameters, + param => Assert.Collection(param.GetAttributes(), + attr => + { + Assert.Equal(marshalAsAttribute, attr.AttributeClass, SymbolEqualityComparer.Default); + Assert.Equal(UnmanagedType.I2, (UnmanagedType)attr.ConstructorArguments[0].Value!); + Assert.Empty(attr.NamedArguments); + })); + }, + TestTargetFramework.Standard); + } + + private static Task VerifySourceGeneratorAsync(string source, Action targetPInvokeAssertion, TestTargetFramework targetFramework = TestTargetFramework.Net) + { + var test = new GeneratedTargetPInvokeTest(targetPInvokeAssertion, targetFramework) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; + + return test.RunAsync(); } - private static IMethodSymbol GetGeneratedPInvokeTargetFromCompilation(Compilation newComp) + class GeneratedTargetPInvokeTest : VerifyCS.Test { - // The last syntax tree is the generated code - SyntaxTree generatedCode = newComp.SyntaxTrees.Last(); - SemanticModel model = newComp.GetSemanticModel(generatedCode); - - var localFunctions = generatedCode.GetRoot() - .DescendantNodes().OfType() - .ToList(); - LocalFunctionStatementSyntax innerDllImport = Assert.Single(localFunctions); - IMethodSymbol targetMethod = (IMethodSymbol)model.GetDeclaredSymbol(innerDllImport)!; - return targetMethod; + private readonly Action _targetPInvokeAssertion; + + public GeneratedTargetPInvokeTest(Action targetPInvokeAssertion, TestTargetFramework targetFramework) + :base(targetFramework) + { + _targetPInvokeAssertion = targetPInvokeAssertion; + } + + private static IMethodSymbol GetGeneratedPInvokeTargetFromCompilation(Compilation compilation) + { + // The last syntax tree is the generated code + SyntaxTree generatedCode = compilation.SyntaxTrees.Last(); + SemanticModel model = compilation.GetSemanticModel(generatedCode); + + var localFunctions = generatedCode.GetRoot() + .DescendantNodes().OfType() + .ToList(); + LocalFunctionStatementSyntax innerDllImport = Assert.Single(localFunctions); + IMethodSymbol targetMethod = (IMethodSymbol)model.GetDeclaredSymbol(innerDllImport)!; + return targetMethod; + } + + protected override void VerifyFinalCompilation(Compilation compilation) + { + _targetPInvokeAssertion(GetGeneratedPInvokeTargetFromCompilation(compilation), compilation); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs index 1523ac5b4c1c5..0eb9830693431 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs @@ -72,7 +72,7 @@ public LibraryImportAttribute(string a) { } /// public static readonly string TrivialClassDeclarations = """ using System.Runtime.InteropServices; - partial class Basic + partial class {|#0:Basic|} { [LibraryImportAttribute("DoesNotExist")] public static partial void Method1(); @@ -296,7 +296,7 @@ partial class Test using System.Runtime.InteropServices; partial class Test { - [LCIDConversion(0)] + [{|#0:LCIDConversion(0)|}] [LibraryImport("DoesNotExist")] public static partial void Method(); } @@ -335,12 +335,12 @@ public object MarshalNativeToManaged(IntPtr pNativeData) partial class Test { [LibraryImport("DoesNotExist")] - [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NS.MyCustomMarshaler), MarshalCookie="COOKIE1")] - public static partial bool Method1([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NS.MyCustomMarshaler), MarshalCookie="COOKIE2")]bool t); + [return: {|#0:MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NS.MyCustomMarshaler), MarshalCookie="COOKIE1")|}] + public static partial bool {|#1:Method1|}([{|#2:MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(NS.MyCustomMarshaler), MarshalCookie="COOKIE2")|}]bool {|#3:t|}); [LibraryImport("DoesNotExist")] - [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "NS.MyCustomMarshaler", MarshalCookie="COOKIE3")] - public static partial bool Method2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "NS.MyCustomMarshaler", MarshalCookie="COOKIE4")]bool t); + [return: {|#4:MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "NS.MyCustomMarshaler", MarshalCookie="COOKIE3")|}] + public static partial bool {|#5:Method2|}([{|#6:MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "NS.MyCustomMarshaler", MarshalCookie="COOKIE4")|}]bool {|#7:t|}); } """; @@ -363,13 +363,13 @@ public ATTRIBUTELibraryImportAttribute(string a) { } partial class Test { [ATTRIBUTELibraryImportAttribute("DoesNotExist")] - public static partial void Method1(); + public static partial void {|CS8795:Method1|}(); [ATTRIBUTELibraryImport("DoesNotExist")] - public static partial void Method2(); + public static partial void {|CS8795:Method2|}(); [System.Runtime.InteropServices.ATTRIBUTELibraryImport("DoesNotExist")] - public static partial void Method3(); + public static partial void {|CS8795:Method3|}(); } """; @@ -385,12 +385,12 @@ public static string BasicParametersAndModifiersWithStringMarshalling(string typ {{preDeclaration}} partial class Test { - [LibraryImport("DoesNotExist", StringMarshalling = StringMarshalling.{{value}})] - public static partial {{typename}} Method( - {{typename}} p, - in {{typename}} pIn, - ref {{typename}} pRef, - out {{typename}} pOut); + [{|#0:LibraryImport("DoesNotExist", StringMarshalling = StringMarshalling.{{value}})|}] + public static partial {{typename}} {|#1:Method|}( + {{typename}} {|#2:p|}, + in {{typename}} {|#3:pIn|}, + ref {{typename}} {|#4:pRef|}, + out {{typename}} {|#5:pOut|}); } """; @@ -407,11 +407,11 @@ public static string BasicParametersAndModifiersWithStringMarshallingCustomType( partial class Test { [LibraryImport("DoesNotExist", StringMarshallingCustomType = typeof({{stringMarshallingCustomTypeName}}))] - public static partial {{typeName}} Method( - {{typeName}} p, - in {{typeName}} pIn, - ref {{typeName}} pRef, - out {{typeName}} pOut); + public static partial {{typeName}} {|#0:Method|}( + {{typeName}} {|#1:p|}, + in {{typeName}} {|#2:pIn|}, + ref {{typeName}} {|#3:pRef|}, + out {{typeName}} {|#4:pOut|}); } """; @@ -441,11 +441,11 @@ public static string BasicParametersAndModifiers(string typeName, string preDecl partial class Test { [LibraryImport("DoesNotExist")] - public static partial {{typeName}} Method( - {{typeName}} p, - in {{typeName}} pIn, - ref {{typeName}} pRef, - out {{typeName}} pOut); + public static partial {{typeName}} {|#0:Method|}( + {{typeName}} {|#1:p|}, + in {{typeName}} {|#2:pIn|}, + ref {{typeName}} {|#3:pRef|}, + out {{typeName}} {|#4:pOut|}); } """; @@ -459,10 +459,10 @@ public static string BasicParametersAndModifiersNoRef(string typeName, string pr partial class Test { [LibraryImport("DoesNotExist")] - public static partial {{typeName}} Method( - {{typeName}} p, - in {{typeName}} pIn, - out {{typeName}} pOut); + public static partial {{typeName}} {|#0:Method|}( + {{typeName}} {|#1:p|}, + in {{typeName}} {|#2:pIn|}, + out {{typeName}} {|#4:pOut|}); } """; @@ -475,11 +475,11 @@ public static string BasicParametersAndModifiersUnsafe(string typeName, string p partial class Test { [LibraryImport("DoesNotExist")] - public static unsafe partial {{typeName}} Method( - {{typeName}} p, - in {{typeName}} pIn, - ref {{typeName}} pRef, - out {{typeName}} pOut); + public static unsafe partial {{typeName}} {|#0:Method|}( + {{typeName}} {|#1:p|}, + in {{typeName}} {|#2:pIn|}, + ref {{typeName}} {|#3:pRef|}, + out {{typeName}} {|#4:pOut|}); } """; @@ -496,7 +496,7 @@ partial class Test { [LibraryImport("DoesNotExist")] public static partial void Method( - [{{attributeName}}] {{typeName}} p); + [{{attributeName}}] {{typeName}} {|#0:p|}); } """; @@ -523,12 +523,12 @@ public static string MarshalAsParametersAndModifiers(string typeName, UnmanagedT partial class Test { [LibraryImport("DoesNotExist")] - [return: MarshalAs(UnmanagedType.{{unmanagedType}})] - public static partial {{typeName}} Method( - [MarshalAs(UnmanagedType.{{unmanagedType}})] {{typeName}} p, - [MarshalAs(UnmanagedType.{{unmanagedType}})] in {{typeName}} pIn, - [MarshalAs(UnmanagedType.{{unmanagedType}})] ref {{typeName}} pRef, - [MarshalAs(UnmanagedType.{{unmanagedType}})] out {{typeName}} pOut); + [return: {|#10:MarshalAs(UnmanagedType.{{unmanagedType}})|}] + public static partial {{typeName}} {|#0:Method|}( + [{|#11:MarshalAs(UnmanagedType.{{unmanagedType}})|}] {{typeName}} {|#1:p|}, + [{|#12:MarshalAs(UnmanagedType.{{unmanagedType}})|}] in {{typeName}} {|#2:pIn|}, + [{|#13:MarshalAs(UnmanagedType.{{unmanagedType}})|}] ref {{typeName}} {|#3:pRef|}, + [{|#14:MarshalAs(UnmanagedType.{{unmanagedType}})|}] out {{typeName}} {|#4:pOut|}); } """; @@ -541,11 +541,11 @@ partial class Test { [LibraryImport("DoesNotExist")] [return: MarshalAs(UnmanagedType.{{unmanagedType}})] - public static unsafe partial {{typeName}} Method( - [MarshalAs(UnmanagedType.{{unmanagedType}})] {{typeName}} p, - [MarshalAs(UnmanagedType.{{unmanagedType}})] in {{typeName}} pIn, - [MarshalAs(UnmanagedType.{{unmanagedType}})] ref {{typeName}} pRef, - [MarshalAs(UnmanagedType.{{unmanagedType}})] out {{typeName}} pOut); + public static unsafe partial {{typeName}} {|#0:Method|}( + [MarshalAs(UnmanagedType.{{unmanagedType}})] {{typeName}} {|#1:p|}, + [MarshalAs(UnmanagedType.{{unmanagedType}})] in {{typeName}} {|#2:pIn|}, + [MarshalAs(UnmanagedType.{{unmanagedType}})] ref {{typeName}} {|#3:pRef|}, + [MarshalAs(UnmanagedType.{{unmanagedType}})] out {{typeName}} {|#4:pOut|}); } """; @@ -629,13 +629,13 @@ partial class Test { [LibraryImport("DoesNotExist")] [return:MarshalAs(UnmanagedType.LPArray, SizeConst=10)] - public static partial {{elementType}}[] Method( - {{elementType}}[] p, - in {{elementType}}[] pIn, - int pRefSize, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=2)] ref {{elementType}}[] pRef, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=5, SizeConst=4)] out {{elementType}}[] pOut, - out int pOutSize + public static partial {{elementType}}[] {|#0:Method|}( + {{elementType}}[] {|#1:p|}, + in {{elementType}}[] {|#2:pIn|}, + int {|#3:pRefSize|}, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=2)] ref {{elementType}}[] {|#4:pRef|}, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=5, SizeConst=4)] out {{elementType}}[] {|#5:pOut|}, + out int {|#6:pOutSize|} ); } """; @@ -649,8 +649,8 @@ partial class Test { [LibraryImport("DoesNotExist")] public static partial void Method( - {{(isByRef ? "ref" : "")}} {{sizeParamType}} pRefSize, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] ref int[] pRef + {{(isByRef ? "ref" : "")}} {{sizeParamType}} {|#0:pRefSize|}, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] ref int[] {|#1:pRef|} ); } """; @@ -675,6 +675,21 @@ public static partial void Method( /// /// Declaration with parameters with MarshalAs. /// + //public static string MarshalUsingParametersAndModifiers(string typeName, string nativeTypeName, string preDeclaration = "") => $$""" + // using System.Runtime.InteropServices; + // using System.Runtime.InteropServices.Marshalling; + // {{preDeclaration}} + // partial class Test + // { + // [LibraryImport("DoesNotExist")] + // [return: MarshalUsing(typeof({{nativeTypeName}}))] + // public static partial {{typeName}} Method( + // [MarshalUsing(typeof({{nativeTypeName}}))] {{typeName}} p, + // [MarshalUsing(typeof({{nativeTypeName}}))] in {{typeName}} pIn, + // [MarshalUsing(typeof({{nativeTypeName}}))] ref {{typeName}} pRef, + // [MarshalUsing(typeof({{nativeTypeName}}))] out {{typeName}} pOut); + // } + // """; public static string MarshalUsingParametersAndModifiers(string typeName, string nativeTypeName, string preDeclaration = "") => $$""" using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; @@ -683,11 +698,11 @@ partial class Test { [LibraryImport("DoesNotExist")] [return: MarshalUsing(typeof({{nativeTypeName}}))] - public static partial {{typeName}} Method( - [MarshalUsing(typeof({{nativeTypeName}}))] {{typeName}} p, - [MarshalUsing(typeof({{nativeTypeName}}))] in {{typeName}} pIn, - [MarshalUsing(typeof({{nativeTypeName}}))] ref {{typeName}} pRef, - [MarshalUsing(typeof({{nativeTypeName}}))] out {{typeName}} pOut); + public static partial {{typeName}} {|#0:Method|}( + [MarshalUsing(typeof({{nativeTypeName}}))] {{typeName}} {|#1:p|}, + [MarshalUsing(typeof({{nativeTypeName}}))] in {{typeName}} {|#2:pIn|}, + [MarshalUsing(typeof({{nativeTypeName}}))] ref {{typeName}} {|#3:pRef|}, + [MarshalUsing(typeof({{nativeTypeName}}))] out {{typeName}} {|#4:pOut|}); } """; public static string BasicParameterWithByRefModifier(string byRefKind, string typeName, string preDeclaration = "") => $$""" @@ -698,7 +713,7 @@ partial class Test { [LibraryImport("DoesNotExist")] public static partial void Method( - {{byRefKind}} {{typeName}} p); + {{byRefKind}} {{typeName}} {|#0:p|}); } """; @@ -710,7 +725,7 @@ partial class Test { [LibraryImport("DoesNotExist")] public static partial void Method( - {{typeName}} p); + {{typeName}} {|#0:p|}); } """; @@ -721,7 +736,7 @@ public static string BasicReturnType(string typeName, string preDeclaration = "" partial class Test { [LibraryImport("DoesNotExist")] - public static partial {{typeName}} Method(); + public static partial {{typeName}} {|#0:Method|}(); } """; @@ -832,20 +847,20 @@ public static string MaybeBlittableGenericTypeParametersAndModifiers() => public static string RecursiveImplicitlyBlittableStruct => BasicParametersAndModifiers("RecursiveStruct", DisableRuntimeMarshalling) + """ struct RecursiveStruct { - RecursiveStruct s; + RecursiveStruct {|CS0523:s|}; int i; } """; public static string MutuallyRecursiveImplicitlyBlittableStruct => BasicParametersAndModifiers("RecursiveStruct1", DisableRuntimeMarshalling) + """ struct RecursiveStruct1 { - RecursiveStruct2 s; + RecursiveStruct2 {|CS0523:s|}; int i; } struct RecursiveStruct2 { - RecursiveStruct1 s; + RecursiveStruct1 {|CS0523:s|}; int i; } """; @@ -972,7 +987,7 @@ partial class Test [LibraryImport("DoesNotExist")] public static partial void Method( int pRefSize, - [MarshalUsing(ConstantElementCount = 10, CountElementName = "pRefSize")] ref int[] pRef + [{|#0:MarshalUsing(ConstantElementCount = 10, CountElementName = "pRefSize")|}] ref int[] {|#1:pRef|} ); } """; @@ -986,7 +1001,7 @@ partial class Test [LibraryImport("DoesNotExist")] public static partial void Method( int pRefSize, - [MarshalUsing(CountElementName = null)] ref int[] pRef + [{|#0:MarshalUsing(CountElementName = null)|}] ref int[] {|#1:pRef|} ); } """; @@ -999,7 +1014,7 @@ partial class Test { [LibraryImport("DoesNotExist")] [return:MarshalUsing(ConstantElementCount=10)] - [return:MarshalAs(UnmanagedType.LPArray, SizeConst=10)] + [return:{|#0:MarshalAs(UnmanagedType.LPArray, SizeConst=10)|}] public static partial int[] Method(); } """; @@ -1011,7 +1026,7 @@ partial class Test { [LibraryImport("DoesNotExist")] public static partial void Method( - [MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 1)] [MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 1)] TestCollection p); + [MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 1)] [{|#0:MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 1)|}] TestCollection p); } """ + CustomCollectionMarshallingCodeSnippets.TestCollection() @@ -1026,7 +1041,7 @@ partial class Test { [LibraryImport("DoesNotExist")] public static partial void Method( - [MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 2)] TestCollection p); + [{|#0:MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 2)|}] TestCollection p); } """ + CustomCollectionMarshallingCodeSnippets.TestCollection() @@ -1040,8 +1055,8 @@ public static partial void Method( partial class Test { [LibraryImport("DoesNotExist")] - [return:MarshalUsing(CountElementName=MarshalUsingAttribute.ReturnsCountValue)] - public static partial int[] Method(); + [return:{|#0:MarshalUsing(CountElementName=MarshalUsingAttribute.ReturnsCountValue)|}] + public static partial int[] {|#1:Method|}(); } """; @@ -1053,7 +1068,7 @@ partial class Test { [LibraryImport("DoesNotExist")] public static partial void Method( - [MarshalUsing(CountElementName="arr")] ref int[] arr + [{|#0:MarshalUsing(CountElementName="arr")|}] ref int[] {|#1:arr|} ); } """; @@ -1065,8 +1080,8 @@ partial class Test { [LibraryImport("DoesNotExist")] public static partial void Method( - [MarshalUsing(CountElementName="arr2")] ref int[] arr, - [MarshalUsing(CountElementName="arr")] ref int[] arr2 + [{|#0:MarshalUsing(CountElementName="arr2")|}] ref int[] {|#1:arr|}, + [{|#2:MarshalUsing(CountElementName="arr")|}] ref int[] {|#3:arr2|} ); } """; @@ -1077,8 +1092,8 @@ partial class Test { [LibraryImport("DoesNotExist")] public static partial void Method( - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ref int[] arr, - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] ref int[] arr2 + [{|#0:MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)|}] ref int[] {|#1:arr|}, + [{|#2:MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)|}] ref int[] {|#3:arr2|} ); } """; @@ -1195,9 +1210,9 @@ public static string RefReturn(string typeName) => $$""" partial struct Basic { [LibraryImport("DoesNotExist")] - public static partial ref {{typeName}} RefReturn(); + public static partial ref {{typeName}} {|#0:RefReturn|}(); [LibraryImport("DoesNotExist")] - public static partial ref readonly {{typeName}} RefReadonlyReturn(); + public static partial ref readonly {{typeName}} {|#1:RefReadonlyReturn|}(); } """; @@ -1206,7 +1221,7 @@ partial struct Basic partial struct Basic { - [LibraryImport("DoesNotExist", SetLa)] + [{|CS1729:LibraryImport("DoesNotExist", {|CS0103:SetLa|})|}] public static partial void Method(); } """; @@ -1215,7 +1230,7 @@ partial struct Basic partial struct Basic { - [LibraryImport(DoesNotExist)] + [LibraryImport({|CS0103:DoesNotExist|})] public static partial void Method(); } """; @@ -1224,7 +1239,7 @@ partial struct Basic partial struct Basic { - [LibraryImport("DoesNotExist", SetLastError = "Foo")] + [LibraryImport("DoesNotExist", SetLastError = {|CS0029:"Foo"|})] public static partial void Method(); } """; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs index 4a948f59db1d1..0f0cb60da3878 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs @@ -11,9 +11,15 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.Interop; using Microsoft.Interop.UnitTests; using Xunit; +using StringMarshalling = System.Runtime.InteropServices.StringMarshalling; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; + namespace LibraryImportGenerator.UnitTests { public class CompileFails @@ -26,213 +32,930 @@ private static string ID( public static IEnumerable CodeSnippetsToCompile() { // Not LibraryImportAttribute - yield return new object[] { ID(), CodeSnippets.UserDefinedPrefixedAttributes, 0, 3 }; + yield return new object[] { ID(), CodeSnippets.UserDefinedPrefixedAttributes, Array.Empty() }; // No explicit marshalling for char or string - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(), 5, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(), 5, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers(), 5, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers(), 5, 0 }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "pOut") + }}; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pOut") + }}; + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers(), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(5) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "pOut") + }}; + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers(), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(5) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pOut") + }}; + + // No explicit marshalling for bool + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pOut") + }}; + + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers(), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(5) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pOut") + }}; - // No explicit marshaling for bool - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(), 5, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers(), 5, 0 }; // Unsupported StringMarshalling configuration - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling(StringMarshalling.Utf8), 5, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling(StringMarshalling.Custom), 6, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling(StringMarshalling.Custom), 6, 0 }; - yield return new object[] { ID(), CodeSnippets.CustomStringMarshallingParametersAndModifiers(), 5, 0 }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling(StringMarshalling.Utf8), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling char with 'StringMarshalling.Utf8' is not supported. Instead, manually convert the char type to the desired byte representation and pass to the source-generated P/Invoke.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Marshalling char with 'StringMarshalling.Utf8' is not supported. Instead, manually convert the char type to the desired byte representation and pass to the source-generated P/Invoke.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling char with 'StringMarshalling.Utf8' is not supported. Instead, manually convert the char type to the desired byte representation and pass to the source-generated P/Invoke.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling char with 'StringMarshalling.Utf8' is not supported. Instead, manually convert the char type to the desired byte representation and pass to the source-generated P/Invoke.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(5) + .WithArguments("Marshalling char with 'StringMarshalling.Utf8' is not supported. Instead, manually convert the char type to the desired byte representation and pass to the source-generated P/Invoke.", "pOut") + }}; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling(StringMarshalling.Custom), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidStringMarshallingConfiguration) + .WithLocation(0) + .WithArguments("Method", "'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'."), + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(5) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "pOut") + }}; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling(StringMarshalling.Custom), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidStringMarshallingConfiguration) + .WithLocation(0) + .WithArguments("Method", "'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'."), + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupported) + .WithLocation(1) + .WithArguments("string", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupported) + .WithLocation(2) + .WithArguments("string", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupported) + .WithLocation(3) + .WithArguments("string", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupported) + .WithLocation(4) + .WithArguments("string", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupported) + .WithLocation(5) + .WithArguments("string", "pOut") + }}; + yield return new object[] { ID(), CodeSnippets.CustomStringMarshallingParametersAndModifiers(), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling char with 'StringMarshalling.Custom' is not supported. To use a custom type marshaller, specify 'MarshalUsingAttribute'.", "pOut") + }}; // Unsupported UnmanagedType - yield return new object[] { ID(), CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.I1), 5, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.U1), 5, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.SafeArray), 10, 0 }; + yield return new object[] { ID(), CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.I1), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(0) + .WithArguments("MarshalAsAttribute", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(1) + .WithArguments("MarshalAsAttribute", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(2) + .WithArguments("MarshalAsAttribute", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(3) + .WithArguments("MarshalAsAttribute", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(4) + .WithArguments("MarshalAsAttribute", "pOut") + }}; + yield return new object[] { ID(), CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.U1), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(0) + .WithArguments("MarshalAsAttribute", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(1) + .WithArguments("MarshalAsAttribute", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(2) + .WithArguments("MarshalAsAttribute", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(3) + .WithArguments("MarshalAsAttribute", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(4) + .WithArguments("MarshalAsAttribute", "pOut") + }}; + yield return new object[] { ID(), CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.SafeArray), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(10) + .WithArguments("SafeArray", "UnmanagedType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(11) + .WithArguments("SafeArray", "UnmanagedType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(12) + .WithArguments("SafeArray", "UnmanagedType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(13) + .WithArguments("SafeArray", "UnmanagedType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(14) + .WithArguments("SafeArray", "UnmanagedType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(0) + .WithArguments("MarshalAsAttribute", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(1) + .WithArguments("MarshalAsAttribute", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(2) + .WithArguments("MarshalAsAttribute", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(3) + .WithArguments("MarshalAsAttribute", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(4) + .WithArguments("MarshalAsAttribute", "pOut") + }}; // Unsupported MarshalAsAttribute usage // * UnmanagedType.CustomMarshaler, MarshalTypeRef, MarshalType, MarshalCookie - yield return new object[] { ID(), CodeSnippets.MarshalAsCustomMarshalerOnTypes, 16, 0 }; + yield return new object[] { ID(), CodeSnippets.MarshalAsCustomMarshalerOnTypes, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(0) + .WithArguments("CustomMarshaler", "UnmanagedType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(0) + .WithArguments("MarshalAsAttribute.MarshalCookie"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(0) + .WithArguments("MarshalAsAttribute.MarshalTypeRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(2) + .WithArguments("CustomMarshaler", "UnmanagedType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(2) + .WithArguments("MarshalAsAttribute.MarshalCookie"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(2) + .WithArguments("MarshalAsAttribute.MarshalTypeRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(4) + .WithArguments("CustomMarshaler", "UnmanagedType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(4) + .WithArguments("MarshalAsAttribute.MarshalCookie"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(4) + .WithArguments("MarshalAsAttribute.MarshalType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(6) + .WithArguments("CustomMarshaler", "UnmanagedType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(6) + .WithArguments("MarshalAsAttribute.MarshalCookie"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(6) + .WithArguments("MarshalAsAttribute.MarshalType"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(1) + .WithArguments("MarshalAsAttribute", "Method1"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(3) + .WithArguments("MarshalAsAttribute", "t"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(5) + .WithArguments("MarshalAsAttribute", "Method2"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(7) + .WithArguments("MarshalAsAttribute", "t") + }}; // Unsupported [In, Out] attributes usage // Blittable array - yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("Out"), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("In, Out"), 1, 0 }; + yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("Out"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The provided '[In]' and '[Out]' attributes on this parameter are unsupported on this parameter.", "p") + } }; + + yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("In, Out"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The provided '[In]' and '[Out]' attributes on this parameter are unsupported on this parameter.", "p") + } }; // By ref with [In, Out] attributes - yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("in int", "In"), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("ref int", "In"), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("ref int", "In, Out"), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("out int", "Out"), 1, 0 }; + yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("in int", "In"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The '[In]' and '[Out]' attributes are unsupported on parameters passed by reference. Use the 'in', 'ref', or 'out' keywords instead.", "p") + } }; + yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("ref int", "In"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The '[In]' and '[Out]' attributes are unsupported on parameters passed by reference. Use the 'in', 'ref', or 'out' keywords instead.", "p") + } }; + yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("ref int", "In, Out"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The '[In]' and '[Out]' attributes are unsupported on parameters passed by reference. Use the 'in', 'ref', or 'out' keywords instead.", "p") + } }; + yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("out int", "Out"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The '[In]' and '[Out]' attributes are unsupported on parameters passed by reference. Use the 'in', 'ref', or 'out' keywords instead.", "p") + } }; // By value non-array with [In, Out] attributes - yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("In"), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("Out"), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("In, Out"), 1, 0 }; + yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("In"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The '[In]' attribute is not supported unless the '[Out]' attribute is also used. The behavior of the '[In]' attribute without the '[Out]' attribute is the same as the default behavior.", "p") + } }; + yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("Out"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The provided '[In]' and '[Out]' attributes on this parameter are unsupported on this parameter.", "p") + } }; + + yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier("In, Out"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The provided '[In]' and '[Out]' attributes on this parameter are unsupported on this parameter.", "p") + } }; // LCIDConversion - yield return new object[] { ID(), CodeSnippets.LCIDConversionAttribute, 1, 0 }; + yield return new object[] { ID(), CodeSnippets.LCIDConversionAttribute, new[] { + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(0) + .WithArguments("LCIDConversionAttribute") + } }; // No size information for array marshalling from unmanaged to managed // * return, out, ref - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 5, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 5, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; - yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), 3, 0 }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; + yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers(CodeSnippets.DisableRuntimeMarshalling), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pOut"), + } }; // Collection with non-integer size param - yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam(isByRef: false), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam(isByRef: false), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam(isByRef: false), 2, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalUsingArrayParameterWithSizeParam(isByRef: false), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalUsingArrayParameterWithSizeParam(isByRef: false), 1, 0 }; - yield return new object[] { ID(), CodeSnippets.MarshalUsingArrayParameterWithSizeParam(isByRef: false), 2, 0 }; + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam(isByRef: false), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "pRef") + } }; + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam(isByRef: false), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "pRef") + } }; + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam(isByRef: false), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pRefSize"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "pRef") + } }; + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam(isByRef: true), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "pRef") + } }; + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam(isByRef: true), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "pRef") + } }; + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam(isByRef: true), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Marshalling bool without explicit marshalling information is not supported. Specify either 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "pRefSize"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "pRef") + } }; // Custom type marshalling with invalid members CustomStructMarshallingCodeSnippets customStructMarshallingCodeSnippets = new(new CodeSnippets()); - yield return new object[] { ID(), customStructMarshallingCodeSnippets.NonStaticMarshallerEntryPoint, 2, 0 }; - yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.ManagedToNativeOnlyOutParameter, 1, 0 }; - yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.ManagedToNativeOnlyReturnValue, 1, 0 }; - yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.NativeToManagedOnlyInParameter, 1, 0 }; - yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.StackallocOnlyRefParameter, 1, 0 }; - yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.ManagedToNativeOnlyOutParameter, 1, 0 }; - yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.ManagedToNativeOnlyReturnValue, 1, 0 }; - yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.NativeToManagedOnlyInParameter, 1, 0 }; - yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateful.StackallocOnlyRefParameter, 1, 0 }; - - // Abstract SafeHandle type by reference - yield return new object[] { ID(), CodeSnippets.BasicParameterWithByRefModifier("ref", "System.Runtime.InteropServices.SafeHandle"), 1, 0 }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.NonStaticMarshallerEntryPoint, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupported).WithLocation(0).WithArguments("S", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported).WithLocation(10).WithArguments(""), + } }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.ManagedToNativeOnlyOutParameter, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::Marshaller' does not support it.", "p"), + } }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.ManagedToNativeOnlyReturnValue, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::Marshaller' does not support it.", "Method"), + } }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.NativeToManagedOnlyInParameter, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::Marshaller' does not support it.", "p"), + } }; + yield return new object[] { ID(), customStructMarshallingCodeSnippets.Stateless.StackallocOnlyRefParameter, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged and unmanaged to managed, but the marshaller type 'global::Marshaller' does not support it.", "p"), + } }; + + // Abstract SafeHandle by reference + yield return new object[] { ID(), CodeSnippets.BasicParameterWithByRefModifier("ref", "System.Runtime.InteropServices.SafeHandle"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged and unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "p"), + } }; // SafeHandle array - yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers("Microsoft.Win32.SafeHandles.SafeFileHandle"), 5, 0 }; + yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers("Microsoft.Win32.SafeHandles.SafeFileHandle"), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "pIn"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged and unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(5) + .WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "pOut"), + } }; // SafeHandle with private constructor by ref or out - yield return new object[] { ID(), CodeSnippets.SafeHandleWithCustomDefaultConstructorAccessibility(privateCtor: true), 3, 0 }; + yield return new object[] { ID(), CodeSnippets.SafeHandleWithCustomDefaultConstructorAccessibility(privateCtor: true), new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "Method"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("The specified parameter needs to be marshalled from managed to unmanaged and unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "pRef"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("The specified parameter needs to be marshalled from unmanaged to managed, but the marshaller type 'global::System.Runtime.InteropServices.Marshalling.SafeHandleMarshaller' does not support it.", "pOut"), + } }; // Collection with constant and element size parameter - yield return new object[] { ID(), CodeSnippets.MarshalUsingCollectionWithConstantAndElementCount, 2, 0 }; - + yield return new object[] { ID(), CodeSnippets.MarshalUsingCollectionWithConstantAndElementCount, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(0) + .WithArguments(""), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + } }; // Collection with null element size parameter name - yield return new object[] { ID(), CodeSnippets.MarshalUsingCollectionWithNullElementName, 2, 0 }; + yield return new object[] { ID(), CodeSnippets.MarshalUsingCollectionWithNullElementName, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(0) + .WithArguments("null", "CountElementName"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling an array from unmanaged to managed requires either the 'SizeParamIndex' or 'SizeConst' fields to be set on a 'MarshalAsAttribute' or the 'ConstantElementCount' or 'CountElementName' properties to be set on a 'MarshalUsingAttribute'.", "pRef"), + } }; // Generic collection marshaller has different arity than collection. CustomCollectionMarshallingCodeSnippets customCollectionMarshallingCodeSnippets = new(new CodeSnippets()); - yield return new object[] { ID(), customCollectionMarshallingCodeSnippets.Stateless.GenericCollectionMarshallingArityMismatch, 2, 0 }; - - yield return new object[] { ID(), CodeSnippets.MarshalAsAndMarshalUsingOnReturnValue, 1, 0 }; - yield return new object[] { ID(), CodeSnippets.CustomElementMarshallingDuplicateElementIndirectionDepth, 1, 0 }; - yield return new object[] { ID(), CodeSnippets.CustomElementMarshallingUnusedElementIndirectionDepth, 1, 0 }; - yield return new object[] { ID(), CodeSnippets.RecursiveCountElementNameOnReturnValue, 2, 0 }; - yield return new object[] { ID(), CodeSnippets.RecursiveCountElementNameOnParameter, 2, 0 }; - yield return new object[] { ID(), CodeSnippets.MutuallyRecursiveCountElementNameOnParameter, 4, 0 }; - yield return new object[] { ID(), CodeSnippets.MutuallyRecursiveSizeParamIndexOnParameter, 4, 0 }; - - // Ref returns - yield return new object[] { ID(), CodeSnippets.RefReturn("int"), 2, 2 }; + yield return new object[] { ID(), customCollectionMarshallingCodeSnippets.Stateless.GenericCollectionMarshallingArityMismatch, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupported) + .WithLocation(0) + .WithArguments("TestCollection", "p"), + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(10) + .WithArguments(""), + } }; + + yield return new object[] { ID(), CodeSnippets.MarshalAsAndMarshalUsingOnReturnValue, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(0) + .WithArguments(""), + } }; + yield return new object[] { ID(), CodeSnippets.CustomElementMarshallingDuplicateElementIndirectionDepth, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(0) + .WithArguments(""), + } }; + yield return new object[] { ID(), CodeSnippets.CustomElementMarshallingUnusedElementIndirectionDepth, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(0) + .WithArguments(""), + } }; + yield return new object[] { ID(), CodeSnippets.RecursiveCountElementNameOnReturnValue, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(0) + .WithArguments(""), + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "Method"), + } }; + yield return new object[] { ID(), CodeSnippets.RecursiveCountElementNameOnParameter, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(0) + .WithArguments(""), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "arr"), + } }; + yield return new object[] { ID(), CodeSnippets.MutuallyRecursiveCountElementNameOnParameter, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(0) + .WithArguments(""), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "arr"), + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(2) + .WithArguments(""), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "arr2"), + } }; + yield return new object[] { ID(), CodeSnippets.MutuallyRecursiveSizeParamIndexOnParameter, new[] + { + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(0) + .WithArguments(""), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "arr"), + VerifyCS.Diagnostic(GeneratorDiagnostics.MarshallingAttributeConfigurationNotSupported) + .WithLocation(2) + .WithArguments(""), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("The specified collection size parameter for an collection must be an integer type. If the size information is applied to a nested collection, the size parameter must be a collection of one less level of nesting with an integral element.", "arr2"), + } }; + yield return new object[] { ID(), CodeSnippets.RefReturn("int"), new[] + { + DiagnosticResult.CompilerError("CS8795") + .WithLocation(0), + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(0) + .WithArguments("ref return", "Basic.RefReturn()"), + DiagnosticResult.CompilerError("CS8795") + .WithLocation(1), + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(1) + .WithArguments("ref return", "Basic.RefReadonlyReturn()"), + } }; } [Theory] [MemberData(nameof(CodeSnippetsToCompile))] - public async Task ValidateSnippets(string id, string source, int expectedGeneratorErrors, int expectedCompilerErrors) + public async Task ValidateSnippets(string id, string source, DiagnosticResult[] diagnostics) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - - // Verify the compilation failed with errors. - IEnumerable generatorErrors = generatorDiags.Where(d => d.Severity == DiagnosticSeverity.Error); - int generatorErrorCount = generatorErrors.Count(); - Assert.True( - expectedGeneratorErrors == generatorErrorCount, - $"Expected {expectedGeneratorErrors} errors, but encountered {generatorErrorCount}. Errors: {string.Join(Environment.NewLine, generatorErrors.Select(d => d.ToString()))}"); - - IEnumerable compilerErrors = newComp.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error); - int compilerErrorCount = compilerErrors.Count(); - Assert.True( - expectedCompilerErrors == compilerErrorCount, - $"Expected {expectedCompilerErrors} errors, but encountered {compilerErrorCount}. Errors: {string.Join(Environment.NewLine, compilerErrors.Select(d => d.ToString()))}"); + // Each snippet will contain the expected diagnostic codes in their expected locations for the compile errors. + // The test case will pass in the expected generator diagnostics. + await VerifyCS.VerifySourceGeneratorAsync(source, diagnostics); } public static IEnumerable CodeSnippetsToCompile_InvalidCode() { - yield return new object[] { ID(), CodeSnippets.RecursiveImplicitlyBlittableStruct, 0, 1 }; - yield return new object[] { ID(), CodeSnippets.MutuallyRecursiveImplicitlyBlittableStruct, 0, 2 }; - yield return new object[] { ID(), CodeSnippets.PartialPropertyName, 0, 2 }; - yield return new object[] { ID(), CodeSnippets.InvalidConstantForModuleName, 0, 1 }; - yield return new object[] { ID(), CodeSnippets.IncorrectAttributeFieldType, 0, 1 }; + yield return new[] { ID(), CodeSnippets.RecursiveImplicitlyBlittableStruct }; + yield return new[] { ID(), CodeSnippets.MutuallyRecursiveImplicitlyBlittableStruct }; + yield return new[] { ID(), CodeSnippets.PartialPropertyName }; + yield return new[] { ID(), CodeSnippets.InvalidConstantForModuleName }; + yield return new[] { ID(), CodeSnippets.IncorrectAttributeFieldType }; } [Theory] [MemberData(nameof(CodeSnippetsToCompile_InvalidCode))] - public async Task ValidateSnippets_InvalidCodeGracefulFailure(string id, string source, int expectedGeneratorErrors, int expectedCompilerErrors) + public async Task ValidateSnippets_InvalidCodeGracefulFailure(string id, string source) { TestUtils.Use(id); - // Do not validate that the compilation has no errors that the generator will not fix. - Compilation comp = await TestUtils.CreateCompilation(source); - - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - - // Verify the compilation failed with errors. - int generatorErrors = generatorDiags.Count(d => d.Severity == DiagnosticSeverity.Error); - Assert.Equal(expectedGeneratorErrors, generatorErrors); - - int compilerErrors = newComp.GetDiagnostics().Count(d => d.Severity == DiagnosticSeverity.Error); - Assert.Equal(expectedCompilerErrors, compilerErrors); + // Each snippet will contain the expected diagnostic codes in their expected locations for the compile errors. + // We expect there to be no generator diagnostics or failures. + await VerifyCS.VerifySourceGeneratorAsync(source); } [Fact] public async Task ValidateDisableRuntimeMarshallingForBlittabilityCheckFromAssemblyReference() { + // Emit the referenced assembly to a stream so we reference it through a metadata reference. + // Our check for strict blittability doesn't work correctly when using source compilation references. + // (There are sometimes false-positives.) + // This causes any diagnostics that depend on strict blittability being correctly calculated to + // not show up in the IDE experience. However, since they correctly show up when doing builds, + // either by running the Build command in the IDE or a command line build, we aren't allowing invalid code. + // This test validates the Build-like experience. In the future, we should update this test to validate the + // IDE-like experience once we fix that case + // (If the IDE experience works, then the command-line experience will also work.) + // This bug is tracked in https://github.com/dotnet/runtime/issues/84739. string assemblySource = $$""" - using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; {{CodeSnippets.ValidateDisableRuntimeMarshalling.NonBlittableUserDefinedTypeWithNativeType}} """; Compilation assemblyComp = await TestUtils.CreateCompilation(assemblySource); - TestUtils.AssertPreSourceGeneratorCompilation(assemblyComp); + Assert.Empty(assemblyComp.GetDiagnostics()); var ms = new MemoryStream(); Assert.True(assemblyComp.Emit(ms).Success); string testSource = CodeSnippets.ValidateDisableRuntimeMarshalling.TypeUsage(string.Empty); - Compilation testComp = await TestUtils.CreateCompilation(testSource, refs: new[] { MetadataReference.CreateFromImage(ms.ToArray()) }); - TestUtils.AssertPreSourceGeneratorCompilation(testComp); + VerifyCS.Test test = new(referenceAncillaryInterop: false) + { + TestCode = testSource, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; - var newComp = TestUtils.RunGenerators(testComp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); + test.TestState.AdditionalReferences.Add(MetadataReference.CreateFromImage(ms.ToArray())); // The errors should indicate the DisableRuntimeMarshalling is required. - Assert.True(generatorDiags.All(d => d.Id == "SYSLIB1051")); - - TestUtils.AssertPostSourceGeneratorCompilation(newComp); + test.ExpectedDiagnostics.Add( + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "Method")); + test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "p")); + test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(2) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "pIn")); + test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(3) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "pRef")); + test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(4) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "pOut")); + + await test.RunAsync(); } [Fact] public async Task ValidateRequireAllowUnsafeBlocksDiagnostic() { - string source = CodeSnippets.TrivialClassDeclarations; - Compilation comp = await TestUtils.CreateCompilation(new[] { source }, allowUnsafe: false); - TestUtils.AssertPreSourceGeneratorCompilation(comp); + var test = new AllowUnsafeBlocksTest() + { + TestCode = CodeSnippets.TrivialClassDeclarations, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; + + test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic("SYSLIB1062")); + test.ExpectedDiagnostics.Add(DiagnosticResult.CompilerError("CS0227").WithLocation(0)); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); + await test.RunAsync(); + } - // The errors should indicate the AllowUnsafeBlocks is required. - Assert.True(generatorDiags.All(d => d.Id == "SYSLIB1062")); + class AllowUnsafeBlocksTest : VerifyCS.Test + { + public AllowUnsafeBlocksTest() + :base(referenceAncillaryInterop: false) + { + } - // There should only be one SYSLIB1062, even if there are multiple LibraryImportAttribute uses. - Assert.Equal(1, generatorDiags.Count()); + protected override CompilationOptions CreateCompilationOptions() => ((CSharpCompilationOptions)base.CreateCompilationOptions()).WithAllowUnsafe(false); } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs index 9eb21ba33484e..6a932ac60a1ce 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs @@ -14,6 +14,13 @@ using Xunit; using SourceGenerators.Tests; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; +using Microsoft.CodeAnalysis.Testing; +using System.Collections.Immutable; +using System.Threading; +using Microsoft.CodeAnalysis.Text; +using System.Text; + namespace LibraryImportGenerator.UnitTests { public class Compiles @@ -417,13 +424,8 @@ public static IEnumerable CustomCollections() public async Task ValidateSnippets(string id, string source) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - Assert.Empty(generatorDiags); - TestUtils.AssertPostSourceGeneratorCompilation(newComp); + await VerifyCS.VerifySourceGeneratorAsync(source); } public static IEnumerable CodeSnippetsToCompileWithPreprocessorSymbols() @@ -442,23 +444,37 @@ public static IEnumerable CodeSnippetsToCompileWithPreprocessorSymbols public async Task ValidateSnippetsWithPreprocessorDefinitions(string id, string source, IEnumerable preprocessorSymbols) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(source, preprocessorSymbols: preprocessorSymbols); - TestUtils.AssertPreSourceGeneratorCompilation(comp); + var test = new PreprocessorTest(preprocessorSymbols) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - Assert.Empty(generatorDiags); + await test.RunAsync(); + } - TestUtils.AssertPostSourceGeneratorCompilation(newComp); + private class PreprocessorTest : VerifyCS.Test + { + private readonly IEnumerable _preprocessorSymbols; + + public PreprocessorTest(IEnumerable preprocessorSymbols) + :base(referenceAncillaryInterop: false) + { + _preprocessorSymbols = preprocessorSymbols; + } + + protected override ParseOptions CreateParseOptions() + => ((CSharpParseOptions)base.CreateParseOptions()).WithPreprocessorSymbols(_preprocessorSymbols); } public static IEnumerable CodeSnippetsToValidateFallbackForwarder() { - yield return new object[] { ID(), CodeSnippets.UserDefinedEntryPoint, TestTargetFramework.Net, true }; + //yield return new object[] { ID(), CodeSnippets.UserDefinedEntryPoint, TestTargetFramework.Net, true }; // Confirm that all unsupported target frameworks can be generated. { string code = CodeSnippets.BasicParametersAndModifiers(CodeSnippets.LibraryImportAttributeDeclaration); - yield return new object[] { ID(), code, TestTargetFramework.Net6, false }; + //yield return new object[] { ID(), code, TestTargetFramework.Net6, false }; yield return new object[] { ID(), code, TestTargetFramework.Core, false }; yield return new object[] { ID(), code, TestTargetFramework.Standard, false }; yield return new object[] { ID(), code, TestTargetFramework.Framework, false }; @@ -498,31 +514,38 @@ public static IEnumerable CodeSnippetsToValidateFallbackForwarder() public async Task ValidateSnippetsFallbackForwarder(string id, string source, TestTargetFramework targetFramework, bool expectFallbackForwarder) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(source, targetFramework); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators( - comp, - new GlobalOptionsOnlyProvider(new TargetFrameworkConfigOptions(targetFramework)), - out var generatorDiags, - new Microsoft.Interop.LibraryImportGenerator()); + var test = new FallbackForwarderTest(targetFramework, expectFallbackForwarder) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; - Assert.Empty(generatorDiags); + await test.RunAsync(); + } - TestUtils.AssertPostSourceGeneratorCompilation(newComp); + class FallbackForwarderTest : VerifyCS.Test + { + private readonly bool _expectFallbackForwarder; - // Verify that the forwarder generates the method as a DllImport. - SyntaxTree generatedCode = newComp.SyntaxTrees.Last(); - SemanticModel model = newComp.GetSemanticModel(generatedCode); - var methods = generatedCode.GetRoot() - .DescendantNodes().OfType() - .ToList(); - MethodDeclarationSyntax generatedMethod = Assert.Single(methods); + public FallbackForwarderTest(TestTargetFramework targetFramework, bool expectFallbackForwarder) + :base(targetFramework) + { + _expectFallbackForwarder = expectFallbackForwarder; + } + protected override void VerifyFinalCompilation(Compilation compilation) + { + SyntaxTree generatedCode = compilation.SyntaxTrees.Last(); + SemanticModel model = compilation.GetSemanticModel(generatedCode); + var methods = generatedCode.GetRoot() + .DescendantNodes().OfType() + .ToList(); + MethodDeclarationSyntax generatedMethod = Assert.Single(methods); - IMethodSymbol method = model.GetDeclaredSymbol(generatedMethod)!; + IMethodSymbol method = model.GetDeclaredSymbol(generatedMethod)!; - // If we expect fallback forwarder, then the DllImportData will not be null. - Assert.Equal(expectFallbackForwarder, method.GetDllImportData() is not null); + // If we expect fallback forwarder, then the DllImportData will not be null. + Assert.Equal(_expectFallbackForwarder, method.GetDllImportData() is not null); + } } public static IEnumerable FullyBlittableSnippetsToCompile() @@ -536,26 +559,32 @@ public static IEnumerable FullyBlittableSnippetsToCompile() public async Task ValidateSnippetsWithBlittableAutoForwarding(string id, string source) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators( - comp, - out var generatorDiags, - new Microsoft.Interop.LibraryImportGenerator()); + var test = new BlittableAutoForwarderTest() + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; - Assert.Empty(generatorDiags); + await test.RunAsync(); + } - TestUtils.AssertPostSourceGeneratorCompilation(newComp); + class BlittableAutoForwarderTest : VerifyCS.Test + { + public BlittableAutoForwarderTest() + :base(referenceAncillaryInterop: false) + { + } - // Verify that the forwarder generates the method as a DllImport. - SyntaxTree generatedCode = newComp.SyntaxTrees.Last(); - SemanticModel model = newComp.GetSemanticModel(generatedCode); - var methods = generatedCode.GetRoot() - .DescendantNodes().OfType() - .ToList(); + protected override void VerifyFinalCompilation(Compilation compilation) + { + SyntaxTree generatedCode = compilation.SyntaxTrees.Last(); + SemanticModel model = compilation.GetSemanticModel(generatedCode); + var methods = generatedCode.GetRoot() + .DescendantNodes().OfType() + .ToList(); - Assert.All(methods, method => Assert.NotNull(model.GetDeclaredSymbol(method)!.GetDllImportData())); + Assert.All(methods, method => Assert.NotNull(model.GetDeclaredSymbol(method)!.GetDllImportData())); + } } public static IEnumerable SnippetsWithBlittableTypesButNonBlittableDataToCompile() @@ -570,29 +599,34 @@ public static IEnumerable SnippetsWithBlittableTypesButNonBlittableDat public async Task ValidateSnippetsWithBlittableTypesButNonBlittableMetadataDoNotAutoForward(string id, string source) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators( - comp, - out var generatorDiags, - new Microsoft.Interop.LibraryImportGenerator()); - - Assert.Empty(generatorDiags); + var test = new NonBlittableNoAutoForwardTest() + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; - TestUtils.AssertPostSourceGeneratorCompilation(newComp); + await test.RunAsync(); + } - // Verify that the generator generates stubs with inner DllImports for all methods. - SyntaxTree generatedCode = newComp.SyntaxTrees.Last(); - SemanticModel model = newComp.GetSemanticModel(generatedCode); - int numStubMethods = generatedCode.GetRoot() - .DescendantNodes().OfType() - .Count(); - int numInnerDllImports = generatedCode.GetRoot() - .DescendantNodes().OfType() - .Count(); + class NonBlittableNoAutoForwardTest : VerifyCS.Test + { + public NonBlittableNoAutoForwardTest() + : base(referenceAncillaryInterop: false) + { + } - Assert.Equal(numStubMethods, numInnerDllImports); + protected override void VerifyFinalCompilation(Compilation compilation) + { + SyntaxTree generatedCode = compilation.SyntaxTrees.Last(); + SemanticModel model = compilation.GetSemanticModel(generatedCode); + int numStubMethods = generatedCode.GetRoot() + .DescendantNodes().OfType() + .Count(); + int numInnerDllImports = generatedCode.GetRoot() + .DescendantNodes().OfType() + .Count(); + Assert.Equal(numStubMethods, numInnerDllImports); + } } public static IEnumerable CodeSnippetsToCompileWithMarshalType() @@ -609,18 +643,21 @@ public static IEnumerable CodeSnippetsToCompileWithMarshalType() public async Task ValidateSnippetsWithMarshalType(string id, string source) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators( - comp, - new LibraryImportGeneratorOptionsProvider(TestTargetFramework.Net, useMarshalType: true, generateForwarders: false), - out var generatorDiags, - new Microsoft.Interop.LibraryImportGenerator()); - - Assert.Empty(generatorDiags); - - TestUtils.AssertPostSourceGeneratorCompilation(newComp, "CS0117"); + var test = new VerifyCS.Test(referenceAncillaryInterop: true) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; + test.SolutionTransforms.Add((solution, projectId) => + solution.AddAnalyzerConfigDocument(DocumentId.CreateNewId(projectId), + "UseMarshalType.editorconfig", + SourceText.From(""" + is_global = true + build_property.LibraryImportGenerator_UseMarshalType = true + """, + Encoding.UTF8), + filePath: "/UseMarshalType.editorconfig")); + await test.RunAsync(); } public static IEnumerable CodeSnippetsToCompileMultipleSources() @@ -635,13 +672,16 @@ public static IEnumerable CodeSnippetsToCompileMultipleSources() public async Task ValidateSnippetsWithMultipleSources(string id, string[] sources) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(sources); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - Assert.Empty(generatorDiags); + // To enable us to reuse snippets that have markup locations in our multiple-sources test, we'll strip out the markup locations. + // We need to do this as each snippet expects to be able to define all expected markup locations (starting from 0), so including multiple snippets + // results in multiple definitions for the same location (which doesn't work). Since we expect no diagnostics, we can strip out the locations. + await VerifyCS.VerifySourceGeneratorAsync(sources.Select(RemoveTestMarkup).ToArray()); + } - TestUtils.AssertPostSourceGeneratorCompilation(newComp); + private static string RemoveTestMarkup(string sourceWithMarkup) + { + TestFileMarkupParser.GetSpans(sourceWithMarkup, out string sourceWithoutMarkup, out ImmutableArray _); + return sourceWithoutMarkup; } public static IEnumerable CodeSnippetsToVerifyNoTreesProduced() @@ -660,14 +700,29 @@ public class Basic { } public async Task ValidateNoGeneratedOuptutForNoImport(string id, string source, TestTargetFramework framework) { TestUtils.Use(id); - Compilation comp = await TestUtils.CreateCompilation(source, framework, allowUnsafe: false); - TestUtils.AssertPreSourceGeneratorCompilation(comp); + var test = new NoChangeTest(framework) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck + }; + + await test.RunAsync(); + } - var newComp = TestUtils.RunGenerators(comp, new GlobalOptionsOnlyProvider(new TargetFrameworkConfigOptions(framework)), out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - Assert.Empty(generatorDiags); + class NoChangeTest : VerifyCS.Test + { + public NoChangeTest(TestTargetFramework framework) + :base(framework) + { + } - // Assert we didn't generate any syntax trees, even empty ones - Assert.Same(comp, newComp); + protected async override Task<(Compilation compilation, ImmutableArray generatorDiagnostics)> GetProjectCompilationAsync(Project project, IVerifier verifier, CancellationToken cancellationToken) + { + var originalCompilation = await project.GetCompilationAsync(cancellationToken); + var (newCompilation, diagnostics) = await base.GetProjectCompilationAsync(project, verifier, cancellationToken); + Assert.Same(originalCompilation, newCompilation); + return (newCompilation, diagnostics); + } } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs index 580683b480f2f..d2bd400584f2f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs @@ -10,7 +10,7 @@ using static Microsoft.Interop.Analyzers.ConvertToLibraryImportAnalyzer; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpAnalyzerVerifier; namespace LibraryImportGenerator.UnitTests { diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs index e930c5d149338..14bb0e4945068 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs @@ -9,9 +9,8 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.Interop.Analyzers; using Xunit; -using static Microsoft.Interop.Analyzers.ConvertToLibraryImportFixer; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier< +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier< Microsoft.Interop.Analyzers.ConvertToLibraryImportAnalyzer, Microsoft.Interop.Analyzers.ConvertToLibraryImportFixer>; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTest.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTest.cs index c56b8aa792f21..2380a0b9d8b4a 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTest.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTest.cs @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; -using LibraryImportGenerator.UnitTests.Verifiers; +using Microsoft.Interop.UnitTests.Verifiers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using Microsoft.Interop.Analyzers; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_AttributeUsage.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_AttributeUsage.cs index c846550b7c10b..03775e7aef3a1 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_AttributeUsage.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_AttributeUsage.cs @@ -8,7 +8,7 @@ using Xunit; using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier< +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier< Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer, Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulLinearCollectionShapeValidation.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulLinearCollectionShapeValidation.cs index 62d599fea5494..c2fc65ead881f 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulLinearCollectionShapeValidation.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulLinearCollectionShapeValidation.cs @@ -9,7 +9,7 @@ using Xunit; using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier< +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier< Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer, Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulValueShapeValidation.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulValueShapeValidation.cs index 6cf2b96e61912..b8dbae8e318d8 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulValueShapeValidation.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulValueShapeValidation.cs @@ -10,7 +10,7 @@ using Xunit; using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier< +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier< Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer, Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessLinearCollectionShapeValidation.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessLinearCollectionShapeValidation.cs index 28149a0f7864e..e4b4b63778ed7 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessLinearCollectionShapeValidation.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessLinearCollectionShapeValidation.cs @@ -9,7 +9,7 @@ using Xunit; using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier< +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier< Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer, Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessValueShapeValidation.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessValueShapeValidation.cs index b855690efc2b3..6fcc90dae43dd 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessValueShapeValidation.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessValueShapeValidation.cs @@ -9,7 +9,7 @@ using Xunit; using static Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier< +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpCodeFixVerifier< Microsoft.Interop.Analyzers.CustomMarshallerAttributeAnalyzer, Microsoft.Interop.Analyzers.CustomMarshallerAttributeFixer>; diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Diagnostics.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Diagnostics.cs index 63119dd4a8d61..030ffba39431d 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Diagnostics.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Diagnostics.cs @@ -11,10 +11,10 @@ using Microsoft.CodeAnalysis.Testing; using Microsoft.Interop; using Microsoft.Interop.UnitTests; -using SourceGenerators.Tests; using Xunit; using StringMarshalling = Microsoft.Interop.StringMarshalling; +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier; namespace LibraryImportGenerator.UnitTests { @@ -34,29 +34,20 @@ class MyClass { } partial class Test { [LibraryImport("DoesNotExist")] - public static partial void Method1(NS.MyClass c); + public static partial void Method1(NS.MyClass {|#0:c|}); [LibraryImport("DoesNotExist")] - public static partial void Method2(int i, List list); + public static partial void Method2(int i, List {|#1:list|}); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.ParameterTypeNotSupported)) - .WithSpan(11, 51, 11, 52) + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupported) + .WithLocation(0) .WithArguments("NS.MyClass", "c"), - (new DiagnosticResult(GeneratorDiagnostics.ParameterTypeNotSupported)) - .WithSpan(14, 57, 14, 61) - .WithArguments("System.Collections.Generic.List", "list"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupported) + .WithLocation(1) + .WithArguments("System.Collections.Generic.List", "list")); } [Fact] @@ -73,29 +64,20 @@ class MyClass { } partial class Test { [LibraryImport("DoesNotExist")] - public static partial NS.MyClass Method1(); + public static partial NS.MyClass {|#0:Method1|}(); [LibraryImport("DoesNotExist")] - public static partial List Method2(); + public static partial List {|#1:Method2|}(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.ReturnTypeNotSupported)) - .WithSpan(11, 38, 11, 45) + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupported) + .WithLocation(0) .WithArguments("NS.MyClass", "Method1"), - (new DiagnosticResult(GeneratorDiagnostics.ReturnTypeNotSupported)) - .WithSpan(14, 37, 14, 44) - .WithArguments("System.Collections.Generic.List", "Method2"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupported) + .WithLocation(1) + .WithArguments("System.Collections.Generic.List", "Method2")); } [Fact] @@ -107,24 +89,17 @@ public async Task ParameterTypeNotSupportedWithDetails_ReportsDiagnostic() partial class Test { [LibraryImport("DoesNotExist")] - public static partial void Method(char c, string s); + public static partial void Method(char {|#0:c|}, string {|#1:s|}); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails)) - .WithSpan(6, 44, 6, 45), - (new DiagnosticResult(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails)) - .WithSpan(6, 54, 6, 55), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "c"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "s")); } [Fact] @@ -136,27 +111,20 @@ public async Task ReturnTypeNotSupportedWithDetails_ReportsDiagnostic() partial class Test { [LibraryImport("DoesNotExist")] - public static partial char Method1(); + public static partial char {|#0:Method1|}(); [LibraryImport("DoesNotExist")] - public static partial string Method2(); + public static partial string {|#1:Method2|}(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails)) - .WithSpan(6, 32, 6, 39), - (new DiagnosticResult(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails)) - .WithSpan(9, 34, 9, 41), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(0) + .WithArguments("Runtime marshalling must be disabled in this project by applying the 'System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute' to the assembly to enable marshalling this type.", "Method1"), + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "Method2")); } [Fact] @@ -168,29 +136,20 @@ public async Task ParameterConfigurationNotSupported_ReportsDiagnostic() partial class Test { [LibraryImport("DoesNotExist")] - public static partial void Method1([MarshalAs(UnmanagedType.BStr)] int i1, int i2); + public static partial void Method1([MarshalAs(UnmanagedType.BStr)] int {|#0:i1|}, int i2); [LibraryImport("DoesNotExist")] - public static partial void Method2(int i1, [MarshalAs(UnmanagedType.FunctionPtr)] bool b2); + public static partial void Method2(int i1, [MarshalAs(UnmanagedType.FunctionPtr)] bool {|#1:b2|}); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.ParameterConfigurationNotSupported)) - .WithSpan(6, 76, 6, 78) + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(0) .WithArguments(nameof(MarshalAsAttribute), "i1"), - (new DiagnosticResult(GeneratorDiagnostics.ParameterConfigurationNotSupported)) - .WithSpan(9, 92, 9, 94) - .WithArguments(nameof(MarshalAsAttribute), "b2"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(1) + .WithArguments(nameof(MarshalAsAttribute), "b2")); } [Fact] @@ -203,30 +162,21 @@ partial class Test { [LibraryImport("DoesNotExist")] [return: MarshalAs(UnmanagedType.BStr)] - public static partial int Method1(int i); + public static partial int {|#0:Method1|}(int i); [LibraryImport("DoesNotExist")] [return: MarshalAs(UnmanagedType.FunctionPtr)] - public static partial bool Method2(int i); + public static partial bool {|#1:Method2|}(int i); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.ReturnConfigurationNotSupported)) - .WithSpan(7, 31, 7, 38) + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(0) .WithArguments(nameof(MarshalAsAttribute), "Method1"), - (new DiagnosticResult(GeneratorDiagnostics.ReturnConfigurationNotSupported)) - .WithSpan(11, 32, 11, 39) - .WithArguments(nameof(MarshalAsAttribute), "Method2"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(1) + .WithArguments(nameof(MarshalAsAttribute), "Method2")); } [Fact] @@ -238,36 +188,27 @@ public async Task MarshalAsUnmanagedTypeNotSupported_ReportsDiagnostic() partial class Test { [LibraryImport("DoesNotExist")] - [return: MarshalAs(1)] - public static partial int Method1(int i); + [return: {|#0:MarshalAs(1)|}] + public static partial int {|#1:Method1|}(int i); [LibraryImport("DoesNotExist")] - public static partial int Method2([MarshalAs((short)0)] bool b); + public static partial int Method2([{|#2:MarshalAs((short)0)|}] bool {|#3:b|}); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.ConfigurationValueNotSupported)) - .WithSpan(6, 14, 6, 26) + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(0) .WithArguments(1, nameof(UnmanagedType)), - (new DiagnosticResult(GeneratorDiagnostics.ReturnConfigurationNotSupported)) - .WithSpan(7, 31, 7, 38) + VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnConfigurationNotSupported) + .WithLocation(1) .WithArguments(nameof(MarshalAsAttribute), "Method1"), - (new DiagnosticResult(GeneratorDiagnostics.ConfigurationValueNotSupported)) - .WithSpan(10, 40, 10, 59) + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationValueNotSupported) + .WithLocation(2) .WithArguments(0, nameof(UnmanagedType)), - (new DiagnosticResult(GeneratorDiagnostics.ParameterConfigurationNotSupported)) - .WithSpan(10, 66, 10, 67) - .WithArguments(nameof(MarshalAsAttribute), "b"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterConfigurationNotSupported) + .WithLocation(3) + .WithArguments(nameof(MarshalAsAttribute), "b")); } [Fact] @@ -279,29 +220,21 @@ public async Task MarshalAsFieldNotSupported_ReportsDiagnostic() partial class Test { [LibraryImport("DoesNotExist")] - [return: MarshalAs(UnmanagedType.I4, SafeArraySubType=VarEnum.VT_I4)] + [return: {|#0:MarshalAs(UnmanagedType.I4, SafeArraySubType=VarEnum.VT_I4)|}] public static partial int Method1(int i); [LibraryImport("DoesNotExist")] - public static partial int Method2([MarshalAs(UnmanagedType.I1, IidParameterIndex = 1)] bool b); + public static partial int Method2([{|#1:MarshalAs(UnmanagedType.I1, IidParameterIndex = 1)|}] bool b); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.ConfigurationNotSupported)) - .WithSpan(6, 14, 6, 73) + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(0) .WithArguments($"{nameof(MarshalAsAttribute)}{Type.Delimiter}{nameof(MarshalAsAttribute.SafeArraySubType)}"), - (new DiagnosticResult(GeneratorDiagnostics.ConfigurationNotSupported)) - .WithSpan(10, 40, 10, 90) - .WithArguments($"{nameof(MarshalAsAttribute)}{Type.Delimiter}{nameof(MarshalAsAttribute.IidParameterIndex)}"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + VerifyCS.Diagnostic(GeneratorDiagnostics.ConfigurationNotSupported) + .WithLocation(1) + .WithArguments($"{nameof(MarshalAsAttribute)}{Type.Delimiter}{nameof(MarshalAsAttribute.IidParameterIndex)}")); } [Fact] @@ -314,10 +247,10 @@ public async Task StringMarshallingForwardingNotSupported_ReportsDiagnostic() partial class Test { [LibraryImport("DoesNotExist", StringMarshalling = StringMarshalling.Utf8)] - public static partial void Method1(string s); + public static partial void {|#0:Method1|}(string s); [LibraryImport("DoesNotExist", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(Native))] - public static partial void Method2(string s); + public static partial void Method2(string {|#1:s|}); struct Native { @@ -326,26 +259,23 @@ public Native(string s) { } } } """ + CodeSnippets.LibraryImportAttributeDeclaration; - - // Compile against Standard so that we generate forwarders - Compilation comp = await TestUtils.CreateCompilation(source, TestTargetFramework.Standard); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - Compilation newComp = TestUtils.RunGenerators( - comp, - new GlobalOptionsOnlyProvider(new TargetFrameworkConfigOptions(TestTargetFramework.Standard)), - out var generatorDiags, - new Microsoft.Interop.LibraryImportGenerator()); DiagnosticResult[] expectedDiags = new DiagnosticResult[] { - (new DiagnosticResult(GeneratorDiagnostics.CannotForwardToDllImport)) - .WithSpan(6, 32, 6, 39) + VerifyCS.Diagnostic(GeneratorDiagnostics.CannotForwardToDllImport) + .WithLocation(0) .WithArguments($"{nameof(TypeNames.LibraryImportAttribute)}{Type.Delimiter}{nameof(StringMarshalling)}={nameof(StringMarshalling)}{Type.Delimiter}{nameof(StringMarshalling.Utf8)}"), - (new DiagnosticResult(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails)) - .WithSpan(9, 47, 9, 48) + VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupportedWithDetails) + .WithLocation(1) + .WithArguments("Marshalling string or char without explicit marshalling information is not supported. Specify 'LibraryImportAttribute.StringMarshalling', 'LibraryImportAttribute.StringMarshallingCustomType', 'MarshalUsingAttribute' or 'MarshalAsAttribute'.", "s") + }; + + var test = new VerifyCS.Test(TestTargetFramework.Standard) + { + TestCode = source, + TestBehaviors = TestBehaviors.SkipGeneratedSourcesCheck }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + test.ExpectedDiagnostics.AddRange(expectedDiags); + await test.RunAsync(); } [Fact] @@ -357,10 +287,10 @@ public async Task InvalidStringMarshallingConfiguration_ReportsDiagnostic() {{CodeSnippets.DisableRuntimeMarshalling}} partial class Test { - [LibraryImport("DoesNotExist", StringMarshalling = StringMarshalling.Custom)] + [{|#0:LibraryImport("DoesNotExist", StringMarshalling = StringMarshalling.Custom)|}] public static partial void Method1(out int i); - [LibraryImport("DoesNotExist", StringMarshalling = StringMarshalling.Utf8, StringMarshallingCustomType = typeof(Native))] + [{|#1:LibraryImport("DoesNotExist", StringMarshalling = StringMarshalling.Utf8, StringMarshallingCustomType = typeof(Native))|}] public static partial void Method2(out int i); struct Native @@ -371,20 +301,14 @@ public Native(string s) { } } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.InvalidStringMarshallingConfiguration)) - .WithSpan(6, 6, 6, 81), - (new DiagnosticResult(GeneratorDiagnostics.InvalidStringMarshallingConfiguration)) - .WithSpan(9, 6, 9, 125) - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidStringMarshallingConfiguration) + .WithLocation(0) + .WithArguments("Method1", "'StringMarshallingCustomType' must be specified when 'StringMarshalling' is set to 'StringMarshalling.Custom'."), + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidStringMarshallingConfiguration) + .WithLocation(1) + .WithArguments("Method2", "'StringMarshalling' should be set to 'StringMarshalling.Custom' when 'StringMarshallingCustomType' is specified.")); } [Fact] @@ -396,28 +320,20 @@ public async Task NonPartialMethod_ReportsDiagnostic() partial class Test { [LibraryImport("DoesNotExist")] - public static void Method() { } + public static void {|#0:Method|}() { } [LibraryImport("DoesNotExist")] - public static extern void ExternMethod(); + public static extern void {|#1:ExternMethod|}(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.InvalidAttributedMethodSignature)) - .WithSpan(6, 24, 6, 30) + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidAttributedMethodSignature) + .WithLocation(0) .WithArguments("Method"), - (new DiagnosticResult(GeneratorDiagnostics.InvalidAttributedMethodSignature)) - .WithSpan(9, 31, 9, 43) - .WithArguments("ExternMethod"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - var newCompDiags = newComp.GetDiagnostics(); - Assert.Empty(newCompDiags); + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidAttributedMethodSignature) + .WithLocation(1) + .WithArguments("ExternMethod")); } [Fact] @@ -429,23 +345,17 @@ public async Task NonStaticMethod_ReportsDiagnostic() partial class Test { [LibraryImport("DoesNotExist")] - public partial void Method(); + public partial void {|#0:Method|}(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.InvalidAttributedMethodSignature)) - .WithSpan(6, 25, 6, 31) - .WithArguments("Method") - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - // Generator ignores the method - TestUtils.AssertPreSourceGeneratorCompilation(newComp); + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidAttributedMethodSignature) + .WithLocation(0) + .WithArguments("Method"), + // Generator ignores the method + DiagnosticResult.CompilerError("CS8795") + .WithLocation(0)); } [Fact] @@ -457,29 +367,25 @@ public async Task GenericMethod_ReportsDiagnostic() partial class Test { [LibraryImport("DoesNotExist")] - public static partial void Method1(); + public static partial void {|#0:Method1|}(); [LibraryImport("DoesNotExist")] - public static partial void Method2(); + public static partial void {|#1:Method2|}(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.InvalidAttributedMethodSignature)) - .WithSpan(6, 32, 6, 39) + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidAttributedMethodSignature) + .WithLocation(0) .WithArguments("Method1"), - (new DiagnosticResult(GeneratorDiagnostics.InvalidAttributedMethodSignature)) - .WithSpan(9, 32, 9, 39) + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidAttributedMethodSignature) + .WithLocation(1) .WithArguments("Method2"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - - // Generator ignores the method - TestUtils.AssertPreSourceGeneratorCompilation(newComp); + // Generator ignores the method + DiagnosticResult.CompilerError("CS8795") + .WithLocation(0), + DiagnosticResult.CompilerError("CS8795") + .WithLocation(1)); } [Theory] @@ -494,26 +400,20 @@ public async Task NonPartialParentType_Diagnostic(string typeKind) {{typeKind}} Test { [LibraryImport("DoesNotExist")] - public static partial void Method(); + public static partial void {|#0:Method|}(); } """; - Compilation comp = await TestUtils.CreateCompilation(source); - - // Also expect CS0751: A partial method must be declared within a partial type - string additionalDiag = "CS0751"; - TestUtils.AssertPreSourceGeneratorCompilation(comp, additionalDiag); - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers)) - .WithSpan(6, 32, 6, 38) + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers) + .WithLocation(0) .WithArguments("Method", "Test"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - - // Generator ignores the method - TestUtils.AssertPreSourceGeneratorCompilation(newComp, additionalDiag); + // Generator ignores the method + DiagnosticResult.CompilerError("CS8795") + .WithLocation(0), + // Also expect CS0751: A partial method must be declared within a partial type + DiagnosticResult.CompilerError("CS0751") + .WithLocation(0)); } [Theory] @@ -530,24 +430,15 @@ public async Task NonPartialGrandparentType_Diagnostic(string typeKind) partial class TestInner { [LibraryImport("DoesNotExist")] - static partial void Method(); + static partial void {|#0:Method|}(); } } """; - Compilation comp = await TestUtils.CreateCompilation(source); - TestUtils.AssertPreSourceGeneratorCompilation(comp); - - var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); - DiagnosticResult[] expectedDiags = new DiagnosticResult[] - { - (new DiagnosticResult(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers)) - .WithSpan(8, 29, 8, 35) - .WithArguments("Method", "Test"), - }; - VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags)); - // Generator ignores the method - TestUtils.AssertPreSourceGeneratorCompilation(newComp); + await VerifyCS.VerifySourceGeneratorAsync(source, + VerifyCS.Diagnostic(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers) + .WithLocation(0) + .WithArguments("Method", "Test")); } private static void VerifyDiagnostics(DiagnosticResult[] expectedDiagnostics, Diagnostic[] actualDiagnostics) diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/IncrementalGenerationTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/IncrementalGenerationTests.cs index 27df3c92b16ca..78ed48e8c0e44 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/IncrementalGenerationTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/IncrementalGenerationTests.cs @@ -3,12 +3,15 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; using Microsoft.Interop.UnitTests; using Xunit; using static Microsoft.Interop.LibraryImportGenerator; @@ -19,13 +22,10 @@ public class IncrementalGenerationTests { private static readonly GeneratorDriverOptions EnableIncrementalTrackingDriverOptions = new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, trackIncrementalGeneratorSteps: true); - public const string RequiresIncrementalSyntaxTreeModifySupport = "The GeneratorDriver treats all SyntaxTree replace operations on a Compilation as an Add/Remove operation instead of a Modify operation" - + ", so all cached results based on that input are thrown out. As a result, we cannot validate that unrelated changes within the same SyntaxTree do not cause regeneration."; - [Fact] public async Task AddingNewUnrelatedType_DoesNotRegenerateSource() { - string source = CodeSnippets.BasicParametersAndModifiers(); + string source = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers()); Compilation comp1 = await TestUtils.CreateCompilation(source); @@ -52,7 +52,7 @@ public async Task AppendingUnrelatedSource_DoesNotRegenerateSource() string source = $$""" namespace NS { - {{CodeSnippets.BasicParametersAndModifiers()}} + {{RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers())}} } """; @@ -83,7 +83,7 @@ namespace NS [Fact] public async Task AddingFileWithNewLibraryImport_DoesNotRegenerateOriginalMethod() { - string source = CodeSnippets.BasicParametersAndModifiers(); + string source = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers()); Compilation comp1 = await TestUtils.CreateCompilation(source); @@ -92,7 +92,7 @@ public async Task AddingFileWithNewLibraryImport_DoesNotRegenerateOriginalMethod driver = driver.RunGenerators(comp1); - Compilation comp2 = comp1.AddSyntaxTrees(CSharpSyntaxTree.ParseText(CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.I1), new CSharpParseOptions(LanguageVersion.Preview))); + Compilation comp2 = comp1.AddSyntaxTrees(CSharpSyntaxTree.ParseText(RemoveTestMarkup(CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.I1)), new CSharpParseOptions(LanguageVersion.Preview))); GeneratorDriver driver2 = driver.RunGenerators(comp2); GeneratorRunResult runResult = driver2.GetRunResult().Results[0]; @@ -113,14 +113,14 @@ public async Task AddingFileWithNewLibraryImport_DoesNotRegenerateOriginalMethod [Fact] public async Task ReplacingFileWithNewLibraryImport_DoesNotRegenerateStubsInOtherFiles() { - Compilation comp1 = await TestUtils.CreateCompilation(new string[] { CodeSnippets.BasicParametersAndModifiers(), CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.I1) }); + Compilation comp1 = await TestUtils.CreateCompilation(new string[] { RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers()), RemoveTestMarkup(CodeSnippets.MarshalAsParametersAndModifiers(UnmanagedType.I1)) }); Microsoft.Interop.LibraryImportGenerator generator = new(); GeneratorDriver driver = TestUtils.CreateDriver(comp1, null, new[] { generator }, EnableIncrementalTrackingDriverOptions); driver = driver.RunGenerators(comp1); - Compilation comp2 = comp1.ReplaceSyntaxTree(comp1.SyntaxTrees.First(), CSharpSyntaxTree.ParseText(CodeSnippets.BasicParametersAndModifiers(), new CSharpParseOptions(LanguageVersion.Preview))); + Compilation comp2 = comp1.ReplaceSyntaxTree(comp1.SyntaxTrees.First(), CSharpSyntaxTree.ParseText(RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers()), new CSharpParseOptions(LanguageVersion.Preview))); GeneratorDriver driver2 = driver.RunGenerators(comp2); GeneratorRunResult runResult = driver2.GetRunResult().Results[0]; @@ -140,7 +140,7 @@ public async Task ReplacingFileWithNewLibraryImport_DoesNotRegenerateStubsInOthe [Fact] public async Task ChangingMarshallingStrategy_RegeneratesStub() { - string stubSource = CodeSnippets.BasicParametersAndModifiers("CustomType", CodeSnippets.DisableRuntimeMarshalling); + string stubSource = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers("CustomType", CodeSnippets.DisableRuntimeMarshalling)); string customTypeImpl1 = "struct CustomType { System.IntPtr handle; }"; @@ -179,7 +179,7 @@ public async Task ChangingMarshallingStrategy_RegeneratesStub() [Fact] public async Task ChangingMarshallingAttributes_SameStrategy_DoesNotRegenerate() { - string source = CodeSnippets.BasicParametersAndModifiers(); + string source = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers()); SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Preview)); @@ -192,7 +192,7 @@ public async Task ChangingMarshallingAttributes_SameStrategy_DoesNotRegenerate() SyntaxTree newTree = syntaxTree.WithRootAndOptions( SyntaxFactory.ParseCompilationUnit( - CodeSnippets.MarshalAsParametersAndModifiers(System.Runtime.InteropServices.UnmanagedType.I4)), + RemoveTestMarkup(CodeSnippets.MarshalAsParametersAndModifiers(System.Runtime.InteropServices.UnmanagedType.I4))), syntaxTree.Options); Compilation comp2 = comp1.ReplaceSyntaxTree(comp1.SyntaxTrees.First(), newTree); @@ -218,9 +218,9 @@ public async Task ChangingMarshallingAttributes_SameStrategy_DoesNotRegenerate() public static IEnumerable CompilationObjectLivenessSources() { // Basic stub - yield return new[] { CodeSnippets.BasicParametersAndModifiers() }; + yield return new[] { RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers()) }; // Stub with custom string marshaller - yield return new[] { CodeSnippets.CustomStringMarshallingParametersAndModifiers() }; + yield return new[] { RemoveTestMarkup(CodeSnippets.CustomStringMarshallingParametersAndModifiers()) }; } // This test requires precise GC to ensure that we're accurately testing that we aren't @@ -271,5 +271,11 @@ public async Task GeneratorRun_WithNewCompilation_DoesNotKeepOldCompilationAlive return (new WeakReference(comp2), driver2); } } + + private static string RemoveTestMarkup(string sourceWithMarkup) + { + TestFileMarkupParser.GetSpans(sourceWithMarkup, out string sourceWithoutMarkup, out ImmutableArray _); + return sourceWithoutMarkup; + } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj index 1c25ff00a281d..9695eba3d8bda 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj @@ -31,6 +31,8 @@ Link="Verifiers\CSharpAnalyzerVerifier.cs"/> + @@ -39,6 +41,7 @@ + diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs index 5b8769e19c9ee..ea3bcef8860a4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs @@ -8,7 +8,7 @@ using Xunit; using static Microsoft.Interop.Analyzers.NativeMarshallingAttributeAnalyzer; -using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier< +using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpAnalyzerVerifier< Microsoft.Interop.Analyzers.NativeMarshallingAttributeAnalyzer>; namespace LibraryImportGenerator.UnitTests diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ShapeBreakingDiagnosticSuppressorTests.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ShapeBreakingDiagnosticSuppressorTests.cs index bcdbe7cf8be8e..820112ccb93eb 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ShapeBreakingDiagnosticSuppressorTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ShapeBreakingDiagnosticSuppressorTests.cs @@ -8,7 +8,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using LibraryImportGenerator.UnitTests.Verifiers; +using Microsoft.Interop.UnitTests.Verifiers; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing;