Use Roslyn Source Generator Testing SDK to test interop source generators (#84867)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Mon, 8 May 2023 19:03:05 +0000 (12:03 -0700)
committerGitHub <noreply@github.com>
Mon, 8 May 2023 19:03:05 +0000 (12:03 -0700)
39 files changed:
eng/Versions.props
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/GeneratorDiagnostics.cs
src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ManagedToNativeVTableMethodGenerator.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CallingConventionForwarding.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CodeSnippets.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComClassGeneratorOutputShape.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGenerator.Unit.Tests.csproj
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/ComInterfaceGeneratorOutputShape.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/CompileFails.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/Compiles.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/GeneratedComInterfaceAnalyzerTests.cs
src/libraries/System.Runtime.InteropServices/tests/ComInterfaceGenerator.Unit.Tests/IVirtualMethodIndexSignatureProvider.cs
src/libraries/System.Runtime.InteropServices/tests/Common/CustomCollectionMarshallingCodeSnippets.cs
src/libraries/System.Runtime.InteropServices/tests/Common/CustomStructMarshallingCodeSnippets.cs
src/libraries/System.Runtime.InteropServices/tests/Common/TestUtils.cs
src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpAnalyzerVerifier.cs
src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpCodeFixVerifier.cs
src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs [new file with mode: 0644]
src/libraries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpVerifierHelper.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AddDisableRuntimeMarshallingAttributeFixerTests.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AttributeForwarding.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CodeSnippets.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CompileFails.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportAnalyzerTests.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ConvertToLibraryImportFixerTests.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTest.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_AttributeUsage.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulLinearCollectionShapeValidation.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatefulValueShapeValidation.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessLinearCollectionShapeValidation.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/CustomMarshallerAttributeFixerTests_StatelessValueShapeValidation.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Diagnostics.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/IncrementalGenerationTests.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/LibraryImportGenerator.Unit.Tests.csproj
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/NativeMarshallingAttributeAnalyzerTests.cs
src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/ShapeBreakingDiagnosticSuppressorTests.cs

index c97134b..feab8f3 100644 (file)
     <GrpcToolsVersion>2.45.0</GrpcToolsVersion>
     <!-- Uncomment to set a fixed version, else the latest is used -->
     <!-- <SdkVersionForWorkloadTesting>8.0.100-alpha.1.23077.3</SdkVersionForWorkloadTesting> -->
-    <CompilerPlatformTestingVersion>1.1.2-beta1.22403.2</CompilerPlatformTestingVersion>
+    <CompilerPlatformTestingVersion>1.1.2-beta1.23205.1</CompilerPlatformTestingVersion>
     <!-- Docs -->
     <MicrosoftPrivateIntellisenseVersion>7.0.0-preview-20221010.1</MicrosoftPrivateIntellisenseVersion>
     <!-- ILLink -->
index 4ca18b1..77b40b5 100644 (file)
@@ -550,7 +550,7 @@ namespace Microsoft.Interop
                 {
                     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;
                 }
index 5d563b2..9df719d 100644 (file)
@@ -187,7 +187,7 @@ namespace Microsoft.Interop
                 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)),
index 3792231..d6c6564 100644 (file)
@@ -238,7 +238,7 @@ namespace Microsoft.Interop
         {
             List<FunctionPointerParameterSyntax> 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<...>)<untypedFunctionPointerExpression>)
index a5e4481..627d5f8 100644 (file)
@@ -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<Microsoft.Interop.VtableIndexStubGenerator>;
+
 namespace ComInterfaceGenerator.Unit.Tests
 {
     public class CallingConventionForwarding
@@ -31,16 +31,12 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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<Compilation, IMethodSymbol> 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<IMethodSymbol> FindFunctionPointerInvocationSignature(Compilation compilation, string userDefinedInterfaceName, string methodName)
+        class CallingConventionForwardingTest : VerifyCS.Test
         {
-            INamedTypeSymbol? userDefinedInterface = compilation.Assembly.GetTypeByMetadataName(userDefinedInterfaceName);
-            Assert.NotNull(userDefinedInterface);
+            private readonly Action<Compilation, IMethodSymbol> _signatureValidator;
+            private readonly string _interfaceName;
+            private readonly string _methodName;
+
+            public CallingConventionForwardingTest(string interfaceName, string methodName, Action<Compilation, IMethodSymbol> 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>());
+                IMethodSymbol methodImplementation = Assert.Single(generatedInterfaceImplementation.GetMembers($"global::{_interfaceName}.{_methodName}").OfType<IMethodSymbol>());
 
-            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<IFunctionPointerInvocationOperation>()).GetFunctionPointerSignature();
+                return Assert.Single(body.Descendants().OfType<IFunctionPointerInvocationOperation>()).GetFunctionPointerSignature();
+            }
         }
     }
 }
index 459fd32..9a1ab29 100644 (file)
@@ -126,7 +126,7 @@ namespace ComInterfaceGenerator.Unit.Tests
             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 @@ namespace ComInterfaceGenerator.Unit.Tests
                 void MethodA();
             }
             {{GeneratedComInterface}}
