Skip to content

Commit

Permalink
Add fixer for converting to GeneratedDllImport
Browse files Browse the repository at this point in the history
  • Loading branch information
elinor-fung committed Jan 15, 2021
1 parent a072658 commit 86b335a
Show file tree
Hide file tree
Showing 12 changed files with 716 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
using System.Threading.Tasks;
using Xunit;
using static Microsoft.Interop.Analyzers.ConvertToGeneratedDllImportFixer;

using VerifyCS = DllImportGenerator.UnitTests.Verifiers.CSharpCodeFixVerifier<
Microsoft.Interop.Analyzers.ConvertToGeneratedDllImportAnalyzer,
Microsoft.Interop.Analyzers.ConvertToGeneratedDllImportFixer>;

namespace DllImportGenerator.UnitTests
{
public class ConvertToGeneratedDllImportFixerTests
{
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Basic(bool usePreprocessorDefines)
{
string source = @$"
using System.Runtime.InteropServices;
partial class Test
{{
[DllImport(""DoesNotExist"")]
public static extern int [|Method|](out int ret);
}}";
// Fixed source will have CS8795 (Partial method must have an implementation) without generator run
string fixedSource = usePreprocessorDefines
? @$"
using System.Runtime.InteropServices;
partial class Test
{{
#if NET
[GeneratedDllImport(""DoesNotExist"")]
public static partial int {{|CS8795:Method|}}(out int ret);
#else
[DllImport(""DoesNotExist"")]
public static extern int Method(out int ret);
#endif
}}"
: @$"
using System.Runtime.InteropServices;
partial class Test
{{
[GeneratedDllImport(""DoesNotExist"")]
public static partial int {{|CS8795:Method|}}(out int ret);
}}";
await VerifyCS.VerifyCodeFixAsync(
source,
fixedSource,
usePreprocessorDefines ? WithPreprocessorDefinesKey : NoPreprocessorDefinesKey);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Comments(bool usePreprocessorDefines)
{
string source = @$"
using System.Runtime.InteropServices;
partial class Test
{{
// P/Invoke
[DllImport(/*name*/""DoesNotExist"")] // comment
public static extern int [|Method1|](out int ret);
/** P/Invoke **/
[DllImport(""DoesNotExist"") /*name*/]
// < ... >
public static extern int [|Method2|](out int ret);
}}";
// Fixed source will have CS8795 (Partial method must have an implementation) without generator run
string fixedSource = usePreprocessorDefines
? @$"
using System.Runtime.InteropServices;
partial class Test
{{
// P/Invoke
#if NET
[GeneratedDllImport(/*name*/""DoesNotExist"")] // comment
public static partial int {{|CS8795:Method1|}}(out int ret);
#else
[DllImport(/*name*/""DoesNotExist"")] // comment
public static extern int Method1(out int ret);
#endif
/** P/Invoke **/
#if NET
[GeneratedDllImport(""DoesNotExist"") /*name*/]
// < ... >
public static partial int {{|CS8795:Method2|}}(out int ret);
#else
[DllImport(""DoesNotExist"") /*name*/]
// < ... >
public static extern int Method2(out int ret);
#endif
}}"
: @$"
using System.Runtime.InteropServices;
partial class Test
{{
// P/Invoke
[GeneratedDllImport(/*name*/""DoesNotExist"")] // comment
public static partial int {{|CS8795:Method1|}}(out int ret);
/** P/Invoke **/
[GeneratedDllImport(""DoesNotExist"") /*name*/]
// < ... >
public static partial int {{|CS8795:Method2|}}(out int ret);
}}";
await VerifyCS.VerifyCodeFixAsync(
source,
fixedSource,
usePreprocessorDefines ? WithPreprocessorDefinesKey : NoPreprocessorDefinesKey);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task MultipleAttributes(bool usePreprocessorDefines)
{
string source = @$"
using System.Runtime.InteropServices;
partial class Test
{{
[System.ComponentModel.Description(""Test""), DllImport(""DoesNotExist"")]
public static extern int [|Method1|](out int ret);
[System.ComponentModel.Description(""Test"")]
[DllImport(""DoesNotExist"")]
[return: MarshalAs(UnmanagedType.I4)]
public static extern int [|Method2|](out int ret);
}}";
// Fixed source will have CS8795 (Partial method must have an implementation) without generator run
string fixedSource = usePreprocessorDefines
? @$"
using System.Runtime.InteropServices;
partial class Test
{{
#if NET
[System.ComponentModel.Description(""Test""), GeneratedDllImport(""DoesNotExist"")]
public static partial int {{|CS8795:Method1|}}(out int ret);
#else
[System.ComponentModel.Description(""Test""), DllImport(""DoesNotExist"")]
public static extern int Method1(out int ret);
#endif
#if NET
[System.ComponentModel.Description(""Test"")]
[GeneratedDllImport(""DoesNotExist"")]
[return: MarshalAs(UnmanagedType.I4)]
public static partial int {{|CS8795:Method2|}}(out int ret);
#else
[System.ComponentModel.Description(""Test"")]
[DllImport(""DoesNotExist"")]
[return: MarshalAs(UnmanagedType.I4)]
public static extern int Method2(out int ret);
#endif
}}"
: @$"
using System.Runtime.InteropServices;
partial class Test
{{
[System.ComponentModel.Description(""Test""), GeneratedDllImport(""DoesNotExist"")]
public static partial int {{|CS8795:Method1|}}(out int ret);
[System.ComponentModel.Description(""Test"")]
[GeneratedDllImport(""DoesNotExist"")]
[return: MarshalAs(UnmanagedType.I4)]
public static partial int {{|CS8795:Method2|}}(out int ret);
}}";
await VerifyCS.VerifyCodeFixAsync(
source,
fixedSource,
usePreprocessorDefines ? WithPreprocessorDefinesKey : NoPreprocessorDefinesKey);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task NamedArguments(bool usePreprocessorDefines)
{
string source = @$"
using System.Runtime.InteropServices;
partial class Test
{{
[DllImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static extern int [|Method1|](out int ret);
[DllImport(""DoesNotExist"", EntryPoint = ""Entry"", CharSet = CharSet.Unicode)]
public static extern int [|Method2|](out int ret);
}}";
// Fixed source will have CS8795 (Partial method must have an implementation) without generator run
string fixedSource = usePreprocessorDefines
? @$"
using System.Runtime.InteropServices;
partial class Test
{{
#if NET
[GeneratedDllImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static partial int {{|CS8795:Method1|}}(out int ret);
#else
[DllImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static extern int Method1(out int ret);
#endif
#if NET
[GeneratedDllImport(""DoesNotExist"", EntryPoint = ""Entry"", CharSet = CharSet.Unicode)]
public static partial int {{|CS8795:Method2|}}(out int ret);
#else
[DllImport(""DoesNotExist"", EntryPoint = ""Entry"", CharSet = CharSet.Unicode)]
public static extern int Method2(out int ret);
#endif
}}" : @$"
using System.Runtime.InteropServices;
partial class Test
{{
[GeneratedDllImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static partial int {{|CS8795:Method1|}}(out int ret);
[GeneratedDllImport(""DoesNotExist"", EntryPoint = ""Entry"", CharSet = CharSet.Unicode)]
public static partial int {{|CS8795:Method2|}}(out int ret);
}}";
await VerifyCS.VerifyCodeFixAsync(
source,
fixedSource,
usePreprocessorDefines ? WithPreprocessorDefinesKey : NoPreprocessorDefinesKey);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task RemoveableNamedArguments(bool usePreprocessorDefines)
{
string source = @$"
using System.Runtime.InteropServices;
partial class Test
{{
[DllImport(""DoesNotExist"", BestFitMapping = false, EntryPoint = ""Entry"")]
public static extern int [|Method1|](out int ret);
[DllImport(""DoesNotExist"", ThrowOnUnmappableChar = false)]
public static extern int [|Method2|](out int ret);
}}";
// Fixed source will have CS8795 (Partial method must have an implementation) without generator run
string fixedSource = usePreprocessorDefines
? @$"
using System.Runtime.InteropServices;
partial class Test
{{
#if NET
[GeneratedDllImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static partial int {{|CS8795:Method1|}}(out int ret);
#else
[DllImport(""DoesNotExist"", BestFitMapping = false, EntryPoint = ""Entry"")]
public static extern int Method1(out int ret);
#endif
#if NET
[GeneratedDllImport(""DoesNotExist"")]
public static partial int {{|CS8795:Method2|}}(out int ret);
#else
[DllImport(""DoesNotExist"", ThrowOnUnmappableChar = false)]
public static extern int Method2(out int ret);
#endif
}}" : @$"
using System.Runtime.InteropServices;
partial class Test
{{
[GeneratedDllImport(""DoesNotExist"", EntryPoint = ""Entry"")]
public static partial int {{|CS8795:Method1|}}(out int ret);
[GeneratedDllImport(""DoesNotExist"")]
public static partial int {{|CS8795:Method2|}}(out int ret);
}}";
await VerifyCS.VerifyCodeFixAsync(
source,
fixedSource,
usePreprocessorDefines ? WithPreprocessorDefinesKey : NoPreprocessorDefinesKey);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(CompilerPlatformVersion)" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.0.1-beta1.20478.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.0.1-beta1.20478.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.Net.Compilers.Toolset" Version="$(CompilerPlatformVersion)">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@

using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
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 DllImportGenerator.UnitTests.Verifiers
{
Expand Down Expand Up @@ -40,54 +35,9 @@ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticRes
await test.RunAsync(CancellationToken.None);
}

internal class Test : CSharpAnalyzerTest<TAnalyzer, XUnitVerifier>
{
public Test()
{
var (refAssem, ancillary) = TestUtils.GetReferenceAssemblies();
ReferenceAssemblies = refAssem;
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));
solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
solution = solution.WithProjectMetadataReferences(projectId, project.MetadataReferences.Concat(ImmutableArray.Create(ancillary)));
solution = solution.WithProjectParseOptions(projectId, ((CSharpParseOptions)project.ParseOptions!).WithLanguageVersion(LanguageVersion.Preview));
return solution;
});
}
}
// Code fix tests support both analyzer and code fix testing. This test class is derived from the code fix test
// to avoid the need to maintain duplicate copies of the customization work.
internal class Test : CSharpCodeFixVerifier<TAnalyzer, EmptyCodeFixProvider>.Test
{ }
}
}
Loading

0 comments on commit 86b335a

Please sign in to comment.