File: CodeGeneration\AddImportsTests.cs
Web Access
Project: src\src\Workspaces\CSharpTest\Microsoft.CodeAnalysis.CSharp.Workspaces.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.Workspaces.UnitTests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.CSharp.Simplification;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
 
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Editing;
 
[UseExportProvider]
public sealed class AddImportsTests
{
    private static async Task<Document> GetDocument(string code, bool withAnnotations)
    {
        var ws = new AdhocWorkspace();
        var emptyProject = ws.AddProject(
            ProjectInfo.Create(
                ProjectId.CreateNewId(),
                VersionStamp.Default,
                "test",
                "test.dll",
                LanguageNames.CSharp,
                metadataReferences: [NetFramework.mscorlib]));
 
        var doc = emptyProject.AddDocument("test.cs", code);
 
        if (withAnnotations)
        {
            var root = await doc.GetSyntaxRootAsync();
            var model = await doc.GetSemanticModelAsync();
 
            root = root.ReplaceNodes(root.DescendantNodesAndSelf().OfType<TypeSyntax>(),
                (o, c) =>
                {
                    var symbol = model.GetSymbolInfo(o).Symbol;
                    return symbol != null
                        ? c.WithAdditionalAnnotations(SymbolAnnotation.Create(symbol), Simplifier.Annotation)
                        : c;
                });
            doc = doc.WithSyntaxRoot(root);
        }
 
        return doc;
    }
 
    private static Task TestNoImportsAddedAsync(
        string initialText,
        bool useSymbolAnnotations)
    {
        return TestAsync(initialText, initialText, initialText, useSymbolAnnotations, performCheck: false);
    }
 
    private static async Task TestAsync(
        string initialText,
        string importsAddedText,
        string simplifiedText,
        bool useSymbolAnnotations,
        bool placeSystemNamespaceFirst = true,
        bool placeImportsInsideNamespaces = false,
        bool performCheck = true)
    {
        var doc = await GetDocument(initialText, useSymbolAnnotations);
 
        var addImportOptions = new AddImportPlacementOptions()
        {
            PlaceSystemNamespaceFirst = placeSystemNamespaceFirst,
            UsingDirectivePlacement = new CodeStyleOption2<AddImportPlacement>(placeImportsInsideNamespaces ? AddImportPlacement.InsideNamespace : AddImportPlacement.OutsideNamespace, NotificationOption2.None),
        };
 
        var formattingOptions = CSharpSyntaxFormattingOptions.Default;
 
        var simplifierOptions = CSharpSimplifierOptions.Default;
 
        var imported = useSymbolAnnotations
            ? await ImportAdder.AddImportsFromSymbolAnnotationAsync(doc, addImportOptions, CancellationToken.None)
            : await ImportAdder.AddImportsFromSyntaxesAsync(doc, addImportOptions, CancellationToken.None);
 
        if (importsAddedText != null)
        {
            var formatted = await Formatter.FormatAsync(imported, SyntaxAnnotation.ElasticAnnotation, formattingOptions, CancellationToken.None);
            var actualText = (await formatted.GetTextAsync()).ToString();
            Assert.Equal(importsAddedText, actualText);
        }
 
        if (simplifiedText != null)
        {
            var reduced = await Simplifier.ReduceAsync(imported, simplifierOptions, CancellationToken.None);
            var formatted = await Formatter.FormatAsync(reduced, SyntaxAnnotation.ElasticAnnotation, formattingOptions, CancellationToken.None);
 
            var actualText = (await formatted.GetTextAsync()).ToString();
            AssertEx.EqualOrDiff(simplifiedText, actualText);
        }
 
        if (performCheck)
        {
            if (initialText == importsAddedText && importsAddedText == simplifiedText)
                throw new Exception($"use {nameof(TestNoImportsAddedAsync)}");
        }
    }
 