-            partial interface IComInterface2 : IComInterface, IOtherComInterface
+            partial interface {|#0:IComInterface2|} : IComInterface, IOtherComInterface
             {
                 void Method2();
             }
index 2bb8d5e..cc3dc85 100644 (file)
@@ -5,9 +5,11 @@ using System;
 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<Microsoft.Interop.ComClassGenerator>;
+
 namespace ComInterfaceGenerator.Unit.Tests
 {
     public class ComClassGeneratorOutputShape
@@ -27,15 +29,8 @@ namespace ComInterfaceGenerator.Unit.Tests
                 [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 @@ namespace ComInterfaceGenerator.Unit.Tests
                 {
                 }
                 """;
-            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<INamedTypeSymbol>(iUnknownDerivedAttribute.AttributeClass).TypeArguments,
-                infoType =>
-                {
-                    Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(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<INamedTypeSymbol>(iUnknownDerivedAttribute.AttributeClass).TypeArguments,
+                    infoType =>
+                    {
+                        Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(infoType).IsFileLocal);
+                    });
+            }
         }
     }
 }
index 64e7dd1..6766f8e 100644 (file)
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
@@ -27,6 +27,8 @@
             Link="Verifiers\CSharpAnalyzerVerifier.cs"/>
     <Compile Include="..\Common\Verifiers\CSharpCodeFixVerifier.cs"
             Link="Verifiers\CSharpCodeFixVerifier.cs"/>
+    <Compile Include="..\Common\Verifiers\CSharpSourceGeneratorVerifier.cs"
+            Link="Verifiers\CSharpSourceGeneratorVerifier.cs"/>
     <Compile Include="..\Common\Verifiers\CSharpVerifierHelper.cs"
             Link="Verifiers\CSharpVerifierHelper.cs"/>
   </ItemGroup>
@@ -35,6 +37,7 @@
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisVersion)" />
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
   </ItemGroup>
 
   <ItemGroup>
index 4b1eafc..d9063c0 100644 (file)
@@ -8,12 +8,16 @@ using System.Linq;
 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<Microsoft.Interop.ComInterfaceGenerator>;
+
 namespace ComInterfaceGenerator.Unit.Tests
 {
     public class ComInterfaceGeneratorOutputShape
@@ -32,15 +36,8 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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 @@ namespace ComInterfaceGenerator.Unit.Tests
                     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<INamedTypeSymbol>(iUnknownDerivedAttribute.AttributeClass).TypeArguments,
-                infoType =>
-                {
-                    Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(infoType).IsFileLocal);
-                },
-                implementationType =>
-                {
-                    Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(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<IMethodSymbol>().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<INamedTypeSymbol>(iUnknownDerivedAttribute.AttributeClass).TypeArguments,
+                    infoType =>
+                    {
+                        Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(infoType).IsFileLocal);
+                    },
+                    implementationType =>
+                    {
+                        Assert.True(Assert.IsAssignableFrom<INamedTypeSymbol>(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<IMethodSymbol>().Where(method => method.IsAbstract && !method.IsStatic),
+                            method =>
+                            {
+                                Assert.NotNull(implementationType.FindImplementationForInterfaceMember(method));
+                            });
+                    });
+            }
         }
     }
 }
index d69ce9e..e4db5b7 100644 (file)
@@ -11,9 +11,16 @@ using System.Runtime.InteropServices;
 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<Microsoft.Interop.ComInterfaceGenerator>;
+using Microsoft.Interop;
+
 namespace ComInterfaceGenerator.Unit.Tests
 {
     public class CompileFails
@@ -27,31 +34,106 @@ namespace ComInterfaceGenerator.Unit.Tests
         {
             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<Diagnostic> 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<Diagnostic> 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<object[]> 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<object[]> 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);
         }
     }
 }
index 4a0dd07..89c1e84 100644 (file)
@@ -11,6 +11,10 @@ using Microsoft.CodeAnalysis;
 using Microsoft.Interop.UnitTests;
 using Xunit;
 
+using VerifyVTableGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.VtableIndexStubGenerator>;
+using VerifyComInterfaceGenerator = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.ComInterfaceGenerator>;
+using Microsoft.CodeAnalysis.Testing;
+
 namespace ComInterfaceGenerator.Unit.Tests
 {
     public class Compiles
@@ -65,6 +69,39 @@ namespace ComInterfaceGenerator.Unit.Tests
             yield return new[] { ID(), codeSnippets.BasicParametersAndModifiersNoImplicitThis<IntPtr>() };
             yield return new[] { ID(), codeSnippets.BasicParametersAndModifiersNoImplicitThis<UIntPtr>() };
 
+            // 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<object[]> 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 @@ namespace ComInterfaceGenerator.Unit.Tests
             yield return new[] { ID(), customStructMarshallingCodeSnippetsManagedToUnmanaged.Stateful.OptionalStackallocParametersAndModifiers };
             yield return new[] { ID(), customStructMarshallingCodeSnippetsManagedToUnmanaged.Stateful.DefaultModeByValueInParameter };
             yield return new[] { ID(), customStructMarshallingCodeSnippetsManagedToUnmanaged.Stateful.DefaultModeReturnValue };
+        }
 
+        public static IEnumerable<object[]> 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 @@ namespace ComInterfaceGenerator.Unit.Tests
             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<object[]> CustomCollections(GeneratorKind generator)
         {
             // Custom collection marshalling
+            CodeSnippets codeSnippets = new(GetAttributeProvider(generator));
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<byte[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<sbyte[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<short[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<ushort[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<int[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<uint[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<long[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<ulong[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<float[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<double[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<IntPtr[]>() };
+            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<UIntPtr[]>() };
+
+            CustomCollectionMarshallingCodeSnippets customCollectionMarshallingCodeSnippetsBidirectional = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator)));
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<byte>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<sbyte>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<short>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<ushort>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<int>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<uint>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<long>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<ulong>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<float>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<double>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<IntPtr>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<UIntPtr>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<byte>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<sbyte>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<short>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<ushort>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<int>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<uint>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<long>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<ulong>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<float>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<double>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<IntPtr>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<UIntPtr>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerReturnValueLength<int>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.NestedMarshallerParametersAndModifiers<int>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.NonBlittableElementParametersAndModifiers };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomElementMarshalling };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<byte>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<sbyte>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<short>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<ushort>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<int>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<uint>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<long>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<ulong>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<float>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<double>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<IntPtr>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<UIntPtr>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<byte>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<sbyte>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<short>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<ushort>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<int>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<uint>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<long>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<ulong>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<float>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<double>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<IntPtr>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<UIntPtr>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerReturnValueLength<int>() };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.NonBlittableElementParametersAndModifiers };
+            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomElementMarshalling };
+        }
+
+        public static IEnumerable<object[]> CustomCollectionsManagedToUnmanaged(GeneratorKind generator)
+        {
             CustomCollectionMarshallingCodeSnippets customCollectionMarshallingCodeSnippetsManagedToUnmanaged = new(new CodeSnippets.ManagedToUnmanaged(GetAttributeProvider(generator)));
             yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.ByValue<byte>() };
             yield return new[] { ID(), customCollectionMarshallingCodeSnippetsManagedToUnmanaged.Stateless.ByValue<sbyte>() };
@@ -233,94 +319,19 @@ namespace ComInterfaceGenerator.Unit.Tests
             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<byte[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<sbyte[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<short[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<ushort[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<int[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<uint[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<long[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<ulong[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<float[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<double[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<IntPtr[]>() };
-            yield return new[] { ID(), codeSnippets.MarshalUsingCollectionCountInfoParametersAndModifiers<UIntPtr[]>() };
-
-            CustomCollectionMarshallingCodeSnippets customCollectionMarshallingCodeSnippetsBidirectional = new(new CodeSnippets.Bidirectional(GetAttributeProvider(generator)));
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<byte>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<sbyte>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<short>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<ushort>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<int>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<uint>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<long>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<ulong>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<float>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<double>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<IntPtr>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.DefaultMarshallerParametersAndModifiers<UIntPtr>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<byte>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<sbyte>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<short>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<ushort>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<int>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<uint>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<long>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<ulong>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<float>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<double>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<IntPtr>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerParametersAndModifiers<UIntPtr>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomMarshallerReturnValueLength<int>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.NestedMarshallerParametersAndModifiers<int>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.NonBlittableElementParametersAndModifiers };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateless.CustomElementMarshalling };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<byte>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<sbyte>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<short>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<ushort>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<int>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<uint>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<long>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<ulong>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<float>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<double>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<IntPtr>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.DefaultMarshallerParametersAndModifiers<UIntPtr>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<byte>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<sbyte>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<short>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<ushort>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<int>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<uint>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<long>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<ulong>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<float>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<double>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<IntPtr>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerParametersAndModifiers<UIntPtr>() };
-            yield return new[] { ID(), customCollectionMarshallingCodeSnippetsBidirectional.Stateful.CustomMarshallerReturnValueLength<int>() };
-            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<object[]> ComInterfaceSnippetsToCompile()
@@ -336,48 +347,8 @@ namespace ComInterfaceGenerator.Unit.Tests
         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<string> allowedDiagnostics = new()
-            {
-                // Duplicate 'using'
-                "CS0105",
-                // Variable assigned to but never read
-                "CS0219"
-            };
-            // There are valid warnings from the generator -- 
-            if (generatorDiags.Length != 0)
-            {
-                List<string> 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<T>'
-                    "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");
     }
 }
index a77166a..1258854 100644 (file)
@@ -7,7 +7,7 @@ using Microsoft.Interop;
 using Microsoft.Interop.Analyzers;
 using Xunit;
 
-using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.GeneratedComInterfaceAttributeAnalyzer>;
+using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.GeneratedComInterfaceAttributeAnalyzer>;
 
 namespace ComInterfaceGenerator.Unit.Tests
 {
index 5d4587a..0474607 100644 (file)
@@ -67,7 +67,7 @@ namespace ComInterfaceGenerator.Unit.Tests
             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 @@ namespace ComInterfaceGenerator.Unit.Tests
             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 @@ namespace ComInterfaceGenerator.Unit.Tests
             partial interface INativeAPI
             {
                 {{AttributeProvider.VirtualMethodIndex(0, ImplicitThisParameter: ImplicitThisParameter, Direction: Direction)}}
-                {{typeName}} Method();
+                {{typeName}} {|#0:Method|}();
             }
             {{AttributeProvider.AdditionalUserRequiredInterfaces("INativeAPI")}}
             """;
index 4144748..f25a503 100644 (file)
@@ -278,7 +278,7 @@ class TestCollection<T> {{}}
 
             public string GenericCollectionMarshallingArityMismatch => _provider.BasicParameterByValue("TestCollection<int>", DisableRuntimeMarshalling)
                 + """
-                [NativeMarshalling(typeof(Marshaller<,,>))]
+                [{|#10:NativeMarshalling(typeof(Marshaller<,,>))|}]
                 class TestCollection<T> {}
 
                 [CustomMarshaller(typeof(TestCollection<>), MarshalMode.Default, typeof(Marshaller<,,>))]
index b3ea192..9b77d5f 100644 (file)
@@ -18,7 +18,7 @@ namespace Microsoft.Interop.UnitTests
         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 @@ namespace Microsoft.Interop.UnitTests
                 + 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()
index 81543e8..4d1ec3a 100644 (file)
@@ -5,8 +5,6 @@ using Microsoft.CodeAnalysis;
 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;
index 2ce99d0..08e29e3 100644 (file)
@@ -8,7 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Testing.XUnit;
 using Microsoft.CodeAnalysis.Diagnostics;
 using Microsoft.CodeAnalysis.Testing;
 
-namespace LibraryImportGenerator.UnitTests.Verifiers
+namespace Microsoft.Interop.UnitTests.Verifiers
 {
     public static class CSharpAnalyzerVerifier<TAnalyzer>
         where TAnalyzer : DiagnosticAnalyzer, new()
index 00f03f1..b63dffc 100644 (file)
@@ -14,9 +14,8 @@ using Microsoft.CodeAnalysis.CSharp.Testing.XUnit;
 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<TAnalyzer, TCodeFix>
         where TAnalyzer : DiagnosticAnalyzer, new()
@@ -120,45 +119,7 @@ namespace LibraryImportGenerator.UnitTests.Verifiers
                 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<string, ReportDiagnostic>();
-                    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<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, CancellationToken cancellationToken)
@@ -187,6 +148,11 @@ namespace LibraryImportGenerator.UnitTests.Verifiers
                             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 (file)
index 0000000..167f000
--- /dev/null
@@ -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<TSourceGenerator>
+        where TSourceGenerator : IIncrementalGenerator, new()
+    {
+        public static DiagnosticResult Diagnostic(string diagnosticId)
+            => new DiagnosticResult(diagnosticId, DiagnosticSeverity.Error);
+
+        public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
+            => new DiagnosticResult(descriptor);
+
+        /// <summary>
+        /// Create a <see cref="DiagnosticResult"/> with the diagnostic message created with the provided arguments.
+        /// A <see cref="DiagnosticResult"/> with the <see cref="DiagnosticResult.Message"/> property set instead of just the <see cref="DiagnosticResult.MessageArguments"/> 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.
+        /// </summary>
+        /// <param name="descriptor">The diagnostic descriptor</param>
+        /// <param name="arguments">The arguments to use to format the diagnostic message</param>
+        /// <returns>A <see cref="DiagnosticResult"/> with a <see cref="DiagnosticResult.Message"/> set with the <paramref name="descriptor"/>'s message format and the <paramref name="arguments"/>.</returns>
+        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<TSourceGenerator, XUnitVerifier>
+        {
+            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<DiagnosticAnalyzer> 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<Diagnostic> generatorDiagnostics)> GetProjectCompilationAsync(Project project, IVerifier verifier, CancellationToken cancellationToken)
+            {
+                var (compilation, diagnostics) = await base.GetProjectCompilationAsync(project, verifier, cancellationToken);
+                VerifyFinalCompilation(compilation);
+                return (compilation, diagnostics);
+            }
+
+            /// <summary>
+            /// Verify any expected invariants on the final compilation after the source generators have been applied.
+            /// </summary>
+            /// <param name="compilation">The compilation.</param>
+            /// <remarks>
+            /// 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.
+            /// </remarks>
+            protected virtual void VerifyFinalCompilation(Compilation compilation)
+            {
+            }
+        }
+    }
+}
index 3d057d8..8df1cbb 100644 (file)
@@ -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 @@ namespace LibraryImportGenerator.UnitTests.Verifiers
             var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory);
             return commandLineArguments.CompilationOptions.SpecificDiagnosticOptions;
         }
+
+        internal static Func<Solution, ProjectId, Solution> GetAllDiagonsticsEnabledTransform(IEnumerable<DiagnosticAnalyzer> 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<string, ReportDiagnostic>();
+                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<Solution, ProjectId, Solution> 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");
+            };
+        }
     }
 }
index 8ae6c15..c93cd35 100644 (file)
@@ -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 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
 
                 struct Native
                 {
+                    public bool b;
                 }
 
                 [CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
@@ -60,7 +52,9 @@ namespace LibraryImportGenerator.UnitTests
                 """;
             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 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
 
                 struct Native
                 {
+                    public bool b;
                 }
 
                 [CustomMarshaller(typeof(S), MarshalMode.Default, typeof(Marshaller))]
@@ -106,19 +101,21 @@ namespace LibraryImportGenerator.UnitTests
 
                 """;
 
-            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 @@ namespace LibraryImportGenerator.UnitTests
 
         class Test : VerifyCS.Test
         {
+            private static readonly ImmutableArray<Type> 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<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(AddDisableRuntimeMarshallingAttributeRule);
-
-            public override void Initialize(AnalysisContext context)
+            protected override IEnumerable<Type> 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<string, string>.Empty
-                                .Add(
-                                    GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute,
-                                    GeneratorDiagnosticProperties.AddDisableRuntimeMarshallingAttribute),
-                            symbol.Type.ToDisplayString(), symbol.Name));
-                    }
-                }, SymbolKind.Parameter);
+                return GeneratorTypes;
             }
         }
     }
index 484901a..08d7c0e 100644 (file)
@@ -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<Microsoft.Interop.LibraryImportGenerator>;
+
 namespace LibraryImportGenerator.UnitTests
 {
     public class AdditionalAttributesOnStub
@@ -46,13 +47,7 @@ namespace LibraryImportGenerator.UnitTests
                     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<IMethodSymbol>().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 @@ namespace LibraryImportGenerator.UnitTests
                     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<IMethodSymbol>().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 @@ namespace LibraryImportGenerator.UnitTests
                     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<IMethodSymbol>().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 @@ namespace LibraryImportGenerator.UnitTests
                     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<IMethodSymbol>().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<object[]> GetDownlevelTargetFrameworks()
@@ -159,20 +136,7 @@ namespace LibraryImportGenerator.UnitTests
                     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<IMethodSymbol>().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 @@ namespace LibraryImportGenerator.UnitTests
                     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<IMethodSymbol>().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 @@ namespace LibraryImportGenerator.UnitTests
                     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<IMethodSymbol>().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 @@ namespace LibraryImportGenerator.UnitTests
                     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<IMethodSymbol>().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<IMethodSymbol>().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);
+                }
+            }
         }
     }
 }
index 2f055e1..1a5322b 100644 (file)
@@ -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<Microsoft.Interop.LibraryImportGenerator>;
 
 namespace LibraryImportGenerator.UnitTests
 {
@@ -53,19 +52,18 @@ namespace LibraryImportGenerator.UnitTests
                     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 @@ namespace LibraryImportGenerator.UnitTests
                     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 @@ namespace LibraryImportGenerator.UnitTests
                     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 @@ namespace LibraryImportGenerator.UnitTests
                     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 @@ namespace LibraryImportGenerator.UnitTests
                     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 @@ namespace LibraryImportGenerator.UnitTests
                     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 @@ namespace LibraryImportGenerator.UnitTests
                 {
                     [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 @@ namespace LibraryImportGenerator.UnitTests
                     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<IMethodSymbol, Compilation> 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<LocalFunctionStatementSyntax>()
-                .ToList();
-            LocalFunctionStatementSyntax innerDllImport = Assert.Single(localFunctions);
-            IMethodSymbol targetMethod = (IMethodSymbol)model.GetDeclaredSymbol(innerDllImport)!;
-            return targetMethod;
+            private readonly Action<IMethodSymbol, Compilation> _targetPInvokeAssertion;
+
+            public GeneratedTargetPInvokeTest(Action<IMethodSymbol, Compilation> 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<LocalFunctionStatementSyntax>()
+                    .ToList();
+                LocalFunctionStatementSyntax innerDllImport = Assert.Single(localFunctions);
+                IMethodSymbol targetMethod = (IMethodSymbol)model.GetDeclaredSymbol(innerDllImport)!;
+                return targetMethod;
+            }
+
+            protected override void VerifyFinalCompilation(Compilation compilation)
+            {
+                _targetPInvokeAssertion(GetGeneratedPInvokeTargetFromCompilation(compilation), compilation);
+            }
         }
     }
 }
index 1523ac5..0eb9830 100644 (file)
@@ -72,7 +72,7 @@ namespace LibraryImportGenerator.UnitTests
         /// </summary>
         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 @@ namespace LibraryImportGenerator.UnitTests
             using System.Runtime.InteropServices;
             partial class Test
             {
-                [LCIDConversion(0)]
+                [{|#0:LCIDConversion(0)|}]
                 [LibraryImport("DoesNotExist")]
                 public static partial void Method();
             }
@@ -335,12 +335,12 @@ namespace LibraryImportGenerator.UnitTests
             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 @@ namespace LibraryImportGenerator.UnitTests
             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 @@ namespace LibraryImportGenerator.UnitTests
             {{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 @@ namespace LibraryImportGenerator.UnitTests
             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 @@ namespace LibraryImportGenerator.UnitTests
             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 @@ namespace LibraryImportGenerator.UnitTests
             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 @@ namespace LibraryImportGenerator.UnitTests
             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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [LibraryImport("DoesNotExist")]
                 public static partial void Method(
-                    [{{attributeName}}] {{typeName}} p);
+                    [{{attributeName}}] {{typeName}} {|#0:p|});
             }
             """;
 
@@ -523,12 +523,12 @@ namespace LibraryImportGenerator.UnitTests
             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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [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 @@ namespace LibraryImportGenerator.UnitTests
         /// <summary>
         /// Declaration with parameters with MarshalAs.
         /// </summary>
+        //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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [LibraryImport("DoesNotExist")]
                 public static partial void Method(
-                    {{byRefKind}} {{typeName}} p);
+                    {{byRefKind}} {{typeName}} {|#0:p|});
             }
             """;
 
@@ -710,7 +725,7 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [LibraryImport("DoesNotExist")]
                 public static partial void Method(
-                    {{typeName}} p);
+                    {{typeName}} {|#0:p|});
             }
             """;
 
@@ -721,7 +736,7 @@ namespace LibraryImportGenerator.UnitTests
             partial class Test
             {
                 [LibraryImport("DoesNotExist")]
-                public static partial {{typeName}} Method();
+                public static partial {{typeName}} {|#0:Method|}();
             }
             """;
 
@@ -832,20 +847,20 @@ namespace LibraryImportGenerator.UnitTests
         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 @@ namespace LibraryImportGenerator.UnitTests
                 [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 @@ namespace LibraryImportGenerator.UnitTests
                 [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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [LibraryImport("DoesNotExist")]
                 public static partial void Method(
-                    [MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 1)] [MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 1)] TestCollection<int> p);
+                    [MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 1)] [{|#0:MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 1)|}] TestCollection<int> p);
             }
             """
                     + CustomCollectionMarshallingCodeSnippets.TestCollection()
@@ -1026,7 +1041,7 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [LibraryImport("DoesNotExist")]
                 public static partial void Method(
-                    [MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 2)] TestCollection<int> p);
+                    [{|#0:MarshalUsing(typeof(CustomIntMarshaller), ElementIndirectionDepth = 2)|}] TestCollection<int> p);
             }
             """
             + CustomCollectionMarshallingCodeSnippets.TestCollection()
@@ -1040,8 +1055,8 @@ namespace LibraryImportGenerator.UnitTests
             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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [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 @@ namespace LibraryImportGenerator.UnitTests
             {
                 [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 @@ namespace LibraryImportGenerator.UnitTests
             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 @@ namespace LibraryImportGenerator.UnitTests
 
             partial struct Basic
             {
-                [LibraryImport("DoesNotExist", SetLa)]
+                [{|CS1729:LibraryImport("DoesNotExist", {|CS0103:SetLa|})|}]
                 public static partial void Method();
             }
             """;
@@ -1215,7 +1230,7 @@ namespace LibraryImportGenerator.UnitTests
 
             partial struct Basic
             {
-                [LibraryImport(DoesNotExist)]
+                [LibraryImport({|CS0103:DoesNotExist|})]
                 public static partial void Method();
             }
             """;
@@ -1224,7 +1239,7 @@ namespace LibraryImportGenerator.UnitTests
 
             partial struct Basic
             {
-                [LibraryImport("DoesNotExist", SetLastError = "Foo")]
+                [LibraryImport("DoesNotExist", SetLastError = {|CS0029:"Foo"|})]
                 public static partial void Method();
             }
             """;
index 4a948f5..0f0cb60 100644 (file)
@@ -11,9 +11,15 @@ using System.Runtime.InteropServices;
 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<Microsoft.Interop.LibraryImportGenerator>;
+
 namespace LibraryImportGenerator.UnitTests
 {
     public class CompileFails
@@ -26,213 +32,930 @@ namespace LibraryImportGenerator.UnitTests
         public static IEnumerable<object[]> CodeSnippetsToCompile()
         {
             // Not LibraryImportAttribute
-            yield return new object[] { ID(), CodeSnippets.UserDefinedPrefixedAttributes, 0, 3 };
+            yield return new object[] { ID(), CodeSnippets.UserDefinedPrefixedAttributes, Array.Empty<DiagnosticResult>() };
 
             // No explicit marshalling for char or string
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<char>(), 5, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<string>(), 5, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers<char>(), 5, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers<string>(), 5, 0 };
+            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<char>(), 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<string>(), 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<char>(), 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<string>(), 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<bool>(), 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<bool>(), 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<bool>(), 5, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParametersAndModifiers<bool>(), 5, 0 };
 
             // Unsupported StringMarshalling configuration
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling<char>(StringMarshalling.Utf8), 5, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling<char>(StringMarshalling.Custom), 6, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling<string>(StringMarshalling.Custom), 6, 0 };
-            yield return new object[] { ID(), CodeSnippets.CustomStringMarshallingParametersAndModifiers<char>(), 5, 0 };
+            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiersWithStringMarshalling<char>(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<char>(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<string>(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<char>(), 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<char>(UnmanagedType.I1), 5, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalAsParametersAndModifiers<char>(UnmanagedType.U1), 5, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalAsParametersAndModifiers<int[]>(UnmanagedType.SafeArray), 10, 0 };
+            yield return new object[] { ID(), CodeSnippets.MarshalAsParametersAndModifiers<char>(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<char>(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<int[]>(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<int[]>("Out"), 1, 0 };
-            yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier<int[]>("In, Out"), 1, 0 };
+            yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier<int[]>("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<int[]>("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<byte>("In"), 1, 0 };
-            yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier<byte>("Out"), 1, 0 };
-            yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier<byte>("In, Out"), 1, 0 };
+            yield return new object[] { ID(), CodeSnippets.ByValueParameterWithModifier<byte>("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<byte>("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<byte>("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<byte[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<sbyte[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<short[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<ushort[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<char[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<string[]>(CodeSnippets.DisableRuntimeMarshalling), 5, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<int[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<uint[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<long[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<ulong[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<float[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<double[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<bool[]>(CodeSnippets.DisableRuntimeMarshalling), 5, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<IntPtr[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
-            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<UIntPtr[]>(CodeSnippets.DisableRuntimeMarshalling), 3, 0 };
+            yield return new object[] { ID(), CodeSnippets.BasicParametersAndModifiers<byte[]>(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<sbyte[]>(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<short[]>(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<ushort[]>(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<char[]>(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<string[]>(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<int[]>(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<uint[]>(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<long[]>(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<ulong[]>(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<float[]>(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<double[]>(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<bool[]>(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<IntPtr[]>(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<UIntPtr[]>(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<float>(isByRef: false), 1, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam<double>(isByRef: false), 1, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam<bool>(isByRef: false), 2, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalUsingArrayParameterWithSizeParam<float>(isByRef: false), 1, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalUsingArrayParameterWithSizeParam<double>(isByRef: false), 1, 0 };
-            yield return new object[] { ID(), CodeSnippets.MarshalUsingArrayParameterWithSizeParam<bool>(isByRef: false), 2, 0 };
+            yield return new object[] { ID(), CodeSnippets.MarshalAsArrayParameterWithSizeParam<float>(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<double>(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<bool>(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<float>(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<double>(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<bool>(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<global::System.Runtime.InteropServices.SafeHandle>' 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<global::Microsoft.Win32.SafeHandles.SafeFileHandle>' 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<global::MySafeHandle>' 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<global::MySafeHandle>' 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<global::MySafeHandle>' 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<int>", "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<Diagnostic> 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<Diagnostic> 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<object[]> 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);
         }
     }
 }
index 9eb21ba..6a932ac 100644 (file)
@@ -14,6 +14,13 @@ using System.Threading.Tasks;
 using Xunit;
 using SourceGenerators.Tests;
 
+using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpSourceGeneratorVerifier<Microsoft.Interop.LibraryImportGenerator>;
+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 @@ namespace LibraryImportGenerator.UnitTests
         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<object[]> CodeSnippetsToCompileWithPreprocessorSymbols()
@@ -442,23 +444,37 @@ namespace LibraryImportGenerator.UnitTests
         public async Task ValidateSnippetsWithPreprocessorDefinitions(string id, string source, IEnumerable<string> 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<string> _preprocessorSymbols;
+
+            public PreprocessorTest(IEnumerable<string> preprocessorSymbols)
+                :base(referenceAncillaryInterop: false)
+            {
+                _preprocessorSymbols = preprocessorSymbols;
+            }
+
+            protected override ParseOptions CreateParseOptions()
+                => ((CSharpParseOptions)base.CreateParseOptions()).WithPreprocessorSymbols(_preprocessorSymbols);
         }
 
         public static IEnumerable<object[]> 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<byte>(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 @@ namespace LibraryImportGenerator.UnitTests
         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<MethodDeclarationSyntax>()
-                .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<MethodDeclarationSyntax>()
+                    .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<object[]> FullyBlittableSnippetsToCompile()
@@ -536,26 +559,32 @@ namespace LibraryImportGenerator.UnitTests
         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<MethodDeclarationSyntax>()
-                .ToList();
+            protected override void VerifyFinalCompilation(Compilation compilation)
+            {
+                SyntaxTree generatedCode = compilation.SyntaxTrees.Last();
+                SemanticModel model = compilation.GetSemanticModel(generatedCode);
+                var methods = generatedCode.GetRoot()
+                    .DescendantNodes().OfType<MethodDeclarationSyntax>()
+                    .ToList();
 
-            Assert.All(methods, method => Assert.NotNull(model.GetDeclaredSymbol(method)!.GetDllImportData()));
+                Assert.All(methods, method => Assert.NotNull(model.GetDeclaredSymbol(method)!.GetDllImportData()));
+            }
         }
 
         public static IEnumerable<object[]> SnippetsWithBlittableTypesButNonBlittableDataToCompile()
@@ -570,29 +599,34 @@ namespace LibraryImportGenerator.UnitTests
         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<MethodDeclarationSyntax>()
-                .Count();
-            int numInnerDllImports = generatedCode.GetRoot()
-                .DescendantNodes().OfType<LocalFunctionStatementSyntax>()
-                .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<MethodDeclarationSyntax>()
+                    .Count();
+                int numInnerDllImports = generatedCode.GetRoot()
+                    .DescendantNodes().OfType<LocalFunctionStatementSyntax>()
+                    .Count();
+                Assert.Equal(numStubMethods, numInnerDllImports);
+            }
         }
 
         public static IEnumerable<object[]> CodeSnippetsToCompileWithMarshalType()
@@ -609,18 +643,21 @@ namespace LibraryImportGenerator.UnitTests
         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<object[]> CodeSnippetsToCompileMultipleSources()
@@ -635,13 +672,16 @@ namespace LibraryImportGenerator.UnitTests
         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<TextSpan> _);
+            return sourceWithoutMarkup;
         }
 
         public static IEnumerable<object[]> CodeSnippetsToVerifyNoTreesProduced()
@@ -660,14 +700,29 @@ namespace LibraryImportGenerator.UnitTests
         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<Diagnostic> 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);
+            }
         }
     }
 }
index 580683b..d2bd400 100644 (file)
@@ -10,7 +10,7 @@ using Xunit;
 
 using static Microsoft.Interop.Analyzers.ConvertToLibraryImportAnalyzer;
 
-using VerifyCS = LibraryImportGenerator.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.ConvertToLibraryImportAnalyzer>;
+using VerifyCS = Microsoft.Interop.UnitTests.Verifiers.CSharpAnalyzerVerifier<Microsoft.Interop.Analyzers.ConvertToLibraryImportAnalyzer>;
 
 namespace LibraryImportGenerator.UnitTests
 {
index e930c5d..14bb0e4 100644 (file)
@@ -9,9 +9,8 @@ using Microsoft.CodeAnalysis;
 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>;
 
index c56b8aa..2380a0b 100644 (file)
@@ -8,7 +8,7 @@ using System.Linq;
 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;
index c846550..03775e7 100644 (file)
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 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>;
 
index 62d599f..c2fc65e 100644 (file)
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
 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>;
 
index 6cf2b96..b8dbae8 100644 (file)
@@ -10,7 +10,7 @@ using System.Threading.Tasks;
 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>;
 
index 28149a0..e4b4b63 100644 (file)
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
 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>;
 
index b855690..6fcc90d 100644 (file)
@@ -9,7 +9,7 @@ using System.Threading.Tasks;
 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>;
 
index 63119dd..030ffba 100644 (file)
@@ -11,10 +11,10 @@ using Microsoft.CodeAnalysis;
 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<Microsoft.Interop.LibraryImportGenerator>;
 
 namespace LibraryImportGenerator.UnitTests
 {
@@ -34,29 +34,20 @@ namespace LibraryImportGenerator.UnitTests
                 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<int> list);
+                    public static partial void Method2(int i, List<int> {|#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<int>", "list"),
-            };
-            VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags));
-
-            var newCompDiags = newComp.GetDiagnostics();
-            Assert.Empty(newCompDiags);
+                VerifyCS.Diagnostic(GeneratorDiagnostics.ParameterTypeNotSupported)
+                    .WithLocation(1)
+                    .WithArguments("System.Collections.Generic.List<int>", "list"));
         }
 
         [Fact]
@@ -73,29 +64,20 @@ namespace LibraryImportGenerator.UnitTests
                 partial class Test
                 {
                     [LibraryImport("DoesNotExist")]
-                    public static partial NS.MyClass Method1();
+                    public static partial NS.MyClass {|#0:Method1|}();
 
                     [LibraryImport("DoesNotExist")]
-                    public static partial List<int> Method2();
+                    public static partial List<int> {|#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<int>", "Method2"),
-            };
-            VerifyDiagnostics(expectedDiags, GetSortedDiagnostics(generatorDiags));
-
-            var newCompDiags = newComp.GetDiagnostics();
-            Assert.Empty(newCompDiags);
+                VerifyCS.Diagnostic(GeneratorDiagnostics.ReturnTypeNotSupported)
+                    .WithLocation(1)
+                    .WithArguments("System.Collections.Generic.List<int>", "Method2"));
         }
 
         [Fact]
@@ -107,24 +89,17 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
                 {
                     [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 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
                     }
                 }
                 """ + 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 @@ namespace LibraryImportGenerator.UnitTests
                 {{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 @@ namespace LibraryImportGenerator.UnitTests
                 }
                 """;
 
-            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 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
                 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 @@ namespace LibraryImportGenerator.UnitTests
                 partial class Test
                 {
                     [LibraryImport("DoesNotExist")]
-                    public static partial void Method1<T>();
+                    public static partial void {|#0:Method1|}<T>();
 
                     [LibraryImport("DoesNotExist")]
-                    public static partial void Method2<T, U>();
+                    public static partial void {|#1:Method2|}<T, U>();
                 }
                 """;
-            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 @@ namespace LibraryImportGenerator.UnitTests
                 {{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 @@ namespace LibraryImportGenerator.UnitTests
                     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)
index 27df3c9..78ed48e 100644 (file)
@@ -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 @@ namespace LibraryImportGenerator.UnitTests
     {
         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<int>();
+            string source = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>());
 
             Compilation comp1 = await TestUtils.CreateCompilation(source);
 
@@ -52,7 +52,7 @@ namespace LibraryImportGenerator.UnitTests
             string source = $$"""
                 namespace NS
                 {
-                    {{CodeSnippets.BasicParametersAndModifiers<int>()}}
+                    {{RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>())}}
                 }
                 """;
 
@@ -83,7 +83,7 @@ namespace LibraryImportGenerator.UnitTests
         [Fact]
         public async Task AddingFileWithNewLibraryImport_DoesNotRegenerateOriginalMethod()
         {
-            string source = CodeSnippets.BasicParametersAndModifiers<int>();
+            string source = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>());
 
             Compilation comp1 = await TestUtils.CreateCompilation(source);
 
@@ -92,7 +92,7 @@ namespace LibraryImportGenerator.UnitTests
 
             driver = driver.RunGenerators(comp1);
 
-            Compilation comp2 = comp1.AddSyntaxTrees(CSharpSyntaxTree.ParseText(CodeSnippets.MarshalAsParametersAndModifiers<bool>(UnmanagedType.I1), new CSharpParseOptions(LanguageVersion.Preview)));
+            Compilation comp2 = comp1.AddSyntaxTrees(CSharpSyntaxTree.ParseText(RemoveTestMarkup(CodeSnippets.MarshalAsParametersAndModifiers<bool>(UnmanagedType.I1)), new CSharpParseOptions(LanguageVersion.Preview)));
 
             GeneratorDriver driver2 = driver.RunGenerators(comp2);
             GeneratorRunResult runResult = driver2.GetRunResult().Results[0];
@@ -113,14 +113,14 @@ namespace LibraryImportGenerator.UnitTests
         [Fact]
         public async Task ReplacingFileWithNewLibraryImport_DoesNotRegenerateStubsInOtherFiles()
         {
-            Compilation comp1 = await TestUtils.CreateCompilation(new string[] { CodeSnippets.BasicParametersAndModifiers<int>(), CodeSnippets.MarshalAsParametersAndModifiers<bool>(UnmanagedType.I1) });
+            Compilation comp1 = await TestUtils.CreateCompilation(new string[] { RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>()), RemoveTestMarkup(CodeSnippets.MarshalAsParametersAndModifiers<bool>(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<ulong>(), new CSharpParseOptions(LanguageVersion.Preview)));
+            Compilation comp2 = comp1.ReplaceSyntaxTree(comp1.SyntaxTrees.First(), CSharpSyntaxTree.ParseText(RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<ulong>()), new CSharpParseOptions(LanguageVersion.Preview)));
             GeneratorDriver driver2 = driver.RunGenerators(comp2);
             GeneratorRunResult runResult = driver2.GetRunResult().Results[0];
 
@@ -140,7 +140,7 @@ namespace LibraryImportGenerator.UnitTests
         [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 @@ namespace LibraryImportGenerator.UnitTests
         [Fact]
         public async Task ChangingMarshallingAttributes_SameStrategy_DoesNotRegenerate()
         {
-            string source = CodeSnippets.BasicParametersAndModifiers<int>();
+            string source = RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>());
 
             SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Preview));
 
@@ -192,7 +192,7 @@ namespace LibraryImportGenerator.UnitTests
 
             SyntaxTree newTree = syntaxTree.WithRootAndOptions(
                 SyntaxFactory.ParseCompilationUnit(
-                    CodeSnippets.MarshalAsParametersAndModifiers<int>(System.Runtime.InteropServices.UnmanagedType.I4)),
+                    RemoveTestMarkup(CodeSnippets.MarshalAsParametersAndModifiers<int>(System.Runtime.InteropServices.UnmanagedType.I4))),
                 syntaxTree.Options);
 
             Compilation comp2 = comp1.ReplaceSyntaxTree(comp1.SyntaxTrees.First(), newTree);
@@ -218,9 +218,9 @@ namespace LibraryImportGenerator.UnitTests
         public static IEnumerable<object[]> CompilationObjectLivenessSources()
         {
             // Basic stub
-            yield return new[] { CodeSnippets.BasicParametersAndModifiers<int>() };
+            yield return new[] { RemoveTestMarkup(CodeSnippets.BasicParametersAndModifiers<int>()) };
             // Stub with custom string marshaller
-            yield return new[] { CodeSnippets.CustomStringMarshallingParametersAndModifiers<string>() };
+            yield return new[] { RemoveTestMarkup(CodeSnippets.CustomStringMarshallingParametersAndModifiers<string>()) };
         }
 
         // This test requires precise GC to ensure that we're accurately testing that we aren't
@@ -271,5 +271,11 @@ namespace LibraryImportGenerator.UnitTests
                 return (new WeakReference(comp2), driver2);
             }
         }
+
+        private static string RemoveTestMarkup(string sourceWithMarkup)
+        {
+            TestFileMarkupParser.GetSpans(sourceWithMarkup, out string sourceWithoutMarkup, out ImmutableArray<TextSpan> _);
+            return sourceWithoutMarkup;
+        }
     }
 }
index 1c25ff0..9695eba 100644 (file)
@@ -31,6 +31,8 @@
             Link="Verifiers\CSharpAnalyzerVerifier.cs"/>
     <Compile Include="..\Common\Verifiers\CSharpCodeFixVerifier.cs"
             Link="Verifiers\CSharpCodeFixVerifier.cs"/>
+    <Compile Include="..\Common\Verifiers\CSharpSourceGeneratorVerifier.cs"
+            Link="Verifiers\CSharpSourceGeneratorVerifier.cs"/>
     <Compile Include="..\Common\Verifiers\CSharpVerifierHelper.cs"
             Link="Verifiers\CSharpVerifierHelper.cs"/>
   </ItemGroup>
@@ -39,6 +41,7 @@
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisVersion)" />
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="$(CompilerPlatformTestingVersion)" />
   </ItemGroup>
 
   <ItemGroup>
index 5b8769e..ea3bcef 100644 (file)
@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 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
index bcdbe7c..820112c 100644 (file)
@@ -8,7 +8,7 @@ using System.Linq;
 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;