File: Microsoft.NetCore.Analyzers\Runtime\ProvideCorrectArgumentsToFormattingMethodsTests.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.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.ProvideCorrectArgumentsToFormattingMethodsAnalyzer,
    Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;
using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier<
    Microsoft.NetCore.Analyzers.Runtime.ProvideCorrectArgumentsToFormattingMethodsAnalyzer,
    Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;
 
namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests
{
    public class ProvideCorrectArgumentsToFormattingMethodsTests
    {
        #region Diagnostic Tests
 
        [Fact]
        public async Task CA2241CSharpStringAsync()
        {
            await VerifyCS.VerifyAnalyzerAsync(@"
using System;
 
public class C
{
    void Method()
    {
        var a = String.Format("""", 1);
        var b = String.Format(""{0}"", 1, 2);
        var c = String.Format(""{0} {1}"", 1, 2, 3);
        var d = String.Format(""{0} {1} {2}"", 1, 2, 3, 4);
        var e = string.Format(""{0} {0}"", 1, 2);
 
        IFormatProvider p = null;
        var f = String.Format(p, """", 1);
        var g = String.Format(p, ""{0}"", 1, 2);
        var h = String.Format(p, ""{0} {1}"", 1, 2, 3);
        var i = String.Format(p, ""{0} {1} {2}"", 1, 2, 3, 4);
    }
}
",
            GetCSharpResultAt(8, 17),
            GetCSharpResultAt(9, 17),
            GetCSharpResultAt(10, 17),
            GetCSharpResultAt(11, 17),
            GetCSharpResultAt(12, 17),
 
            GetCSharpResultAt(15, 17),
            GetCSharpResultAt(16, 17),
            GetCSharpResultAt(17, 17),
            GetCSharpResultAt(18, 17));
        }
 
        [Fact]
        public async Task CA2241CSharpConsoleWriteAsync()
        {
            await VerifyCS.VerifyAnalyzerAsync(@"
using System;
 
public class C
{
    void Method()
    {
        Console.Write("""", 1);
        Console.Write(""{0}"", 1, 2);
        Console.Write(""{0} {1}"", 1, 2, 3);
        Console.Write(""{0} {1} {2}"", 1, 2, 3, 4);
        Console.Write(""{0} {1} {2} {3}"", 1, 2, 3, 4, 5);
    }
}
",
            GetCSharpResultAt(8, 9),
            GetCSharpResultAt(9, 9),
            GetCSharpResultAt(10, 9),
            GetCSharpResultAt(11, 9),
            GetCSharpResultAt(12, 9));
        }
 
        [Fact]
        public async Task CA2241CSharpConsoleWriteLineAsync()
        {
            await VerifyCS.VerifyAnalyzerAsync(@"
using System;
 
public class C
{
    void Method()
    {
        Console.WriteLine("""", 1);
        Console.WriteLine(""{0}"", 1, 2);
        Console.WriteLine(""{0} {1}"", 1, 2, 3);
        Console.WriteLine(""{0} {1} {2}"", 1, 2, 3, 4);
        Console.WriteLine(""{0} {1} {2} {3}"", 1, 2, 3, 4, 5);
    }
}
",
            GetCSharpResultAt(8, 9),
            GetCSharpResultAt(9, 9),
            GetCSharpResultAt(10, 9),
            GetCSharpResultAt(11, 9),
            GetCSharpResultAt(12, 9));
        }
 
        [Fact]
        public async Task CA2241CSharpPassingAsync()
        {
            await VerifyCS.VerifyAnalyzerAsync(@"
using System;
 
public class C
{
    void Method()
    {
        var a = String.Format(""{0}"", 1);
        var b = String.Format(""{0} {1}"", 1, 2);
        var c = String.Format(""{0} {1} {2}"", 1, 2, 3);
        var d = String.Format(""{0} {1} {2} {3}"", 1, 2, 3, 4);
        var e = String.Format(""{0} {1} {2} {0}"", 1, 2, 3);
        var f = String.Format(""{0} {0} {0} {0}"", 1);
 
        Console.Write(""{0}"", 1);
        Console.Write(""{0} {1}"", 1, 2);
        Console.Write(""{0} {1} {2}"", 1, 2, 3);
        Console.Write(""{0} {1} {2} {3}"", 1, 2, 3, 4);
        Console.Write(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5);
        Console.Write(""{0} {1} {2} {3} {0}"", 1, 2, 3, 4);
        Console.Write(""{0} {0} {0} {0} {0}"", 1);
 
        Console.WriteLine(""{0}"", 1);
        Console.WriteLine(""{0} {1}"", 1, 2);
        Console.WriteLine(""{0} {1} {2}"", 1, 2, 3);
        Console.WriteLine(""{0} {1} {2} {3}"", 1, 2, 3, 4);
        Console.WriteLine(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5);
        Console.WriteLine(""{0} {1} {2} {3} {0}"", 1, 2, 3, 4);
        Console.WriteLine(""{0} {0} {0} {0} {0}"", 1);
    }
}
");
        }
 
        [Fact]
        public async Task CA2241CSharpDifferentDiagnosticsAsync()
        {
            await VerifyCS.VerifyAnalyzerAsync(@"
using System;
 
public class C
{
    void Method()
    {
        var a = String.Format(""{1}"", 1);
        var b = String.Format(""{0} {1}"", 1, 2);
        var c = String.Format(""{0} {1}"", 1, 2, 3);
        var d = String.Format(""{0} {1} {2"", 1, 2, 3);
 
        Console.Write(""{1}"", 1);
        Console.Write(""{0} {1}"", 1, 2);
        Console.Write(""{0} {1}"", 1, 2, 3);
        Console.Write(""{0} {1} {2"", 1, 2, 3);
 
        Console.WriteLine(""{1}"", 1);
        Console.WriteLine(""{0} {1}"", 1, 2);
        Console.WriteLine(""{0} {1}"", 1, 2, 3);
        Console.WriteLine(""{0} {1} {2"", 1, 2, 3);
    }
}
",
            GetCSharpResultAt(8, 17, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.ArgumentCountRule),
            GetCSharpResultAt(10, 17, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.ArgumentCountRule),
            GetCSharpResultAt(11, 17, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.InvalidFormatRule),
            GetCSharpResultAt(13, 9, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.ArgumentCountRule),
            GetCSharpResultAt(15, 9, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.ArgumentCountRule),
            GetCSharpResultAt(16, 9, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.InvalidFormatRule),
            GetCSharpResultAt(18, 9, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.ArgumentCountRule),
            GetCSharpResultAt(20, 9, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.ArgumentCountRule),
            GetCSharpResultAt(21, 9, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.InvalidFormatRule));
        }
 
        [Fact]
        public async Task CA2241CSharpExplicitObjectArraySupportedAsync()
        {
            await VerifyCS.VerifyAnalyzerAsync(@"
using System;
 
public class C
{
    void Method()
    {
        var s = String.Format(""{0} {1} {2} {3}"", new object[] {1, 2});
        Console.Write(""{0} {1} {2} {3}"", new object[] {1, 2, 3, 4, 5});
        Console.WriteLine(""{0} {1} {2} {3}"", new object[] {1, 2, 3, 4, 5});
    }
}
",
            GetCSharpResultAt(8, 17),
            GetCSharpResultAt(9, 9),
            GetCSharpResultAt(10, 9));
        }
 
        [Fact]
        public async Task CA2241CSharpVarArgsNotSupportedAsync()
        {
            // currently not supported due to "https://github.com/dotnet/roslyn/issues/7346"
            await new VerifyCS.Test
            {
                ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net472.Default,
                TestCode = @"
using System;
 
public class C
{
    void Method()
    {
        Console.Write(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, __arglist(5));
        Console.WriteLine(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, __arglist(5));
    }
}
",
            }.RunAsync();
        }
 
        [Fact]
        public async Task CA2241VBStringAsync()
        {
            await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
 
Public Class C
    Sub Method()
        Dim a = String.Format("""", 1)
        Dim b = String.Format(""{0}"", 1, 2)
        Dim c = String.Format(""{0} {1}"", 1, 2, 3)
        Dim d = String.Format(""{0} {1} {2}"", 1, 2, 3, 4)
 
        Dim p as IFormatProvider = Nothing
        Dim e = String.Format(p, """", 1)
        Dim f = String.Format(p, ""{0}"", 1, 2)
        Dim g = String.Format(p, ""{0} {1}"", 1, 2, 3)
        Dim h = String.Format(p, ""{0} {1} {2}"", 1, 2, 3, 4)
    End Sub
End Class
",
            GetBasicResultAt(6, 17),
            GetBasicResultAt(7, 17),
            GetBasicResultAt(8, 17),
            GetBasicResultAt(9, 17),
 
            GetBasicResultAt(12, 17),
            GetBasicResultAt(13, 17),
            GetBasicResultAt(14, 17),
            GetBasicResultAt(15, 17));
        }
 
        [Fact]
        public async Task CA2241VBConsoleWriteAsync()
        {
            // this works in VB
            // Dim s = Console.WriteLine(""{0} {1} {2}"", 1, 2, 3, 4)
            // since VB bind it to __arglist version where we skip analysis
            // due to a bug - https://github.com/dotnet/roslyn/issues/7346
            // we might skip it only in C# since VB doesn't support __arglist
            await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
 
Public Class C
    Sub Method()
        Console.Write("""", 1)
        Console.Write(""{0}"", 1, 2)
        Console.Write(""{0} {1}"", 1, 2, 3)
        Console.Write(""{0} {1} {2}"", 1, 2, 3, 4)
        Console.Write(""{0} {1} {2} {3}"", 1, 2, 3, 4, 5)
    End Sub
End Class
",
            GetBasicResultAt(6, 9),
            GetBasicResultAt(7, 9),
            GetBasicResultAt(8, 9),
#if NETCOREAPP
            GetBasicResultAt(9, 9),
#endif
            GetBasicResultAt(10, 9));
        }
 
        [Fact]
        public async Task CA2241VBConsoleWriteLineAsync()
        {
            // this works in VB
            // Dim s = Console.WriteLine(""{0} {1} {2}"", 1, 2, 3, 4)
            // since VB bind it to __arglist version where we skip analysis
            // due to a bug - https://github.com/dotnet/roslyn/issues/7346
            // we might skip it only in C# since VB doesn't support __arglist
            await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
 
Public Class C
    Sub Method()
        Console.WriteLine("""", 1)
        Console.WriteLine(""{0}"", 1, 2)
        Console.WriteLine(""{0} {1}"", 1, 2, 3)
        Console.WriteLine(""{0} {1} {2}"", 1, 2, 3, 4)
        Console.WriteLine(""{0} {1} {2} {3}"", 1, 2, 3, 4, 5)
    End Sub
End Class
",
            GetBasicResultAt(6, 9),
            GetBasicResultAt(7, 9),
            GetBasicResultAt(8, 9),
#if NETCOREAPP
            GetBasicResultAt(9, 9),
#endif
            GetBasicResultAt(10, 9));
        }
 
        [Fact]
        public async Task CA2241VBPassingAsync()
        {
            await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
 
Public Class C
    Sub Method()
        Dim a = String.Format(""{0}"", 1)
        Dim b = String.Format(""{0} {1}"", 1, 2)
        Dim c = String.Format(""{0} {1} {2}"", 1, 2, 3)
        Dim d = String.Format(""{0} {1} {2} {3}"", 1, 2, 3, 4)
 
        Console.Write(""{0}"", 1)
        Console.Write(""{0} {1}"", 1, 2)
        Console.Write(""{0} {1} {2}"", 1, 2, 3)
        Console.Write(""{0} {1} {2} {3}"", 1, 2, 3, 4)
        Console.Write(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5)
 
        Console.WriteLine(""{0}"", 1)
        Console.WriteLine(""{0} {1}"", 1, 2)
        Console.WriteLine(""{0} {1} {2}"", 1, 2, 3)
        Console.WriteLine(""{0} {1} {2} {3}"", 1, 2, 3, 4)
        Console.WriteLine(""{0} {1} {2} {3} {4}"", 1, 2, 3, 4, 5)
    End Sub
End Class
");
        }
 
        [Fact]
        public async Task CA2241VBExplicitObjectArraySupportedAsync()
        {
            await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
 
Public Class C
    Sub Method()
        Dim s = String.Format(""{0} {1} {2} {3}"", New Object() {1, 2})
        Console.Write(""{0} {1} {2} {3}"", New Object() {1, 2, 3, 4, 5})
        Console.WriteLine(""{0} {1} {2} {3}"", New Object() {1, 2, 3, 4, 5})
    End Sub
End Class
",
            GetBasicResultAt(6, 17),
            GetBasicResultAt(7, 9),
            GetBasicResultAt(8, 9));
        }
 
        [Fact]
        public async Task CA2241CSharpFormatStringParserAsync()
        {
            await VerifyCS.VerifyAnalyzerAsync(@"
using System;
 
public class C
{
    void Method()
    {
        var a = String.Format(""{0,-4 :xd}"", 1);
        var b = String.Format(""{0   ,    5 : d} {1}"", 1, 2);
        var c = String.Format(""{0:d} {1} {2}"", 1, 2, 3);
        var d = String.Format(""{0, 5} {1} {2} {3}"", 1, 2, 3, 4);
 
        Console.Write(""{0,1}"", 1);
        Console.Write(""{0:   x} {1}"", 1, 2);
        Console.Write(""{{escape}}{0} {1} {2}"", 1, 2, 3);
        Console.Write(""{0: {{escape}} x} {1} {2} {3}"", 1, 2, 3, 4);
        Console.Write(""{0 , -10  :   {{escape}}  y} {1} {2} {3} {4}"", 1, 2, 3, 4, 5);
    }
}
");
        }
 
        [Theory]
        [WorkItem(2799, "https://github.com/dotnet/roslyn-analyzers/issues/2799")]
        // No configuration - validate no diagnostics in default configuration
        [InlineData(null)]
        // Configured but disabled
        [InlineData(false)]
        // Configured and enabled
        [InlineData(true)]
        public async Task EditorConfigConfiguration_HeuristicAdditionalStringFormattingMethodsAsync(bool? editorConfig)
        {
            string editorConfigText = editorConfig == null ? string.Empty :
                "dotnet_code_quality.try_determine_additional_string_formatting_methods_automatically = " + editorConfig.Value;
 
            var csharpTest = new VerifyCS.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
class Test
{
    public static string MyFormat(string format, params object[] args) => format;
 
    void M1(string param)
    {
        var a = MyFormat("""", 1);
    }
}"
                    },
                    AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
") }
                }
            };
 
            if (editorConfig == true)
            {
                csharpTest.ExpectedDiagnostics.Add(
                    // Test0.cs(8,17): warning CA2241: Provide correct arguments to formatting methods
                    GetCSharpResultAt(8, 17));
            }
 
            await csharpTest.RunAsync();
 
            var basicTest = new VerifyVB.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
Class Test
    Public Shared Function MyFormat(format As String, ParamArray args As Object()) As String
        Return format
    End Function
 
    Private Sub M1(ByVal param As String)
        Dim a = MyFormat("""", 1)
    End Sub
End Class"
},
                    AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
") }
                }
            };
 
            if (editorConfig == true)
            {
                basicTest.ExpectedDiagnostics.Add(
                    // Test0.vb(8,17): warning CA2241: Provide correct arguments to formatting methods
                    GetBasicResultAt(8, 17));
            }
 
            await basicTest.RunAsync();
        }
 
        [Fact]
        [WorkItem(7023, "https://github.com/dotnet/roslyn-analyzers/issues/7023")]
 
        public async Task EditorConfigConfiguration_HeuristicAdditionalStringFormattingMethodsShouldNotConsiderIFormattableToString()
        {
            string editorConfigText =
                "dotnet_code_quality.try_determine_additional_string_formatting_methods_automatically = true";
 
            var csharpTest = new VerifyCS.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
class Test
{
    string Formatted(double value1, double value2)
    {
        return value1 >= value2 ?
            value1.ToString(""F1"", System.Globalization.CultureInfo.InvariantCulture) :
            value2.ToString(""F1"", System.Globalization.CultureInfo.InvariantCulture);
    }
}"
                    },
                    AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
") }
                }
            };
 
            await csharpTest.RunAsync();
 
            var basicTest = new VerifyVB.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
Class Test
    Private Function M1(ByVal value1 as Double, ByVal value2 as Double) As String
        If value1 > value2
            Return value1.ToString(""F1"", System.Globalization.CultureInfo.InvariantCulture)
        Else
            Return value2.ToString(""F1"", System.Globalization.CultureInfo.InvariantCulture)
        End If
    End Function
End Class"
},
                    AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
") }
                }
            };
 
            await basicTest.RunAsync();
        }
 
        [Theory]
        [WorkItem(2799, "https://github.com/dotnet/roslyn-analyzers/issues/2799")]
        // No configuration - validate no diagnostics in default configuration
        [InlineData("")]
        // Match by method name
        [InlineData("dotnet_code_quality.additional_string_formatting_methods = MyFormat")]
        // Setting only for Rule ID
        [InlineData("dotnet_code_quality." + ProvideCorrectArgumentsToFormattingMethodsAnalyzer.RuleId + ".additional_string_formatting_methods = MyFormat")]
        // Match by documentation ID without "M:" prefix
        [InlineData("dotnet_code_quality.additional_string_formatting_methods = Test.MyFormat(System.String,System.Object[])~System.String")]
        // Match by documentation ID with "M:" prefix
        [InlineData("dotnet_code_quality.additional_string_formatting_methods = M:Test.MyFormat(System.String,System.Object[])~System.String")]
        public async Task EditorConfigConfiguration_AdditionalStringFormattingMethodsAsync(string editorConfigText)
        {
            var csharpTest = new VerifyCS.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
class Test
{
    public static string MyFormat(string format, params object[] args) => format;
 
    void M1(string param)
    {
        var a = MyFormat("""", 1);
    }
}"
                    },
                    AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
") }
                }
            };
 
            if (editorConfigText.Length > 0)
            {
                csharpTest.ExpectedDiagnostics.Add(
                    // Test0.cs(8,17): warning CA2241: Provide correct arguments to formatting methods
                    GetCSharpResultAt(8, 17));
            }
 
            await csharpTest.RunAsync();
 
            var basicTest = new VerifyVB.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
Class Test
    Public Shared Function MyFormat(format As String, ParamArray args As Object()) As String
        Return format
    End Function
 
    Private Sub M1(ByVal param As String)
        Dim a = MyFormat("""", 1)
    End Sub
End Class"
},
                    AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true
 
[*]
{editorConfigText}
") }
                }
            };
 
            if (editorConfigText.Length > 0)
            {
                basicTest.ExpectedDiagnostics.Add(
                    // Test0.vb(8,17): warning CA2241: Provide correct arguments to formatting methods
                    GetBasicResultAt(8, 17));
            }
 
            await basicTest.RunAsync();
        }
 
        [Fact]
        [WorkItem(6012, "https://github.com/dotnet/roslyn-analyzers/issues/6012")]
        public async Task EditorConfigConfiguration_StringSyntaxAnnotatedMethodsAsync()
        {
            var csharpTest = new VerifyCS.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
using System.Diagnostics.CodeAnalysis;
 
class Test
{
    public static string MyFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string specification, params object[] args) => specification;
 
    void M1(string param)
    {
        var a = MyFormat("""", 1);
    }
}"
                    },
                    ReferenceAssemblies = ReferenceAssemblies.Net.Net70,
                }
            };
 
            csharpTest.ExpectedDiagnostics.Add(
                // Test0.cs(10,17): warning CA2241: Provide correct arguments to formatting methods
                GetCSharpResultAt(10, 17));
 
            await csharpTest.RunAsync();
 
            var basicTest = new VerifyVB.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
Imports System.Diagnostics.CodeAnalysis
 
Class Test
    Public Shared Function MyFormat(<StringSyntax(StringSyntaxAttribute.CompositeFormat)> specification As String, ParamArray args As Object()) As String
        Return specification
    End Function
 
    Private Sub M1(ByVal param As String)
        Dim a = MyFormat("""", 1)
    End Sub
End Class"
                    },
                    ReferenceAssemblies = ReferenceAssemblies.Net.Net70,
                }
            };
 
            basicTest.ExpectedDiagnostics.Add(
                // Test0.vb(10,17): warning CA2241: Provide correct arguments to formatting methods
                GetBasicResultAt(10, 17));
 
            await basicTest.RunAsync();
        }
 
        [Fact]
        public async Task EditorConfigConfiguration_StringSyntaxAnnotatedMethodsMultipleFrameworksAsync()
        {
            await new VerifyCS.Test
            {
                TestState =
                {
                    Sources =
                    {
                        """
                        using System.Diagnostics.CodeAnalysis;
 
                        class Test
                        {
                            public static string MyFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string specification, params object[] args) => specification;
 
                            void M1(string param)
                            {
                                var a = [|MyFormat("", 1)|];
                                var b = [|NetStandardTest.MyFormat("", 1)|];
                            }
                        }
                        """
                    },
                    AdditionalProjects =
                    {
                        ["NetstandardReference"] =
                        {
                            ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20,
                            Sources =
                            {
                                """
                                using System.Diagnostics.CodeAnalysis;
 
                                public class NetStandardTest
                                {
                                    public static string MyFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string specification, params object[] args) => specification;
                                }
                                """,
                                """
                                namespace System.Diagnostics.CodeAnalysis
                                {
                                    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
                                    internal sealed class StringSyntaxAttribute : Attribute
                                    {
                                        public StringSyntaxAttribute(string syntax)
                                        {
                                            Syntax = syntax;
                                            Arguments = Array.Empty<object?>();
                                        }
 
                                        public StringSyntaxAttribute(string syntax, params object?[] arguments)
                                        {
                                            Syntax = syntax;
                                            Arguments = arguments;
                                        }
 
                                        public string Syntax { get; }
                                        public object?[] Arguments { get; }
                                        public const string CompositeFormat = nameof(CompositeFormat);
                                    }
                                }
                                """
                            },
                        }
                    },
                    AdditionalProjectReferences =
                    {
                        "NetstandardReference"
                    },
                    ReferenceAssemblies = ReferenceAssemblies.Net.Net70,
                },
                MarkupOptions = MarkupOptions.UseFirstDescriptor,
            }.RunAsync();
 
            await new VerifyVB.Test
            {
                TestState =
                {
                    Sources =
                    {
                        """
                        Imports System.Diagnostics.CodeAnalysis
 
                        Class Test
                            Public Shared Function MyFormat(<StringSyntax(StringSyntaxAttribute.CompositeFormat)> specification As String, ParamArray args As Object()) As String
                                Return specification
                            End Function
 
                            Private Sub M1(ByVal param As String)
                                Dim a = [|MyFormat("", 1)|]
                                Dim b = [|NetStandardTest.MyFormat("", 1)|]
                            End Sub
                        End Class
                        """
                    },
                    AdditionalProjects =
                    {
                        ["NetstandardReference", LanguageNames.CSharp] =
                        {
                            ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20,
                            Sources =
                            {
                                """
                                using System.Diagnostics.CodeAnalysis;
 
                                public class NetStandardTest
                                {
                                    public static string MyFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string specification, params object[] args) => specification;
                                }
                                """,
                                """
                                namespace System.Diagnostics.CodeAnalysis
                                {
                                    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
                                    internal sealed class StringSyntaxAttribute : Attribute
                                    {
                                        public StringSyntaxAttribute(string syntax)
                                        {
                                            Syntax = syntax;
                                            Arguments = Array.Empty<object?>();
                                        }
 
                                        public StringSyntaxAttribute(string syntax, params object?[] arguments)
                                        {
                                            Syntax = syntax;
                                            Arguments = arguments;
                                        }
 
                                        public string Syntax { get; }
                                        public object?[] Arguments { get; }
                                        public const string CompositeFormat = nameof(CompositeFormat);
                                    }
                                }
                                """
                            },
                        }
                    },
                    AdditionalProjectReferences =
                    {
                        "NetstandardReference"
                    },
                    ReferenceAssemblies = ReferenceAssemblies.Net.Net70,
                },
                MarkupOptions = MarkupOptions.UseFirstDescriptor,
            }.RunAsync();
        }
 
        [Fact]
        [WorkItem(90357, "https://github.com/dotnet/runtime/issues/90357")]
        public async Task CA2241CSharpMethodWithNoPossibleArgumentsOnlyChecksFormat()
        {
            var csharpTest = new VerifyCS.Test
            {
                TestState =
                {
                    Sources =
                    {
                        @"
using System.Diagnostics.CodeAnalysis;
 
class Test
{
    public static int Parse([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format) => -1;
 
    void M1(string param)
    {
        var a = Parse(""{0} {1}"");
        var b = Parse(""{0 {1}"");
    }
}"
                    },
                    ReferenceAssemblies = ReferenceAssemblies.Net.Net70,
                }
            };
 
            csharpTest.ExpectedDiagnostics.Add(
                GetCSharpResultAt(11, 17, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.InvalidFormatRule));
 
            await csharpTest.RunAsync();
        }
 
        #endregion
 
        private static DiagnosticResult GetCSharpResultAt(int line, int column)
            => GetCSharpResultAt(line, column, ProvideCorrectArgumentsToFormattingMethodsAnalyzer.ArgumentCountRule);
 
        private static DiagnosticResult GetCSharpResultAt(int line, int column, DiagnosticDescriptor descriptor)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyCS.Diagnostic(descriptor)
                .WithLocation(line, column);
#pragma warning restore RS0030 // Do not use banned APIs
 
        private static DiagnosticResult GetBasicResultAt(int line, int column)
#pragma warning disable RS0030 // Do not use banned APIs
            => VerifyVB.Diagnostic(ProvideCorrectArgumentsToFormattingMethodsAnalyzer.ArgumentCountRule)
                .WithLocation(line, column);
#pragma warning restore RS0030 // Do not use banned APIs
    }
}