    public static object[][] TestAllData =
    [
        [false],
        [true],
    ];
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestAddImport(bool useSymbolAnnotations)
    {
        await TestAsync(
@"class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
 
class C
{
    public List<int> F;
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestAddSystemImportFirst(bool useSymbolAnnotations)
    {
        await TestAsync(
@"using N;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
using N;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
using N;
 
class C
{
    public List<int> F;
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestDoNotAddSystemImportFirst(bool useSymbolAnnotations)
    {
        await TestAsync(
@"using N;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using N;
using System.Collections.Generic;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using N;
using System.Collections.Generic;
 
class C
{
    public List<int> F;
}",
            useSymbolAnnotations,
            placeSystemNamespaceFirst: false
);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestAddImportsInOrder(bool useSymbolAnnotations)
    {
        await TestAsync(
@"using System.Collections;
using System.Diagnostics;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
 
class C
{
    public List<int> F;
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestAddMultipleImportsInOrder(bool useSymbolAnnotations)
    {
        await TestAsync(
@"class C
{
    public System.Collections.Generic.List<int> F;
    public System.EventHandler Handler;
}",
 
@"using System;
using System.Collections.Generic;
 
class C
{
    public System.Collections.Generic.List<int> F;
    public System.EventHandler Handler;
}",
 
@"using System;
using System.Collections.Generic;
 
class C
{
    public List<int> F;
    public EventHandler Handler;
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestImportNotRedundantlyAdded(bool useSymbolAnnotations)
    {
        await TestAsync(
@"using System.Collections.Generic;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
 
class C
{
    public List<int> F;
}", useSymbolAnnotations);
    }
 
    [Fact]
    public async Task TestBuiltInTypeFromSyntaxes()
    {
        await TestAsync(
@"class C
{
    public System.Int32 F;
}",
 
@"using System;
 
class C
{
    public System.Int32 F;
}",
 
@"
class C
{
    public int F;
}", useSymbolAnnotations: false);
    }
 
    [Fact]
    public async Task TestBuiltInTypeFromSymbols()
    {
        await TestAsync(
@"class C
{
    public System.Int32 F;
}",
 
@"class C
{
    public System.Int32 F;
}",
 
@"class C
{
    public int F;
}", useSymbolAnnotations: true);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestImportNotAddedForNamespaceDeclarations(bool useSymbolAnnotations)
    {
        await TestNoImportsAddedAsync(
@"namespace N
{
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestImportNotAddedForReferencesInsideNamespaceDeclarations(bool useSymbolAnnotations)
    {
        await TestAsync(
@"namespace N
{
    class C
    {
        private N.C c;
    }
}",
 
@"namespace N
{
    class C
    {
        private N.C c;
    }
}",
 
@"namespace N
{
    class C
    {
        private C c;
    }
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestImportNotAddedForReferencesInsideParentOfNamespaceDeclarations(bool useSymbolAnnotations)
    {
        await TestAsync(
@"namespace N
{
    class C
    {
    }
}
 
namespace N.N1
{
    class C1
    {
        private N.C c;
    }
}",
 
@"namespace N
{
    class C
    {
    }
}
 
namespace N.N1
{
    class C1
    {
        private N.C c;
    }
}",
 
@"namespace N
{
    class C
    {
    }
}
 
namespace N.N1
{
    class C1
    {
        private C c;
    }
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestImportNotAddedForReferencesMatchingNestedImports(bool useSymbolAnnotations)
    {
        await TestAsync(
@"namespace N
{
    using System.Collections.Generic;
 
    class C
    {
        private System.Collections.Generic.List<int> F;
    }
}",
 
@"namespace N
{
    using System.Collections.Generic;
 
    class C
    {
        private System.Collections.Generic.List<int> F;
    }
}",
 
@"namespace N
{
    using System.Collections.Generic;
 
    class C
    {
        private List<int> F;
    }
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestImportRemovedIfItMakesReferenceAmbiguous(bool useSymbolAnnotations)
    {
        // this is not really an artifact of the AddImports feature, it is due
        // to Simplifier not reducing the namespace reference because it would 
        // become ambiguous, thus leaving an unused using directive
        await TestAsync(
@"namespace N
{
    class C
    {
    }
}
 
class C
{
    public N.C F;
}",
 
@"using N;
 
namespace N
{
    class C
    {
    }
}
 
class C
{
    public N.C F;
}",
 
@"
namespace N
{
    class C
    {
    }
}
 
class C
{
    public N.C F;
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    [WorkItem("https://github.com/dotnet/roslyn/issues/8797")]
    public async Task TestBannerTextRemainsAtTopOfDocumentWithoutExistingImports(bool useSymbolAnnotations)
    {
        await TestAsync(
@"// --------------------------------------------------------------------------------------------------------------------
// <copyright file=""File.cs"" company=""MyOrgnaization"">
// Copyright (C) MyOrgnaization 2016
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"// --------------------------------------------------------------------------------------------------------------------
// <copyright file=""File.cs"" company=""MyOrgnaization"">
// Copyright (C) MyOrgnaization 2016
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"// --------------------------------------------------------------------------------------------------------------------
// <copyright file=""File.cs"" company=""MyOrgnaization"">
// Copyright (C) MyOrgnaization 2016
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
 
class C
{
    public List<int> F;
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    [WorkItem("https://github.com/dotnet/roslyn/issues/8797")]
    public async Task TestBannerTextRemainsAtTopOfDocumentWithExistingImports(bool useSymbolAnnotations)
    {
        await TestAsync(
@"// --------------------------------------------------------------------------------------------------------------------
// <copyright file=""File.cs"" company=""MyOrgnaization"">
// Copyright (C) MyOrgnaization 2016
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using ZZZ;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"// --------------------------------------------------------------------------------------------------------------------
// <copyright file=""File.cs"" company=""MyOrgnaization"">
// Copyright (C) MyOrgnaization 2016
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using ZZZ;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"// --------------------------------------------------------------------------------------------------------------------
// <copyright file=""File.cs"" company=""MyOrgnaization"">
// Copyright (C) MyOrgnaization 2016
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
using System.Collections.Generic;
using ZZZ;
 
class C
{
    public List<int> F;
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    [WorkItem("https://github.com/dotnet/roslyn/issues/8797")]
    public async Task TestLeadingWhitespaceLinesArePreserved(bool useSymbolAnnotations)
    {
        await TestAsync(
@"class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
 
class C
{
    public List<int> F;
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestImportAddedToNestedImports(bool useSymbolAnnotations)
    {
        await TestAsync(
@"namespace N
{
    using System;
 
    class C
    {
        private System.Collections.Generic.List<int> F;
    }
}",
 
@"namespace N
{
    using System;
    using System.Collections.Generic;
 
    class C
    {
        private System.Collections.Generic.List<int> F;
    }
}",
 
@"namespace N
{
    using System;
    using System.Collections.Generic;
 
    class C
    {
        private List<int> F;
    }
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestImportNameNotSimplfied(bool useSymbolAnnotations)
    {
        await TestAsync(
@"namespace System
{
    using System.Threading;
 
    class C
    {
        private System.Collections.Generic.List<int> F;
    }
}",
 
@"namespace System
{
    using System.Collections.Generic;
    using System.Threading;
 
    class C
    {
        private System.Collections.Generic.List<int> F;
    }
}",
 
@"namespace System
{
    using System.Collections.Generic;
    using System.Threading;
 
    class C
    {
        private List<int> F;
    }
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestUnnecessaryImportAddedAndRemoved(bool useSymbolAnnotations)
    {
        await TestAsync(
@"using List = System.Collections.Generic.List<int>;
 
namespace System
{
    class C
    {
        private List F;
    }
}",
 
@"using System.Collections.Generic;
using List = System.Collections.Generic.List<int>;
 
namespace System
{
    class C
    {
        private List F;
    }
}",
 
@"using List = System.Collections.Generic.List<int>;
 
namespace System
{
    class C
    {
        private List F;
    }
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    public async Task TestImportAddedToStartOfDocumentIfNoNestedImports(bool useSymbolAnnotations)
    {
        await TestAsync(
@"namespace N
{
    class C
    {
        private System.Collections.Generic.List<int> F;
    }
}",
 
@"using System.Collections.Generic;
 
namespace N
{
    class C
    {
        private System.Collections.Generic.List<int> F;
    }
}",
 
@"using System.Collections.Generic;
 
namespace N
{
    class C
    {
        private List<int> F;
    }
}", useSymbolAnnotations);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    [WorkItem("https://github.com/dotnet/roslyn/issues/9228")]
    public async Task TestDoNotAddDuplicateImportIfNamespaceIsDefinedInSourceAndExternalAssembly(bool useSymbolAnnotations)
    {
        var externalCode =
@"namespace N.M { public class A : System.Attribute { } }";
 
        var code =
@"using System;
using N.M;
 
class C
{
    public void M1(String p1) { }
 
    public void M2([A] String p2) { }
}";
 
        var otherAssemblyReference = GetInMemoryAssemblyReferenceForCode(externalCode);
 
        var ws = new AdhocWorkspace();
        var emptyProject = ws.AddProject(
            ProjectInfo.Create(
                ProjectId.CreateNewId(),
                VersionStamp.Default,
                "test",
                "test.dll",
                LanguageNames.CSharp,
                metadataReferences: [NetFramework.mscorlib]));
 
        var project = emptyProject
            .AddMetadataReferences([otherAssemblyReference])
            .WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
 
        project = project.AddDocument("duplicate.cs", externalCode).Project;
        var document = project.AddDocument("test.cs", code);
 
        var compilation = await document.Project.GetCompilationAsync(CancellationToken.None);
        var compilerDiagnostics = compilation.GetDiagnostics(CancellationToken.None);
        Assert.Empty(compilerDiagnostics.Where(d => d.Severity == DiagnosticSeverity.Error));
 
        var attribute = compilation.GetTypeByMetadataName("N.M.A");
 
        var syntaxRoot = await document.GetSyntaxRootAsync(CancellationToken.None).ConfigureAwait(false);
        SyntaxNode p1SyntaxNode = syntaxRoot.DescendantNodes().OfType<ParameterSyntax>().FirstOrDefault();
 
        // Add N.M.A attribute to p1.
        var editor = await DocumentEditor.CreateAsync(document, CancellationToken.None).ConfigureAwait(false);
        var attributeSyntax = editor.Generator.Attribute(editor.Generator.TypeExpression(attribute));
 
        editor.AddAttribute(p1SyntaxNode, attributeSyntax);
        var documentWithAttribute = editor.GetChangedDocument();
 
        var addImportOptions = new AddImportPlacementOptions();
        var formattingOptions = CSharpSyntaxFormattingOptions.Default;
 
        // Add namespace import.
        var imported = useSymbolAnnotations
            ? await ImportAdder.AddImportsFromSymbolAnnotationAsync(documentWithAttribute, addImportOptions, CancellationToken.None).ConfigureAwait(false)
            : await ImportAdder.AddImportsFromSyntaxesAsync(documentWithAttribute, addImportOptions, CancellationToken.None).ConfigureAwait(false);
 
        var formatted = await Formatter.FormatAsync(imported, formattingOptions, CancellationToken.None);
        var actualText = (await formatted.GetTextAsync()).ToString();
 
        Assert.Equal(@"using System;
using N.M;
 
class C
{
    public void M1([global::N.M.A] String p1) { }
 
    public void M2([A] String p2) { }
}", actualText);
    }
 
    private static MetadataReference GetInMemoryAssemblyReferenceForCode(string code)
    {
        var tree = CSharpSyntaxTree.ParseText(code);
 
        var compilation = CSharpCompilation
            .Create("test.dll", [tree])
            .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
            .AddReferences(NetFramework.mscorlib);
 
        return compilation.ToMetadataReference();
    }
 
    #region AddImports Safe Tests
 
    [Fact]
    public async Task TestSafeWithMatchingSimpleName()
    {
        await TestNoImportsAddedAsync(
@"using B;
 
namespace A
{
    class C1 {}
    class C2 {}
}
 
namespace B
{
    class C1 {}
}
 
class C
{
    C1 M(A.C2 c2) => default;
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithMatchingGenericName()
    {
        await TestNoImportsAddedAsync(
@"using B;
 
namespace A
{
    class C1<T> {}
    class C2 {}
}
 
namespace B
{
    class C1<T> {}
}
 
class C
{
    C1<int> M(A.C2 c2) => default;
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithMatchingQualifiedName()
    {
        await TestNoImportsAddedAsync(
@"using B;
 
namespace A
{
    class O {}
    class C2 {}
}
 
namespace B
{
	class O
	{
    	public class C1 {}
	}
}
 
class C
{
    O.C1 M(A.C2 c2) => default;
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithMatchingAliasedIdentifierName()
    {
        await TestNoImportsAddedAsync(
@"using C1 = B.C1;
 
namespace A
{
    class C1 {}
    class C2 {}
}
 
namespace B
{
    class C1 {}
}
 
namespace Inner
{
    class C
    {
        C1 M(A.C2 c2) => default;
    }
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithMatchingGenericNameAndTypeArguments()
    {
        await TestNoImportsAddedAsync(
@"using B;
 
namespace A
{
    class C1<T> {}
    class C2 {}
    class C3 {}
}
 
namespace B
{
    class C1<T> {}
    class C3 {}
}
 
class C
{
    C1<C3> M(A.C2 c2) => default;
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithMatchingGenericNameAndTypeArguments_DifferentArity()
    {
        await TestAsync(
@"using B;
 
namespace A
{
    class C1<T, X> {}
    class C2 {}
}
 
namespace B
{
    class C1<T> {}
    class C3 {}
}
 
class C
{
    C1<C3> M(A.C2 c2) => default;
}",
@"using A;
using B;
 
namespace A
{
    class C1<T, X> {}
    class C2 {}
}
 
namespace B
{
    class C1<T> {}
    class C3 {}
}
 
class C
{
    C1<C3> M(A.C2 c2) => default;
}",
@"using A;
using B;
 
namespace A
{
    class C1<T, X> {}
    class C2 {}
}
 
namespace B
{
    class C1<T> {}
    class C3 {}
}
 
class C
{
    C1<C3> M(C2 c2) => default;
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithMatchingQualifiedNameAndTypeArguments()
    {
        await TestNoImportsAddedAsync(
@"using B;
 
namespace A
{
    class O {}
    class C2 {}
    class C3 {}
}
 
namespace B
{
    class C3 {}
	class O
	{
    	public class C1<T> {}
	}
}
 
class C
{
    O.C1<C3> M(A.C2 c2) => default;
}", useSymbolAnnotations: true);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/39641")]
    public async Task TestSafeWithMatchingSimpleNameInAllLocations()
    {
        await TestNoImportsAddedAsync(
@"using B;
using System.Collections.Generic;
 
namespace A
{
	class C1 { }
	class C2 { }
}
 
namespace B
{
	class C1
	{
		public static C1 P { get; }
	}
}
 
#nullable enable
#pragma warning disable
 
class C
{
	/// <summary>
	/// <see cref=""C1""/>
	/// </summary>
	C1 M(C1 c1, A.C2 c2)
 
    {
        C1 result = (C1)c1 ?? new C1() ?? C1.P ?? new C1[0] { }[0] ?? new List<C1>()[0] ?? (C1?)null;
        (C1 a, int b) = (default, default);
        return result;
    }
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithMatchingExtensionMethod()
    {
        await TestNoImportsAddedAsync(
@"using B;
 
namespace A
{
    static class AExtensions
    {
        public static void M(this int a){}
    }
    public class C1 {}
}
 
namespace B
{
    static class BExtensions
    {
        public static void M(this int a){}
    }
}
 
class C
{
    void M(A.C1 c1) => 42.M();
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithMatchingExtensionMethodAndArguments()
    {
        await TestNoImportsAddedAsync(
@"using B;
 
namespace A
{
    static class AExtensions
    {
        public static void M(this int a, C2 c2){}
    }
    public class C1 {}
    public class C2 {}
}
 
namespace B
{
    static class BExtensions
    {
        public static void M(this int a, C2 c2){}
    }
    public class C2 {}
}
 
class C
{
    void M(A.C1 c1) => 42.M(default(C2));
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithMatchingExtensionMethodAndTypeArguments()
    {
        await TestNoImportsAddedAsync(
@"using B;
 
namespace A
{
    static class AExtensions
    {
        public static void M<T>(this int a){}
    }
    public class C1 {}
    public class C2 {}
}
 
namespace B
{
    static class BExtensions
    {
        public static void M<T>(this int a){}
    }
    public class C2 {}
}
 
class C
{
    void M(A.C1 c1) => 42.M<C2>();
}", useSymbolAnnotations: true);
    }
 
    [Fact]
    public async Task TestSafeWithLambdaExtensionMethodAmbiguity()
    {
        await TestNoImportsAddedAsync(
@"using System;
 
class C
{
    // Don't add a using for N even though it is used here.
    public N.Other x;
 
    public static void Main()
    {
        M(x => x.M1());
    }
 
    public static void M(Action<C> a){}
    public static void M(Action<int> a){}
 
    public void M1(){}
}
 
namespace N
{
    public class Other { }
 
    public static class Extensions
    {
        public static void M1(this int a){}
    }
}", useSymbolAnnotations: true);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    [WorkItem("https://github.com/dotnet/roslyn/issues/55746")]
    public async Task TestAddImport_InsideNamespace(bool useSymbolAnnotations)
    {
        await TestAsync(
@"namespace N
{
    class C
    {
        public System.Collections.Generic.List<int> F;
    }
}",
 
@"namespace N
{
    using System.Collections.Generic;
 
    class C
    {
        public System.Collections.Generic.List<int> F;
    }
}",
 
@"namespace N
{
    using System.Collections.Generic;
 
    class C
    {
        public List<int> F;
    }
}", useSymbolAnnotations, placeImportsInsideNamespaces: true);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    [WorkItem("https://github.com/dotnet/roslyn/issues/55746")]
    public async Task TestAddImport_InsideNamespace_NoNamespace(bool useSymbolAnnotations)
    {
        await TestAsync(
@"class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
 
class C
{
    public System.Collections.Generic.List<int> F;
}",
 
@"using System.Collections.Generic;
 
class C
{
    public List<int> F;
}", useSymbolAnnotations, placeImportsInsideNamespaces: true);
    }
 
    [Theory, MemberData(nameof(TestAllData))]
    [WorkItem("https://github.com/dotnet/roslyn/issues/55746")]
    public async Task TestAddImport_InsideNamespace_MultipleNamespaces(bool useSymbolAnnotations)
    {
        await TestAsync(
@"namespace N1
{
    namespace N2
    {
        class C
        {
            public System.Collections.Generic.List<int> F;
        }
    }
}",
 
@"namespace N1
{
    namespace N2
    {
        using System.Collections.Generic;
 
        class C
        {
            public System.Collections.Generic.List<int> F;
        }
    }
}",
 
@"namespace N1
{
    namespace N2
    {
        using System.Collections.Generic;
 
        class C
        {
            public List<int> F;
        }
    }
}", useSymbolAnnotations, placeImportsInsideNamespaces: true);
    }
 
    #endregion
}