|
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Debugging;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Debugging;
[UseExportProvider]
[Trait(Traits.Feature, Traits.Features.DebuggingNameResolver)]
public class NameResolverTests
{
private static async Task TestAsync(string text, string searchText, params string[] expectedNames)
{
using var workspace = EditorTestWorkspace.CreateCSharp(text);
var nameResolver = new BreakpointResolver(workspace.CurrentSolution, searchText);
var results = await nameResolver.DoAsync(CancellationToken.None);
Assert.Equal(expectedNames, results.Select(r => r.LocationNameOpt));
}
[Fact]
public async Task TestCSharpLanguageDebugInfoCreateNameResolver()
{
using var workspace = EditorTestWorkspace.CreateCSharp(" ");
var debugInfo = new CSharpBreakpointResolutionService();
var results = await debugInfo.ResolveBreakpointsAsync(workspace.CurrentSolution, "goo", CancellationToken.None);
Assert.Equal(0, results.Count());
}
[Fact]
public async Task TestSimpleNameInClass()
{
var text =
"""
class C
{
void Goo()
{
}
}
""";
await TestAsync(text, "Goo", "C.Goo()");
await TestAsync(text, "goo");
await TestAsync(text, "C.Goo", "C.Goo()");
await TestAsync(text, "N.C.Goo");
await TestAsync(text, "Goo<T>");
await TestAsync(text, "C<T>.Goo");
await TestAsync(text, "Goo()", "C.Goo()");
await TestAsync(text, "Goo(int i)");
await TestAsync(text, "Goo(int)");
}
[Fact]
public async Task TestSimpleNameInNamespace()
{
var text =
"""
namespace N
{
class C
{
void Goo()
{
}
}
}
""";
await TestAsync(text, "Goo", "N.C.Goo()");
await TestAsync(text, "goo");
await TestAsync(text, "C.Goo", "N.C.Goo()");
await TestAsync(text, "N.C.Goo", "N.C.Goo()");
await TestAsync(text, "Goo<T>");
await TestAsync(text, "C<T>.Goo");
await TestAsync(text, "Goo()", "N.C.Goo()");
await TestAsync(text, "C.Goo()", "N.C.Goo()");
await TestAsync(text, "N.C.Goo()", "N.C.Goo()");
await TestAsync(text, "Goo(int i)");
await TestAsync(text, "Goo(int)");
await TestAsync(text, "Goo(a)");
}
[Fact]
public async Task TestSimpleNameInGenericClassNamespace()
{
var text =
"""
namespace N
{
class C<T>
{
void Goo()
{
}
}
}
""";
await TestAsync(text, "Goo", "N.C<T>.Goo()");
await TestAsync(text, "goo");
await TestAsync(text, "C.Goo", "N.C<T>.Goo()");
await TestAsync(text, "N.C.Goo", "N.C<T>.Goo()");
await TestAsync(text, "Goo<T>");
await TestAsync(text, "C<T>.Goo", "N.C<T>.Goo()");
await TestAsync(text, "C<T>.Goo()", "N.C<T>.Goo()");
await TestAsync(text, "Goo()", "N.C<T>.Goo()");
await TestAsync(text, "C.Goo()", "N.C<T>.Goo()");
await TestAsync(text, "N.C.Goo()", "N.C<T>.Goo()");
await TestAsync(text, "Goo(int i)");
await TestAsync(text, "Goo(int)");
await TestAsync(text, "Goo(a)");
}
[Fact]
public async Task TestGenericNameInClassNamespace()
{
var text =
"""
namespace N
{
class C
{
void Goo<T>()
{
}
}
}
""";
await TestAsync(text, "Goo", "N.C.Goo<T>()");
await TestAsync(text, "goo");
await TestAsync(text, "C.Goo", "N.C.Goo<T>()");
await TestAsync(text, "N.C.Goo", "N.C.Goo<T>()");
await TestAsync(text, "Goo<T>", "N.C.Goo<T>()");
await TestAsync(text, "Goo<X>", "N.C.Goo<T>()");
await TestAsync(text, "Goo<T,X>");
await TestAsync(text, "C<T>.Goo");
await TestAsync(text, "C<T>.Goo()");
await TestAsync(text, "Goo()", "N.C.Goo<T>()");
await TestAsync(text, "C.Goo()", "N.C.Goo<T>()");
await TestAsync(text, "N.C.Goo()", "N.C.Goo<T>()");
await TestAsync(text, "Goo(int i)");
await TestAsync(text, "Goo(int)");
await TestAsync(text, "Goo(a)");
await TestAsync(text, "Goo<T>(int i)");
await TestAsync(text, "Goo<T>(int)");
await TestAsync(text, "Goo<T>(a)");
}
[Fact]
public async Task TestOverloadsInSingleClass()
{
var text =
"""
class C
{
void Goo()
{
}
void Goo(int i)
{
}
}
""";
await TestAsync(text, "Goo", "C.Goo()", "C.Goo(int)");
await TestAsync(text, "goo");
await TestAsync(text, "C.Goo", "C.Goo()", "C.Goo(int)");
await TestAsync(text, "N.C.Goo");
await TestAsync(text, "Goo<T>");
await TestAsync(text, "C<T>.Goo");
await TestAsync(text, "Goo()", "C.Goo()");
await TestAsync(text, "Goo(int i)", "C.Goo(int)");
await TestAsync(text, "Goo(int)", "C.Goo(int)");
await TestAsync(text, "Goo(i)", "C.Goo(int)");
}
[Fact]
public async Task TestMethodsInMultipleClasses()
{
var text =
"""
namespace N
{
class C
{
void Goo()
{
}
}
}
namespace N1
{
class C
{
void Goo(int i)
{
}
}
}
""";
await TestAsync(text, "Goo", "N1.C.Goo(int)", "N.C.Goo()");
await TestAsync(text, "goo");
await TestAsync(text, "C.Goo", "N1.C.Goo(int)", "N.C.Goo()");
await TestAsync(text, "N.C.Goo", "N.C.Goo()");
await TestAsync(text, "N1.C.Goo", "N1.C.Goo(int)");
await TestAsync(text, "Goo<T>");
await TestAsync(text, "C<T>.Goo");
await TestAsync(text, "Goo()", "N.C.Goo()");
await TestAsync(text, "Goo(int i)", "N1.C.Goo(int)");
await TestAsync(text, "Goo(int)", "N1.C.Goo(int)");
await TestAsync(text, "Goo(i)", "N1.C.Goo(int)");
}
[Fact]
public async Task TestMethodsWithDifferentArityInMultipleClasses()
{
var text =
"""
namespace N
{
class C
{
void Goo()
{
}
}
}
namespace N1
{
class C
{
void Goo<T>(int i)
{
}
}
}
""";
await TestAsync(text, "Goo", "N1.C.Goo<T>(int)", "N.C.Goo()");
await TestAsync(text, "goo");
await TestAsync(text, "C.Goo", "N1.C.Goo<T>(int)", "N.C.Goo()");
await TestAsync(text, "N.C.Goo", "N.C.Goo()");
await TestAsync(text, "N1.C.Goo", "N1.C.Goo<T>(int)");
await TestAsync(text, "Goo<T>", "N1.C.Goo<T>(int)");
await TestAsync(text, "C<T>.Goo");
await TestAsync(text, "Goo()", "N.C.Goo()");
await TestAsync(text, "Goo<T>()");
await TestAsync(text, "Goo(int i)", "N1.C.Goo<T>(int)");
await TestAsync(text, "Goo(int)", "N1.C.Goo<T>(int)");
await TestAsync(text, "Goo(i)", "N1.C.Goo<T>(int)");
await TestAsync(text, "Goo<T>(int i)", "N1.C.Goo<T>(int)");
await TestAsync(text, "Goo<T>(int)", "N1.C.Goo<T>(int)");
await TestAsync(text, "Goo<T>(i)", "N1.C.Goo<T>(int)");
}
[Fact]
public async Task TestOverloadsWithMultipleParametersInSingleClass()
{
var text =
"""
class C
{
void Goo(int a)
{
}
void Goo(int a, string b = "bb")
{
}
void Goo(__arglist)
{
}
}
""";
await TestAsync(text, "Goo", "C.Goo(int)", "C.Goo(int, [string])", "C.Goo(__arglist)");
await TestAsync(text, "goo");
await TestAsync(text, "C.Goo", "C.Goo(int)", "C.Goo(int, [string])", "C.Goo(__arglist)");
await TestAsync(text, "N.C.Goo");
await TestAsync(text, "Goo<T>");
await TestAsync(text, "C<T>.Goo");
await TestAsync(text, "Goo()", "C.Goo(__arglist)");
await TestAsync(text, "Goo(int i)", "C.Goo(int)");
await TestAsync(text, "Goo(int)", "C.Goo(int)");
await TestAsync(text, "Goo(int x = 42)", "C.Goo(int)");
await TestAsync(text, "Goo(i)", "C.Goo(int)");
await TestAsync(text, "Goo(int i, int b)", "C.Goo(int, [string])");
await TestAsync(text, "Goo(int, bool)", "C.Goo(int, [string])");
await TestAsync(text, "Goo(i, s)", "C.Goo(int, [string])");
await TestAsync(text, "Goo(,)", "C.Goo(int, [string])");
await TestAsync(text, "Goo(int x = 42,)", "C.Goo(int, [string])");
await TestAsync(text, "Goo(int x = 42, y = 42)", "C.Goo(int, [string])");
await TestAsync(text, "Goo([attr] x = 42, y = 42)", "C.Goo(int, [string])");
await TestAsync(text, "Goo(int i, int b, char c)");
await TestAsync(text, "Goo(int, bool, char)");
await TestAsync(text, "Goo(i, s, c)");
await TestAsync(text, "Goo(__arglist)", "C.Goo(int)");
}
[Fact]
public async Task AccessorTests()
{
var text =
"""
class C
{
int Property1 { get { return 42; } }
int Property2 { set { } }
int Property3 { get; set;}
}
""";
await TestAsync(text, "Property1", "C.Property1");
await TestAsync(text, "Property2", "C.Property2");
await TestAsync(text, "Property3", "C.Property3");
}
[Fact]
public async Task NegativeTests()
{
var text =
"""
using System.Runtime.CompilerServices;
abstract class C
{
public abstract void AbstractMethod(int a);
int Field;
delegate void Delegate();
event Delegate Event;
[IndexerName("ABCD")]
int this[int i] { get { return i; } }
void Goo() { }
void Goo(int x = 1, int y = 2) { }
~C() { }
}
""";
await TestAsync(text, "AbstractMethod");
await TestAsync(text, "Field");
await TestAsync(text, "Delegate");
await TestAsync(text, "Event");
await TestAsync(text, "this");
await TestAsync(text, "C.this[int]");
await TestAsync(text, "C.get_Item");
await TestAsync(text, "C.get_Item(i)");
await TestAsync(text, "C[i]");
await TestAsync(text, "ABCD");
await TestAsync(text, "C.ABCD(int)");
await TestAsync(text, "42");
await TestAsync(text, "Goo", "C.Goo()", "C.Goo([int], [int])"); // just making sure it would normally resolve before trying bad syntax
await TestAsync(text, "Goo Goo");
await TestAsync(text, "Goo()asdf");
await TestAsync(text, "Goo(),");
await TestAsync(text, "Goo(),f");
await TestAsync(text, "Goo().Goo");
await TestAsync(text, "Goo(");
await TestAsync(text, "(Goo");
await TestAsync(text, "Goo)");
await TestAsync(text, "(Goo)");
await TestAsync(text, "Goo(x = 42, y = 42)", "C.Goo([int], [int])"); // just making sure it would normally resolve before trying bad syntax
await TestAsync(text, "int x = 42");
await TestAsync(text, "Goo(int x = 42, y = 42");
await TestAsync(text, "C");
await TestAsync(text, "C.C");
await TestAsync(text, "~");
await TestAsync(text, "~C");
await TestAsync(text, "C.~C()");
await TestAsync(text, "");
}
[Fact]
public async Task TestInstanceConstructors()
{
var text =
"""
class C
{
public C() { }
}
class G<T>
{
public G() { }
~G() { }
}
""";
await TestAsync(text, "C", "C.C()");
await TestAsync(text, "C.C", "C.C()");
await TestAsync(text, "C.C()", "C.C()");
await TestAsync(text, "C()", "C.C()");
await TestAsync(text, "C<T>");
await TestAsync(text, "C<T>()");
await TestAsync(text, "C(int i)");
await TestAsync(text, "C(int)");
await TestAsync(text, "C(i)");
await TestAsync(text, "G", "G<T>.G()");
await TestAsync(text, "G()", "G<T>.G()");
await TestAsync(text, "G.G", "G<T>.G()");
await TestAsync(text, "G.G()", "G<T>.G()");
await TestAsync(text, "G<T>.G", "G<T>.G()");
await TestAsync(text, "G<t>.G()", "G<T>.G()");
await TestAsync(text, "G<T>");
await TestAsync(text, "G<T>()");
await TestAsync(text, "G.G<T>");
await TestAsync(text, ".ctor");
await TestAsync(text, ".ctor()");
await TestAsync(text, "C.ctor");
await TestAsync(text, "C.ctor()");
await TestAsync(text, "G.ctor");
await TestAsync(text, "G<T>.ctor()");
await TestAsync(text, "Finalize", "G<T>.~G()");
}
[Fact]
public async Task TestStaticConstructors()
{
var text =
"""
class C
{
static C()
{
}
}
""";
await TestAsync(text, "C", "C.C()");
await TestAsync(text, "C.C", "C.C()");
await TestAsync(text, "C.C()", "C.C()");
await TestAsync(text, "C()", "C.C()");
await TestAsync(text, "C<T>");
await TestAsync(text, "C<T>()");
await TestAsync(text, "C(int i)");
await TestAsync(text, "C(int)");
await TestAsync(text, "C(i)");
await TestAsync(text, "C.cctor");
await TestAsync(text, "C.cctor()");
}
[Fact]
public async Task TestAllConstructors()
{
var text =
"""
class C
{
static C()
{
}
public C(int i)
{
}
}
""";
await TestAsync(text, "C", "C.C(int)", "C.C()");
await TestAsync(text, "C.C", "C.C(int)", "C.C()");
await TestAsync(text, "C.C()", "C.C()");
await TestAsync(text, "C()", "C.C()");
await TestAsync(text, "C<T>");
await TestAsync(text, "C<T>()");
await TestAsync(text, "C(int i)", "C.C(int)");
await TestAsync(text, "C(int)", "C.C(int)");
await TestAsync(text, "C(i)", "C.C(int)");
}
[Fact]
public async Task TestPartialMethods()
{
var text =
"""
partial class C
{
partial int M1();
partial void M2() { }
partial void M2();
partial int M3();
partial int M3(int x) { return 0; }
partial void M4() { }
}
""";
await TestAsync(text, "M1");
await TestAsync(text, "C.M1");
await TestAsync(text, "M2", "C.M2()");
await TestAsync(text, "M3", "C.M3(int)");
await TestAsync(text, "M3()");
await TestAsync(text, "M3(y)", "C.M3(int)");
await TestAsync(text, "M4", "C.M4()");
}
[Fact]
public async Task TestLeadingAndTrailingText()
{
var text =
"""
class C
{
void Goo() { };
}
""";
await TestAsync(text, "Goo;", "C.Goo()");
await TestAsync(text,
@"Goo();", "C.Goo()");
await TestAsync(text, " Goo;", "C.Goo()");
await TestAsync(text, " Goo;;");
await TestAsync(text, " Goo; ;");
await TestAsync(text,
@"Goo();", "C.Goo()");
await TestAsync(text,
@"Goo();", "C.Goo()");
await TestAsync(text,
@"Goo(); // comment", "C.Goo()");
await TestAsync(text,
"""
/*comment*/
Goo(/* params */); /* comment
""", "C.Goo()");
}
[Fact]
public async Task TestEscapedKeywords()
{
var text =
"""
struct @true { }
class @foreach
{
void where(@true @this) { }
void @false() { }
}
""";
await TestAsync(text, "where", "@foreach.where(@true)");
await TestAsync(text, "@where", "@foreach.where(@true)");
await TestAsync(text, "@foreach.where", "@foreach.where(@true)");
await TestAsync(text, "foreach.where");
await TestAsync(text, "@foreach.where(true)");
await TestAsync(text, "@foreach.where(@if)", "@foreach.where(@true)");
await TestAsync(text, "false");
}
[Fact]
public async Task TestAliasQualifiedNames()
{
var text =
"""
extern alias A
class C
{
void Goo(D d) { }
}
""";
await TestAsync(text, "A::Goo");
await TestAsync(text, "A::Goo(A::B)");
await TestAsync(text, "A::Goo(A::B)");
await TestAsync(text, "A::C.Goo");
await TestAsync(text, "C.Goo(A::Q)", "C.Goo(D)");
}
[Fact]
public async Task TestNestedTypesAndNamespaces()
{
var text =
"""
namespace N1
{
class C
{
void Goo() { }
}
namespace N2
{
class C { }
}
namespace N3
{
class D { }
}
namespace N4
{
class C
{
void Goo(double x) { }
class D
{
void Goo() { }
class E
{
void Goo() { }
}
}
}
}
namespace N5 { }
}
""";
await TestAsync(text, "Goo", "N1.N4.C.Goo(double)", "N1.N4.C.D.Goo()", "N1.N4.C.D.E.Goo()", "N1.C.Goo()");
await TestAsync(text, "C.Goo", "N1.N4.C.Goo(double)", "N1.C.Goo()");
await TestAsync(text, "D.Goo", "N1.N4.C.D.Goo()");
await TestAsync(text, "N1.N4.C.D.Goo", "N1.N4.C.D.Goo()");
await TestAsync(text, "N1.Goo");
await TestAsync(text, "N3.C.Goo");
await TestAsync(text, "N5.C.Goo");
}
[Fact]
public async Task TestInterfaces()
{
var text =
"""
interface I1
{
void Goo();
}
class C1 : I1
{
void I1.Goo() { }
}
""";
await TestAsync(text, "Goo", "C1.Goo()");
await TestAsync(text, "I1.Goo");
await TestAsync(text, "C1.Goo", "C1.Goo()");
await TestAsync(text, "C1.I1.Moo");
}
}
|