public async Task SkipLocalsInitOnDownlevelTargetFrameworks(TestTargetFramework targetFramework, bool expectSkipLocalsInit) { string source = $@" using System.Runtime.InteropServices; {CodeSnippets.LibraryImportAttributeDeclaration} partial class C {{ [LibraryImportAttribute(""DoesNotExist"")] [return: MarshalAs(UnmanagedType.Bool)] public static partial bool Method(); }}"; Compilation comp = await TestUtils.CreateCompilation(source, targetFramework); 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"); 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); } }
public async Task ValidateSnippetsFallbackForwarder(string source, TestTargetFramework targetFramework, bool expectFallbackForwarder) { Compilation comp = await TestUtils.CreateCompilation(source, targetFramework); TestUtils.AssertPreSourceGeneratorCompilation(comp); var newComp = TestUtils.RunGenerators( comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); Assert.Empty(generatorDiags); TestUtils.AssertPostSourceGeneratorCompilation(newComp); // 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); 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); }
/// <summary> /// Create a compilation given sources /// </summary> /// <param name="sources">Sources to compile</param> /// <param name="targetFramework">Target framework of the compilation</param> /// <param name="outputKind">Output type</param> /// <returns>The resulting compilation</returns> public static Task <Compilation> CreateCompilation(string[] sources, TestTargetFramework targetFramework = TestTargetFramework.Net, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary, IEnumerable <string>?preprocessorSymbols = null) { return(CreateCompilation( sources.Select(source => CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Preview, preprocessorSymbols: preprocessorSymbols))).ToArray(), targetFramework, outputKind)); }
public async Task SkipLocalsInitOnDownlevelTargetFrameworks(TestTargetFramework targetFramework, bool expectSkipLocalsInit) { string source = $@" using System.Runtime.InteropServices; {CodeSnippets.GeneratedDllImportAttributeDeclaration} namespace System.Runtime.InteropServices {{ sealed class NativeMarshallingAttribute : System.Attribute {{ public NativeMarshallingAttribute(System.Type nativeType) {{ }} }} }} partial class C {{ [GeneratedDllImportAttribute(""DoesNotExist"")] public static partial S Method(); }} [NativeMarshalling(typeof(Native))] struct S {{ }} struct Native {{ public Native(S s) {{ }} public S ToManaged() {{ return default; }} }}"; Compilation comp = await TestUtils.CreateCompilation(source, targetFramework); Compilation newComp = TestUtils.RunGenerators(comp, out _, new Microsoft.Interop.DllImportGenerator()); 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); } }
public async Task TargetFrameworkNotSupported_NoLibraryImport_NoDiagnostic(TestTargetFramework targetFramework) { string source = @" using System.Runtime.InteropServices; partial class Test { [DllImport(""DoesNotExist"")] public static extern void Method(); } "; Compilation comp = await TestUtils.CreateCompilation(source, targetFramework); TestUtils.AssertPreSourceGeneratorCompilation(comp); var newComp = TestUtils.RunGenerators(comp, out var generatorDiags, new Microsoft.Interop.LibraryImportGenerator()); Assert.Empty(generatorDiags); var newCompDiags = newComp.GetDiagnostics(); Assert.Empty(newCompDiags); }
/// <summary> /// Get the reference assembly collection for the <see cref="TestTargetFramework"/>. /// </summary> /// <param name="targetFramework">The target framework.</param> /// <returns>The reference assembly collection and metadata references</returns> private static async Task <ImmutableArray <MetadataReference> > GetReferenceAssemblies(TestTargetFramework targetFramework = TestTargetFramework.Net) { // Compute the reference assemblies for the target framework. if (targetFramework == TestTargetFramework.Net) { return(SourceGenerators.Tests.LiveReferencePack.GetMetadataReferences()); } else { var referenceAssembliesSdk = targetFramework switch { TestTargetFramework.Framework => ReferenceAssemblies.NetFramework.Net48.Default, TestTargetFramework.Standard => ReferenceAssemblies.NetStandard.NetStandard21, TestTargetFramework.Core => ReferenceAssemblies.NetCore.NetCoreApp31, TestTargetFramework.Net5 => ReferenceAssemblies.Net.Net50, TestTargetFramework.Net6 => ReferenceAssemblies.Net.Net60, _ => ReferenceAssemblies.Default }; // Update the reference assemblies to include details from the NuGet.config. var referenceAssemblies = referenceAssembliesSdk .WithNuGetConfigFilePath(Path.Combine(Path.GetDirectoryName(typeof(TestUtils).Assembly.Location) !, "NuGet.config")); return(await ResolveReferenceAssemblies(referenceAssemblies)); } }
/// <summary> /// Create a compilation given sources /// </summary> /// <param name="sources">Sources to compile</param> /// <param name="targetFramework">Target framework of the compilation</param> /// <param name="outputKind">Output type</param> /// <param name="refs">Addtional metadata references</param> /// <returns>The resulting compilation</returns> public static async Task <Compilation> CreateCompilation(SyntaxTree[] sources, TestTargetFramework targetFramework = TestTargetFramework.Net, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary, IEnumerable <MetadataReference>?refs = null) { var referenceAssemblies = await GetReferenceAssemblies(targetFramework); // [TODO] Can remove once ancillary logic is removed. if (targetFramework is TestTargetFramework.Net) { referenceAssemblies = referenceAssemblies.Add(GetAncillaryReference()); } if (refs is not null) { referenceAssemblies = referenceAssemblies.AddRange(refs); } return(CSharpCompilation.Create("compilation", sources, referenceAssemblies, new CSharpCompilationOptions(outputKind, allowUnsafe: true, specificDiagnosticOptions: BindingRedirectWarnings))); }
/// <summary> /// Create a compilation given source /// </summary> /// <param name="source">Source to compile</param> /// <param name="targetFramework">Target framework of the compilation</param> /// <param name="outputKind">Output type</param> /// <param name="refs">Addtional metadata references</param> /// <param name="preprocessorSymbols">Prepocessor symbols</param> /// <returns>The resulting compilation</returns> public static Task <Compilation> CreateCompilation(string source, TestTargetFramework targetFramework = TestTargetFramework.Net, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary, IEnumerable <MetadataReference>?refs = null, IEnumerable <string>?preprocessorSymbols = null) { return(CreateCompilation(new[] { source }, targetFramework, outputKind, refs, preprocessorSymbols)); }
/// <summary> /// Create a compilation given source /// </summary> /// <param name="source">Source to compile</param> /// <param name="outputKind">Output type</param> /// <param name="allowUnsafe">Whether or not use of the unsafe keyword should be allowed</param> /// <returns>The resulting compilation</returns> public static Task <Compilation> CreateCompilation(string source, TestTargetFramework targetFramework = TestTargetFramework.Net, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary, bool allowUnsafe = true, IEnumerable <string>?preprocessorSymbols = null) { return(CreateCompilation(new[] { source }, targetFramework, outputKind, allowUnsafe, preprocessorSymbols)); }
/// <summary> /// Get the reference assembly collection for the <see cref="TestTargetFramework"/>. /// </summary> /// <param name="targetFramework">The target framework.</param> /// <returns>The reference assembly collection and metadata references</returns> public static (ReferenceAssemblies, MetadataReference) GetReferenceAssemblies(TestTargetFramework targetFramework = TestTargetFramework.Net) { // Compute the reference assemblies for the target framework. var referenceAssembliesSdk = targetFramework switch { TestTargetFramework.Framework => ReferenceAssemblies.NetFramework.Net48.Default, TestTargetFramework.Standard => ReferenceAssemblies.NetStandard.NetStandard21, TestTargetFramework.Core => ReferenceAssemblies.NetCore.NetCoreApp31, TestTargetFramework.Net => ReferenceAssemblies.Net.Net60, TestTargetFramework.Net5 => ReferenceAssemblies.Net.Net50, TestTargetFramework.Net6 => ReferenceAssemblies.Net.Net60, _ => ReferenceAssemblies.Default }; // Update the reference assemblies to include details from the NuGet.config. var referenceAssemblies = referenceAssembliesSdk .WithNuGetConfigFilePath(Path.Combine(Path.GetDirectoryName(typeof(TestUtils).Assembly.Location) !, "NuGet.config")); // Include the assembly containing the new attribute and all of its references. // [TODO] Remove once the attribute has been added to the BCL var attrAssem = typeof(GeneratedDllImportAttribute).GetTypeInfo().Assembly; return(referenceAssemblies, MetadataReference.CreateFromFile(attrAssem.Location)); }
/// <summary> /// Create a compilation given sources /// </summary> /// <param name="sources">Sources to compile</param> /// <param name="outputKind">Output type</param> /// <param name="allowUnsafe">Whether or not use of the unsafe keyword should be allowed</param> /// <returns>The resulting compilation</returns> public static async Task <Compilation> CreateCompilation(SyntaxTree[] sources, TestTargetFramework targetFramework = TestTargetFramework.Net, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary, bool allowUnsafe = true, IEnumerable <string>?preprocessorSymbols = null) { var(mdRefs, ancillary) = GetReferenceAssemblies(targetFramework); var referenceAssemblies = await ResolveReferenceAssemblies(mdRefs); // [TODO] Can remove once ancillary logic is removed. if (targetFramework is TestTargetFramework.Net6 or TestTargetFramework.Net) { referenceAssemblies = referenceAssemblies.Add(ancillary); } return(CSharpCompilation.Create("compilation", sources, referenceAssemblies, new CSharpCompilationOptions(outputKind, allowUnsafe: allowUnsafe))); }