File: Microsoft.NetCore.Analyzers\Security\UseDefaultDllImportSearchPathsAttributeForLibraryImportAndDllImportTests.cs
Web Access
Project: ..\..\..\src\Microsoft.CodeAnalysis.NetAnalyzers\tests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests\Microsoft.CodeAnalysis.NetAnalyzers.UnitTests.csproj (Microsoft.CodeAnalysis.NetAnalyzers.UnitTests)
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the MIT license.  See License.txt in the project root for license information.
 
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpSecurityCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Security.UseDefaultDllImportSearchPathsAttribute,
    Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;
 
namespace Microsoft.NetCore.Analyzers.Security.UnitTests
{
    // All the test cases use user32.dll as an example,
    // however it is a commonly used system dll and will be influenced by Known Dlls mechanism,
    // which will ignore all the configuration about the search algorithm.
    // Fow now, this rule didn't take Known Dlls into consideration.
    // If it is needed in the future, we can recover this rule.
    public class UseDefaultDllImportSearchPathsAttributeWithLibraryImportTests
    {
        private async Task RunAnalyzerAsync(string source, string generatedSource, params DiagnosticResult[] diagnostics)
        {
            await RunAnalyzerWithConfigAsync(source, generatedSource, null, diagnostics);
        }
        private async Task RunAnalyzerWithConfigAsync(string source, string generatedSource, (string filename, string content)? editorConfig, params DiagnosticResult[] diagnostics)
        {
            var test = new VerifyCS.Test
            {
                TestState =
                {
                    Sources = {source + generatedSource },
					//GeneratedSources = { ("Generated.cs", generatedSource) },
				},
                LanguageVersion = LanguageVersion.CSharp9,
            };
            test.ExpectedDiagnostics.AddRange(diagnostics);
            if (editorConfig is not null)
            {
                test.TestState.AnalyzerConfigFiles.Add(editorConfig.Value);
            }
 
            await test.RunAsync();
        }
 
        private const string LibraryImportAttribute = """
 
            namespace System.Runtime.InteropServices
            {
                [System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple=false, Inherited=false)]
                public sealed class LibraryImportAttribute : Attribute
                {
                    public LibraryImportAttribute(string libraryName) { }
                }
            }
 
            """;
 
        private const string MessageBoxImplementation_NoDllImport = """
 
            partial class TestClass
            {
                public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type) => 0;
            }
 
            """;
 
        private const string MessageBoxImplementation_DllImport = """
 
            partial class TestClass
            {
            	[DllImport("user32.dll")]
                public static extern partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
            }
 
            """;
 
