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 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
    }
}