|
// 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.
using System.Collections.Immutable;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.AliasAmbiguousType;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.AliasAmbiguousType;
[Trait(Traits.Feature, Traits.Features.CodeActionsAliasAmbiguousType)]
public sealed class AliasAmbiguousTypeTests(ITestOutputHelper logger) : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest_NoEditor(logger)
{
internal override (DiagnosticAnalyzer?, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (null, new CSharpAliasAmbiguousTypeCodeFixProvider());
protected override ImmutableArray<CodeAction> MassageActions(ImmutableArray<CodeAction> actions)
=> FlattenActions(actions);
private static string GetAmbiguousDefinition(string typeDefinition, string ns1Name = "N1", string ns2Name = "N2")
=> $$"""
namespace {{ns1Name}}
{
{{typeDefinition}}
}
namespace {{ns2Name}}
{
{{typeDefinition}}
}
""";
[Fact]
public async Task TestAmbiguousClassObjectCreationUsingsInNamespace()
{
var classDef = GetAmbiguousDefinition("public class Ambiguous { }");
var initialMarkup = classDef + """
namespace Test
{
using N1;
using N2;
class C
{
void M()
{
var a = new [|Ambiguous|]();
}
}
}
""";
var expectedMarkup0 = classDef + """
namespace Test
{
using N1;
using N2;
using Ambiguous = N1.Ambiguous;
class C
{
void M()
{
var a = new Ambiguous();
}
}
}
""";
var expectedMarkup1 = classDef + """
namespace Test
{
using N1;
using N2;
using Ambiguous = N2.Ambiguous;
class C
{
void M()
{
var a = new Ambiguous();
}
}
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup0, index: 0);
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup1, index: 1);
await TestSmartTagTextAsync(initialMarkup, "using Ambiguous = N1.Ambiguous;");
}
[Fact]
public async Task TestAmbiguousClassObjectCreationUsingsInCompilationUnit()
{
var classDef = GetAmbiguousDefinition("public class Ambiguous { }");
await TestInRegularAndScriptAsync("""
using N1;
using N2;
""" + classDef + """
namespace Test
{
class C
{
void M()
{
var a = new [|Ambiguous|]();
}
}
}
""", """
using N1;
using N2;
using Ambiguous = N1.Ambiguous;
""" + classDef + """
namespace Test
{
class C
{
void M()
{
var a = new Ambiguous();
}
}
}
""");
}
[Fact]
public async Task TestAmbiguousClassObjectCreationGenericsDoNotOfferDiagnostic()
{
var genericAmbiguousClassDefinition = GetAmbiguousDefinition("public class Ambiguous<T> { }");
await TestMissingAsync("""
using N1;
using N2;
""" + genericAmbiguousClassDefinition + """
namespace Test
{
class C
{
void M()
{
var a = new [|Ambiguous<int>|]();
}
}
}
""");
}
[Fact]
public async Task TestAmbiguousAttribute()
{
var classDef = GetAmbiguousDefinition("public class AmbiguousAttribute: System.Attribute { }");
await TestInRegularAndScriptAsync("""
using N1;
using N2;
""" + classDef + """
namespace Test
{
[[|Ambiguous|]]
class C
{
}
}
""", """
using N1;
using N2;
using AmbiguousAttribute = N1.AmbiguousAttribute;
""" + classDef + """
namespace Test
{
[Ambiguous]
class C
{
}
}
""");
}
[Fact]
public Task TestNamespaceAndTypenameIdenticalOffersNoDiagnostics()
=> TestMissingAsync("""
<Workspace>
<Project Language="C#" AssemblyName="Assembly1" CommonReferences="true">
<Document FilePath="File1.cs">
namespace N
{
public class Ambiguous { }
}
</Document>
</Project>
<Project Language="C#" AssemblyName="Assembly2" CommonReferences="true">
<Document FilePath="File2.cs">
namespace N
{
public class Ambiguous { }
}
</Document>
</Project>
<Project Language="C#" AssemblyName="Test" CommonReferences="true">
<ProjectReference Alias="A1">Assembly1</ProjectReference>
<ProjectReference Alias="A2">Assembly2</ProjectReference>
<Document FilePath="File3.cs">
extern alias A1;
extern alias A2;
using A1::N;
using A2::N;
namespace N1
{
public class C
{
void M()
{
var a = new [|Ambiguous|]();
}
}
}
</Document>
</Project>
</Workspace>
""");
[Fact]
public Task TestAmbiguousAliasNoDiagnostics()
=> TestMissingAsync("""
extern alias alias;
using alias=alias;
class myClass : [|alias|]::Uri
{
}
""");
[Fact]
public async Task TestAmbiguousNestedClass()
{
var initialMarkup = """
using static Static<string>;
using static Static<int>;
public static class Static<T>
{
public class Nested
{
public void M() { }
}
}
class D
{
static void Main(string[] args)
{
var c = new [|Nested|]();
c.M();
}
}
""";
await TestInRegularAndScriptAsync(initialMarkup, """
using static Static<string>;
using static Static<int>;
using Nested = Static<string>.Nested;
public static class Static<T>
{
public class Nested
{
public void M() { }
}
}
class D
{
static void Main(string[] args)
{
var c = new Nested();
c.M();
}
}
""", index: 0);
await TestInRegularAndScriptAsync(initialMarkup, """
using static Static<string>;
using static Static<int>;
using Nested = Static<int>.Nested;
public static class Static<T>
{
public class Nested
{
public void M() { }
}
}
class D
{
static void Main(string[] args)
{
var c = new Nested();
c.M();
}
}
""", index: 1);
await TestSmartTagTextAsync(initialMarkup, "using Nested = Static<string>.Nested;");
}
[Fact]
public async Task TestAmbiguousClassDiagnosedAtBaseList()
{
var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }");
var initialMarkup = """
using N1;
using N2;
""" + classDef + """
namespace NTest
{
public class Test : [|AmbiguousClass|] { }
}
""";
var expectedMarkup = """
using N1;
using N2;
using AmbiguousClass = N1.AmbiguousClass;
""" + classDef + """
namespace NTest
{
public class Test : AmbiguousClass { }
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup);
}
[Fact]
public async Task TestAmbiguousClassDiagnosedAtTypeConstraint()
{
var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }");
var initialMarkup = """
using N1;
using N2;
""" + classDef + """
namespace NTest
{
public class Test<T> where T : [|AmbiguousClass|] { }
}
""";
var expectedMarkup = """
using N1;
using N2;
using AmbiguousClass = N1.AmbiguousClass;
""" + classDef + """
namespace NTest
{
public class Test<T> where T : AmbiguousClass { }
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup);
}
[Fact]
public async Task TestAmbiguousEnumDiagnosedAtFieldDeclaration()
{
var enumDef = GetAmbiguousDefinition(@"public enum AmbiguousEnum { }");
var initialMarkup = """
using N1;
using N2;
""" + enumDef + """
namespace NTest
{
public class Test
{
private [|AmbiguousEnum|] _AmbiguousEnum;
}
}
""";
var expectedMarkup = """
using N1;
using N2;
using AmbiguousEnum = N1.AmbiguousEnum;
""" + enumDef + """
namespace NTest
{
public class Test
{
private AmbiguousEnum _AmbiguousEnum;
}
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup);
}
[Fact]
public async Task TestAmbiguousStructDiagnosedAtPropertyDeclaration()
{
var strcutDef = GetAmbiguousDefinition(@"public struct AmbiguousStruct { }");
var initialMarkup = """
using N1;
using N2;
""" + strcutDef + """
namespace NTest
{
public class Test
{
public [|AmbiguousStruct|] AmbiguousStruct { get; }
}
}
""";
var expectedMarkup = """
using N1;
using N2;
using AmbiguousStruct = N1.AmbiguousStruct;
""" + strcutDef + """
namespace NTest
{
public class Test
{
public AmbiguousStruct AmbiguousStruct { get; }
}
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup);
}
[Fact]
public async Task TestAmbiguousClassDiagnosedAtTypeArgument()
{
var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }");
var initialMarkup = """
using N1;
using N2;
""" + classDef + """
namespace NTest
{
public class Test
{
public void M()
{
var list = new System.Collections.Generic.List<[|AmbiguousClass|]> { new AmbiguousClass() };
}
}
}
""";
var expectedMarkup = """
using N1;
using N2;
using AmbiguousClass = N1.AmbiguousClass;
""" + classDef + """
namespace NTest
{
public class Test
{
public void M()
{
var list = new System.Collections.Generic.List<AmbiguousClass> { new AmbiguousClass() };
}
}
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup);
}
[Fact]
public async Task TestAmbiguousClassDiagnosedAtIdentifierOfIncompleteExpression()
{
var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }");
var initialMarkup = """
using N1;
using N2;
""" + classDef + """
namespace NTest
{
public class Test
{
public void M()
{
[|AmbiguousClass|]
}
}
}
""";
var expectedMarkup = """
using N1;
using N2;
using AmbiguousClass = N1.AmbiguousClass;
""" + classDef + """
namespace NTest
{
public class Test
{
public void M()
{
AmbiguousClass
}
}
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup);
}
[Fact]
public async Task TestAmbiguousClassDiagnosedAtMethodParameter()
{
var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }");
var initialMarkup = """
using N1;
using N2;
""" + classDef + """
namespace NTest
{
public class Test
{
public void M([|AmbiguousClass|] a)
{
}
}
}
""";
var expectedMarkup = """
using N1;
using N2;
using AmbiguousClass = N1.AmbiguousClass;
""" + classDef + """
namespace NTest
{
public class Test
{
public void M(AmbiguousClass a)
{
}
}
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup);
}
[Fact]
public async Task TestAmbiguousClassDiagnosedAtFromClauseTypeIdentifier()
{
var classDef = GetAmbiguousDefinition(@"public class AmbiguousClass { }");
var initialMarkup = """
using N1;
using N2;
using System.Linq;
""" + classDef + """
namespace NTest
{
public class Test
{
public void M()
{
var qry = from [|AmbiguousClass|] a in new object[] { }
select a;
}
}
}
""";
var expectedMarkup = """
using N1;
using N2;
using System.Linq;
using AmbiguousClass = N1.AmbiguousClass;
""" + classDef + """
namespace NTest
{
public class Test
{
public void M()
{
var qry = from AmbiguousClass a in new object[] { }
select a;
}
}
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30838")]
public async Task TestSortSystemFirst1()
{
var classDef = GetAmbiguousDefinition("public class Ambiguous { }", "Microsoft", "System");
var initialMarkup = classDef + """
namespace Test
{
using System;
using Microsoft;
class C
{
void M()
{
var a = new [|Ambiguous|]();
}
}
}
""";
var expectedMarkup0 = classDef + """
namespace Test
{
using System;
using Microsoft;
using Ambiguous = System.Ambiguous;
class C
{
void M()
{
var a = new Ambiguous();
}
}
}
""";
var expectedMarkup1 = classDef + """
namespace Test
{
using System;
using Microsoft;
using Ambiguous = Microsoft.Ambiguous;
class C
{
void M()
{
var a = new Ambiguous();
}
}
}
""";
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup0, index: 0);
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup1, index: 1);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/30838")]
public async Task TestSortSystemFirst2()
{
var classDef = GetAmbiguousDefinition("public class Ambiguous { }", "Microsoft", "System");
var initialMarkup = classDef + """
namespace Test
{
using System;
using Microsoft;
class C
{
void M()
{
var a = new [|Ambiguous|]();
}
}
}
""";
var expectedMarkup0 = classDef + """
namespace Test
{
using System;
using Microsoft;
using Ambiguous = Microsoft.Ambiguous;
class C
{
void M()
{
var a = new Ambiguous();
}
}
}
""";
var expectedMarkup1 = classDef + """
namespace Test
{
using System;
using Microsoft;
using Ambiguous = System.Ambiguous;
class C
{
void M()
{
var a = new Ambiguous();
}
}
}
""";
var options = new OptionsCollection(LanguageNames.CSharp)
{
{ GenerationOptions.PlaceSystemNamespaceFirst, false }
};
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup0, options: options, index: 0);
await TestInRegularAndScriptAsync(initialMarkup, expectedMarkup1, options: options, index: 1);
}
}
|