        // It will try to retrieve the MessageBox from user32.dll, which will be searched in a default order.
        [Fact]
        public async Task Test_LibraryImportAttribute_DiagnosticAsync()
        {
            string source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport,
                GetCSharpResultAt(8, 31, UseDefaultDllImportSearchPathsAttribute.UseDefaultDllImportSearchPathsAttributeRule, "MessageBox"));
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport,
                GetCSharpResultAt(8, 31, UseDefaultDllImportSearchPathsAttribute.UseDefaultDllImportSearchPathsAttributeRule, "MessageBox"));
        }
 
        [Fact]
        public async Task Test_DllInUpperCase_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.DLL")]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport,
                GetCSharpResultAt(8, 31, UseDefaultDllImportSearchPathsAttribute.UseDefaultDllImportSearchPathsAttributeRule, "MessageBox"));
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport,
                GetCSharpResultAt(8, 31, UseDefaultDllImportSearchPathsAttribute.UseDefaultDllImportSearchPathsAttributeRule, "MessageBox"));
 
        }
 
        [Fact]
        public async Task Test_WithoutDllExtension_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32")]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport,
                GetCSharpResultAt(8, 31, UseDefaultDllImportSearchPathsAttribute.UseDefaultDllImportSearchPathsAttributeRule, "MessageBox"));
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport,
                GetCSharpResultAt(8, 31, UseDefaultDllImportSearchPathsAttribute.UseDefaultDllImportSearchPathsAttributeRule, "MessageBox"));
        }
 
        [Fact]
        public async Task Test_DllImportSearchPathAssemblyDirectory_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport,
                GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "AssemblyDirectory"));
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport,
                GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "AssemblyDirectory"));
        }
 
        [Fact]
        public async Task Test_UnsafeDllImportSearchPathBits_BitwiseCombination_OneValueIsBad_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.UserDirectories)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport, GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "AssemblyDirectory"));
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport, GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "AssemblyDirectory"));
        }
 
        [Fact]
        public async Task Test_UnsafeDllImportSearchPathBits_BitwiseCombination_BothIsBad_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.ApplicationDirectory)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport, GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule,
                "AssemblyDirectory, ApplicationDirectory"));
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport, GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule,
                "AssemblyDirectory, ApplicationDirectory"));
 
        }
 
        [Fact]
        public async Task Test_DllImportSearchPathLegacyBehavior_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.LegacyBehavior)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport, GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule,
                    "LegacyBehavior"));
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport, GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule,
                    "LegacyBehavior"));
 
        }
 
        [Fact]
        public async Task Test_DllImportSearchPathUseDllDirectoryForDependencies_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.UseDllDirectoryForDependencies)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport,
                GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "UseDllDirectoryForDependencies"));
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport,
                GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "UseDllDirectoryForDependencies"));
        }
 
        [Fact]
        public async Task Test_DllImportSearchPathAssemblyDirectory_Assembly_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                [assembly:DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport,
                GetCSharpResultAt(10, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "AssemblyDirectory"));
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport,
                GetCSharpResultAt(10, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "AssemblyDirectory"));
        }
 
        [Fact]
        public async Task Test_AssemblyDirectory_ApplicationDirectory_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                [assembly:DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.ApplicationDirectory)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport,
                GetCSharpResultAt(11, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "ApplicationDirectory"));
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport,
                GetCSharpResultAt(11, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "ApplicationDirectory"));
        }
 
        [Fact]
        public async Task Test_ApplicationDirectory_AssemblyDirectory_DiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                [assembly:DefaultDllImportSearchPaths(DllImportSearchPath.ApplicationDirectory)]
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport, GetCSharpResultAt(11, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule,
                    "AssemblyDirectory"));
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport, GetCSharpResultAt(11, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule,
                    "AssemblyDirectory"));
        }
 
        [Theory]
        [InlineData("")]
        [InlineData("dotnet_code_quality.CA5393.unsafe_DllImportSearchPath_bits = 2 | 256 | 512")]
        [InlineData("dotnet_code_quality.CA5393.unsafe_DllImportSearchPath_bits = 770")]
        public async Task EditorConfigConfiguration_UnsafeDllImportSearchPathBits_DefaultValue_DiagnosticAsync(
            string editorConfigText)
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.ApplicationDirectory)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            var config = ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
");
            await RunAnalyzerWithConfigAsync(source, MessageBoxImplementation_DllImport, config,
                GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "AssemblyDirectory, ApplicationDirectory"));
            await RunAnalyzerWithConfigAsync(source, MessageBoxImplementation_NoDllImport, config,
                GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "AssemblyDirectory, ApplicationDirectory"));
        }
 
        [Theory]
        [InlineData("dotnet_code_quality.CA5393.unsafe_DllImportSearchPath_bits = 2048")]
        public async Task EditorConfigConfiguration_UnsafeDllImportSearchPathBits_NonDefaultValue_DiagnosticAsync(
            string editorConfigText)
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            var config = ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
");
            await RunAnalyzerWithConfigAsync(source + MessageBoxImplementation_DllImport, "", config, GetCSharpResultAt(9, 31,
                    UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "System32"));
            await RunAnalyzerWithConfigAsync(source + MessageBoxImplementation_NoDllImport, "", config, GetCSharpResultAt(9, 31,
                    UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "System32"));
        }
 
        [Theory]
        [InlineData("dotnet_code_quality.CA5393.unsafe_DllImportSearchPath_bits = 1026")]
        public async Task EditorConfigConfiguration_UnsafeDllImportSearchPathBits_BitwiseCombination_DiagnosticAsync(
            string editorConfigText)
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            var config = ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
");
            await RunAnalyzerWithConfigAsync(source, MessageBoxImplementation_DllImport, config,
                GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "UserDirectories"));
            await RunAnalyzerWithConfigAsync(source, MessageBoxImplementation_NoDllImport, config,
                GetCSharpResultAt(9, 31, UseDefaultDllImportSearchPathsAttribute.DoNotUseUnsafeDllImportSearchPathRule, "UserDirectories"));
        }
 
        // user32.dll will be searched in UserDirectories, which is specified by DllImportSearchPath and is good.
        [Fact]
        public async Task Test_LibraryImportAndDefaultDllImportSearchPathsAttributes_NoDiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport);
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport);
        }
 
        [Theory]
        [InlineData("dotnet_code_quality.CA5392.unsafe_DllImportSearchPath_bits = 2 | 1024")]
        [InlineData(
            "dotnet_code_quality.CA5392.unsafe_DllImportSearchPath_bits = DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.UserDirectories")]
        public async Task EditorConfigConfiguration_UnsafeDllImportSearchPathBits_BitwiseCombination_NoDiagnosticAsync(
            string editorConfigText)
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            var config = ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
");
            await RunAnalyzerWithConfigAsync(source, MessageBoxImplementation_DllImport, config);
            await RunAnalyzerWithConfigAsync(source, MessageBoxImplementation_NoDllImport, config);
        }
 
        [Theory]
        [InlineData("dotnet_code_quality.CA5393.unsafe_DllImportSearchPath_bits = 2048")]
        public async Task EditorConfigConfiguration_UnsafeDllImportSearchPathBits_NonDefaultValue_NoDiagnosticAsync(
            string editorConfigText)
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.AssemblyDirectory | DllImportSearchPath.ApplicationDirectory)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            var config = ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
");
            await RunAnalyzerWithConfigAsync(source, MessageBoxImplementation_DllImport, config);
            await RunAnalyzerWithConfigAsync(source, MessageBoxImplementation_NoDllImport, config);
        }
 
        [Fact]
        public async Task Test_NoAttribute_NoDiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport);
            // Doesn't make sense to run with a implementation with [DllImport]
        }
 
        // In this case, [DefaultDllImportSearchPaths] is applied to the assembly.
        // So, this attribute specifies the paths that are used by default to search for any DLL that provides a function for a platform invoke, in any code in the assembly.
        [Fact]
        public async Task Test_LibraryImportAndAssemblyDefaultDllImportSearchPathsAttributes_NoDiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                [assembly:DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
 
                partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport);
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport);
        }
 
        // It will have a compiler warning and recommend to use [LibraryImport] also.
        [Fact]
        public async Task Test_DefaultDllImportSearchPaths_NoDiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport);
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport);
        }
 
        // It will have a compiler warning and recommend to use [LibraryImport] also.
        [Fact]
        public async Task Test_AssemblyDefaultDllImportSearchPaths_NoDiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                [assembly:DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
 
                partial class TestClass
                {
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport);
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport);
        }
 
        [Fact]
        public async Task Test_DllImportAndLibraryImportWarnsOnLibraryImport()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                internal partial class TestClass
                {
                    [LibraryImport("user32.dll")]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
                } 
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport, GetCSharpResultAt(8, 31,
                UseDefaultDllImportSearchPathsAttribute.UseDefaultDllImportSearchPathsAttributeRule, "MessageBox"));
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport, GetCSharpResultAt(8, 31,
                UseDefaultDllImportSearchPathsAttribute.UseDefaultDllImportSearchPathsAttributeRule, "MessageBox"));
        }
 
        // [LibraryImport] is set with an absolute path, which will let the [DefaultDllImportSearchPaths] be ignored.
        [WindowsOnlyFact]
        public async Task Test_LibraryImportAttributeWithAbsolutePath_DefaultDllImportSearchPaths_NoDiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("C:\\Windows\\System32\\user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
 
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport);
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport);
        }
 
        // [LibraryImport] is set with an absolute path.
        [WindowsOnlyFact]
        public async Task Test_LibraryImportAttributeWithAbsolutePath_NoDiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("C:\\Windows\\System32\\user32.dll")]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport);
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport);
        }
 
        [WindowsOnlyFact]
        public async Task Test_UsingNonexistentAbsolutePath_NoDiagnosticAsync()
        {
            var source = """
 
                using System;
                using System.Runtime.InteropServices;
 
                partial class TestClass
                {
                    [LibraryImport("C:\\Nonexistent\\user32.dll")]
                    [DefaultDllImportSearchPaths(DllImportSearchPath.UserDirectories)]
                    public static partial int MessageBox(IntPtr hWnd, String text, String caption, uint type);
 
                    public void TestMethod()
                    {
                        MessageBox(new IntPtr(0), "Hello World!", "Hello Dialog", 0);
                    }
                }
                """ + LibraryImportAttribute;
            await RunAnalyzerAsync(source, MessageBoxImplementation_DllImport);
            await RunAnalyzerAsync(source, MessageBoxImplementation_NoDllImport);
        }
 
        private static DiagnosticResult GetCSharpResultAt(int line, int column, DiagnosticDescriptor rule,
            params string[] arguments)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyCS.Diagnostic(rule)
                .WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
                .WithArguments(arguments);
    }
}