|
// 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.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.Tagging;
using Microsoft.CodeAnalysis.Editor.UnitTests;
using Microsoft.CodeAnalysis.Editor.UnitTests.Classification;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Remote.Testing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Threading;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
using static Microsoft.CodeAnalysis.Editor.UnitTests.Classification.FormattedClassifications;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Classification;
[Trait(Traits.Feature, Traits.Features.Classification)]
public sealed partial class SemanticClassifierTests : AbstractCSharpClassifierTests
{
protected override async Task<ImmutableArray<ClassifiedSpan>> GetClassificationSpansAsync(string code, ImmutableArray<TextSpan> spans, ParseOptions? options, TestHost testHost)
{
using var workspace = CreateWorkspace(code, options, testHost);
var document = workspace.CurrentSolution.GetRequiredDocument(workspace.Documents.First().Id);
return await GetSemanticClassificationsAsync(document, spans);
}
private new Task TestAsync(
[StringSyntax("C#-Test")] string code,
TestHost testHost,
params FormattedClassification[] expected)
{
return base.TestAsync(code, testHost, expected);
}
[Theory, CombinatorialData]
public async Task GenericClassDeclaration(TestHost testHost)
{
await TestInMethodAsync(
className: "Class<T>",
methodName: "M",
@"new Class<int>();",
testHost,
Class("Class"));
}
[Theory, CombinatorialData]
public async Task RefVar(TestHost testHost)
{
await TestInMethodAsync(
@"int i = 0; ref var x = ref i;",
testHost,
Classifications(Keyword("var"), Local("i")));
}
[Theory, CombinatorialData]
public async Task UsingAlias1(TestHost testHost)
{
await TestAsync(
@"using M = System.Math;",
testHost,
Class("M"),
Namespace("System"),
Class("Math"),
Static("Math"));
}
[Theory, CombinatorialData]
public async Task DynamicAsTypeArgument(TestHost testHost)
{
await TestInMethodAsync(
className: "Class<T>",
methodName: "M",
@"new Class<dynamic>();",
testHost,
Classifications(Class("Class"), Keyword("dynamic")));
}
[Theory, CombinatorialData]
public async Task UsingTypeAliases(TestHost testHost)
{
var code = """
using Alias = Test;
class Test { void M() { Test a = new Test(); Alias b = new Alias(); } }
""";
await TestAsync(code,
code,
testHost,
Class("Alias"),
Class("Test"),
Class("Test"),
Class("Test"),
Class("Alias"),
Class("Alias"));
}
[Theory, CombinatorialData]
public async Task DynamicTypeAlias(TestHost testHost)
{
await TestAsync(
"""
using dynamic = System.EventArgs;
class C
{
dynamic d = new dynamic();
}
""",
testHost,
Class("dynamic"),
Namespace("System"),
Class("EventArgs"),
Class("dynamic"),
Class("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsDelegateName(TestHost testHost)
{
await TestAsync(
"""
delegate void dynamic();
class C
{
void M()
{
dynamic d;
}
}
""",
testHost,
Delegate("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsInterfaceName(TestHost testHost)
{
await TestAsync(
"""
interface dynamic
{
}
class C
{
dynamic d;
}
""",
testHost,
Interface("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsEnumName(TestHost testHost)
{
await TestAsync(
"""
enum dynamic
{
}
class C
{
dynamic d;
}
""",
testHost,
Enum("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsClassName(TestHost testHost)
{
await TestAsync(
"""
class dynamic
{
}
class C
{
dynamic d;
}
""",
testHost,
Class("dynamic"));
}
[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/46985")]
public async Task DynamicAsRecordName(TestHost testHost)
{
await TestAsync(
"""
record dynamic
{
}
class C
{
dynamic d;
}
""",
testHost,
RecordClass("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsClassNameAndLocalVariableName(TestHost testHost)
{
await TestAsync(
"""
class dynamic
{
dynamic()
{
dynamic dynamic;
}
}
""",
testHost,
Class("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsStructName(TestHost testHost)
{
await TestAsync(
"""
struct dynamic
{
}
class C
{
dynamic d;
}
""",
testHost,
Struct("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsGenericClassName(TestHost testHost)
{
await TestAsync(
"""
class dynamic<T>
{
}
class C
{
dynamic<int> d;
}
""",
testHost,
Class("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsGenericClassNameButOtherArity(TestHost testHost)
{
await TestAsync(
"""
class dynamic<T>
{
}
class C
{
dynamic d;
}
""",
testHost,
Keyword("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsUndefinedGenericType(TestHost testHost)
{
await TestAsync(
"""
class dynamic
{
}
class C
{
dynamic<int> d;
}
""",
testHost,
Class("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsExternAlias(TestHost testHost)
{
await TestAsync(
"""
extern alias dynamic;
class C
{
dynamic::Goo a;
}
""",
testHost,
Namespace("dynamic"));
}
[Theory, CombinatorialData]
public async Task GenericClassNameButOtherArity(TestHost testHost)
{
await TestAsync(
"""
class A<T>
{
}
class C
{
A d;
}
""", testHost,
Class("A"));
}
[Theory, CombinatorialData]
public async Task GenericTypeParameter(TestHost testHost)
{
await TestAsync(
"""
class C<T>
{
void M()
{
default(T) }
}
""",
testHost,
TypeParameter("T"));
}
[Theory, CombinatorialData]
public async Task GenericMethodTypeParameter(TestHost testHost)
{
await TestAsync(
"""
class C
{
T M<T>(T t)
{
return default(T);
}
}
""",
testHost,
TypeParameter("T"),
TypeParameter("T"),
TypeParameter("T"));
}
[Theory, CombinatorialData]
public async Task GenericMethodTypeParameterInLocalVariableDeclaration(TestHost testHost)
{
await TestAsync(
"""
class C
{
void M<T>()
{
T t;
}
}
""",
testHost,
TypeParameter("T"));
}
[Theory, CombinatorialData]
public async Task ParameterOfLambda1(TestHost testHost)
{
await TestAsync(
"""
class C
{
C()
{
Action a = (C p) => {
};
}
}
""",
testHost,
Class("C"));
}
[Theory, CombinatorialData]
public async Task ParameterOfAnonymousMethod(TestHost testHost)
{
await TestAsync(
"""
class C
{
C()
{
Action a = delegate (C p) {
};
}
}
""",
testHost,
Class("C"));
}
[Theory, CombinatorialData]
public async Task GenericTypeParameterAfterWhere(TestHost testHost)
{
await TestAsync(
"""
class C<A, B> where A : B
{
}
""",
testHost,
TypeParameter("A"),
TypeParameter("B"));
}
[Theory, CombinatorialData]
public async Task BaseClass(TestHost testHost)
{
await TestAsync(
"""
class C
{
}
class C2 : C
{
}
""",
testHost,
Class("C"));
}
[Theory, CombinatorialData]
public async Task BaseInterfaceOnInterface(TestHost testHost)
{
await TestAsync(
"""
interface T
{
}
interface T2 : T
{
}
""",
testHost,
Interface("T"));
}
[Theory, CombinatorialData]
public async Task BaseInterfaceOnClass(TestHost testHost)
{
await TestAsync(
"""
interface T
{
}
class T2 : T
{
}
""",
testHost,
Interface("T"));
}
[Theory, CombinatorialData]
public async Task InterfaceColorColor(TestHost testHost)
{
await TestAsync(
"""
interface T
{
}
class T2 : T
{
T T;
}
""",
testHost,
Interface("T"),
Interface("T"));
}
[Theory, CombinatorialData]
public async Task DelegateColorColor(TestHost testHost)
{
await TestAsync(
"""
delegate void T();
class T2
{
T T;
}
""",
testHost,
Delegate("T"));
}
[Theory, CombinatorialData]
public async Task DelegateReturnsItself(TestHost testHost)
{
await TestAsync(
"""
delegate T T();
class C
{
T T(T t);
}
""",
testHost,
Delegate("T"),
Delegate("T"),
Delegate("T"));
}
[Theory, CombinatorialData]
public async Task StructColorColor(TestHost testHost)
{
await TestAsync(
"""
struct T
{
T T;
}
""",
testHost,
Struct("T"));
}
[Theory, CombinatorialData]
public async Task EnumColorColor(TestHost testHost)
{
await TestAsync(
"""
enum T
{
T,
T
}
class C
{
T T;
}
""",
testHost,
Enum("T"));
}
[Theory, CombinatorialData]
public async Task DynamicAsGenericTypeParameter(TestHost testHost)
{
await TestAsync(
"""
class C<dynamic>
{
dynamic d;
}
""",
testHost,
TypeParameter("dynamic"));
}
[Theory, CombinatorialData]
public async Task DynamicAsGenericFieldName(TestHost testHost)
{
await TestAsync(
"""
class A<T>
{
T dynamic;
}
""",
testHost,
TypeParameter("T"));
}
[Theory, CombinatorialData]
public async Task PropertySameNameAsClass(TestHost testHost)
{
await TestAsync(
"""
class N
{
N N { get; set; }
void M()
{
N n = N;
N = n;
N = N;
}
}
""",
testHost,
Class("N"),
Class("N"),
Property("N"),
Property("N"),
Local("n"),
Property("N"),
Property("N"));
}
[Theory, CombinatorialData]
public async Task AttributeWithoutAttributeSuffix(TestHost testHost)
{
await TestAsync(
"""
using System;
[Obsolete]
class C
{
}
""",
testHost,
Namespace("System"),
Class("Obsolete"),
Obsolete("C"));
}
[Theory, CombinatorialData]
public async Task AttributeOnNonExistingMember(TestHost testHost)
{
await TestAsync(
"""
using System;
class A
{
[Obsolete]
}
""",
testHost,
Namespace("System"),
Class("Obsolete"));
}
[Theory, CombinatorialData]
public async Task AttributeWithoutAttributeSuffixOnAssembly(TestHost testHost)
{
await TestAsync(
"""
using System;
[assembly: My]
class MyAttribute : Attribute
{
}
""",
testHost,
Namespace("System"),
Class("My"),
Class("Attribute"));
}
[Theory, CombinatorialData]
public async Task AttributeViaNestedClassOrDerivedClass(TestHost testHost)
{
await TestAsync(
"""
using System;
[Base.My]
[Derived.My]
class Base
{
public class MyAttribute : Attribute
{
}
}
class Derived : Base
{
}
""",
testHost,
Namespace("System"),
Class("Base"),
Class("My"),
Class("Derived"),
Class("My"),
Class("Attribute"),
Class("Base"));
}
[Theory, CombinatorialData]
public async Task NamedAndOptional(TestHost testHost)
{
await TestAsync(
"""
class C
{
void B(C C = null)
{
}
void M()
{
B(C: null);
}
}
""",
testHost,
Class("C"),
Method("B"),
Parameter("C"));
}
[Theory, CombinatorialData]
public async Task PartiallyWrittenGenericName1(TestHost testHost)
{
await TestInMethodAsync(
className: "Class<T>",
methodName: "M",
@"Class<int",
testHost,
Class("Class"));
}
[Theory, CombinatorialData]
public async Task PartiallyWrittenGenericName2(TestHost testHost)
{
await TestInMethodAsync(
className: "Class<T1, T2>",
methodName: "M",
@"Class<int, b",
testHost,
Class("Class"));
}
// The "Color Color" problem is the C# IDE folklore for when
// a property name is the same as a type name
// and the resulting ambiguities that the spec
// resolves in favor of properties
[Theory, CombinatorialData]
public async Task ColorColor(TestHost testHost)
{
await TestAsync(
"""
class Color
{
Color Color;
}
""",
testHost,
Class("Color"));
}
[Theory, CombinatorialData]
public async Task ColorColor2(TestHost testHost)
{
await TestAsync(
"""
class T
{
T T = new T();
T()
{
this.T = new T();
}
}
""",
testHost,
Class("T"),
Class("T"),
Field("T"),
Class("T"));
}
[Theory, CombinatorialData]
public async Task ColorColor3(TestHost testHost)
{
await TestAsync(
"""
class T
{
T T = new T();
void M();
T()
{
T.M();
}
}
""",
testHost,
Class("T"),
Class("T"),
Field("T"),
Method("M"));
}
/// <summary>
/// Instance field should be preferred to type
/// §7.5.4.1
/// </summary>
[Theory, CombinatorialData]
public async Task ColorColor4(TestHost testHost)
{
await TestAsync(
"""
class T
{
T T;
void M()
{
T.T = null;
}
}
""",
testHost,
Class("T"),
Field("T"),
Field("T"));
}
/// <summary>
/// Type should be preferred to a static field
/// §7.5.4.1
/// </summary>
[Theory, CombinatorialData]
public async Task ColorColor5(TestHost testHost)
{
await TestAsync(
"""
class T
{
static T T;
void M()
{
T.T = null;
}
}
""",
testHost,
Class("T"),
Class("T"),
Field("T"),
Static("T"));
}
/// <summary>
/// Needs to prefer the local
/// </summary>
[Theory, CombinatorialData]
public async Task ColorColor6(TestHost testHost)
{
await TestAsync(
"""
class T
{
int field;
void M()
{
T T = new T();
T.field = 0;
}
}
""",
testHost,
Class("T"),
Class("T"),
Local("T"),
Field("field"));
}
/// <summary>
/// Needs to prefer the type
/// </summary>
[Theory, CombinatorialData]
public async Task ColorColor7(TestHost testHost)
{
await TestAsync(
"""
class T
{
static int field;
void M()
{
T T = new T();
T.field = 0;
}
}
""",
testHost,
Class("T"),
Class("T"),
Class("T"),
Field("field"),
Static("field"));
}
[Theory, CombinatorialData]
public async Task ColorColor8(TestHost testHost)
{
await TestAsync(
"""
class T
{
void M(T T)
{
}
void M2()
{
T T = new T();
M(T);
}
}
""",
testHost,
Class("T"),
Class("T"),
Class("T"),
Method("M"),
Local("T"));
}
[Theory, CombinatorialData]
public async Task ColorColor9(TestHost testHost)
{
await TestAsync(
"""
class T
{
T M(T T)
{
T = new T();
return T;
}
}
""",
testHost,
Class("T"),
Class("T"),
Parameter("T"),
Class("T"),
Parameter("T"));
}
[Theory, CombinatorialData]
public async Task ColorColor10(TestHost testHost)
{
// note: 'var' now binds to the type of the local.
await TestAsync(
"""
class T
{
void M()
{
var T = new object();
T temp = T as T;
}
}
""",
testHost,
Keyword("var"),
Class("T"),
Local("T"),
Class("T"));
}
[Theory, CombinatorialData]
public async Task ColorColor11(TestHost testHost)
{
await TestAsync(
"""
class T
{
void M()
{
var T = new object();
bool b = T is T;
}
}
""",
testHost,
Keyword("var"),
Local("T"),
Class("T"));
}
[Theory, CombinatorialData]
public async Task ColorColor12(TestHost testHost)
{
await TestAsync(
"""
class T
{
void M()
{
T T = new T();
var t = typeof(T);
}
}
""",
testHost,
Class("T"),
Class("T"),
Keyword("var"),
Class("T"));
}
[Theory, CombinatorialData]
public async Task ColorColor13(TestHost testHost)
{
await TestAsync(
"""
class T
{
void M()
{
T T = new T();
T t = default(T);
}
}
""",
testHost,
Class("T"),
Class("T"),
Class("T"),
Class("T"));
}
[Theory, CombinatorialData]
public async Task ColorColor14(TestHost testHost)
{
await TestAsync(
"""
class T
{
void M()
{
object T = new T();
T t = (T)T;
}
}
""",
testHost,
Class("T"),
Class("T"),
Class("T"),
Local("T"));
}
[Theory, CombinatorialData]
public async Task NamespaceNameSameAsTypeName1(TestHost testHost)
{
await TestAsync(
"""
namespace T
{
class T
{
void M()
{
T.T T = new T.T();
}
}
}
""",
testHost,
Namespace("T"),
Class("T"),
Class("T"));
}
[Theory, CombinatorialData]
public async Task NamespaceNameSameAsTypeNameWithGlobal(TestHost testHost)
{
await TestAsync(
"""
namespace T
{
class T
{
void M()
{
global::T.T T = new global::T.T();
}
}
}
""",
testHost,
Namespace("T"),
Namespace("T"),
Class("T"),
Namespace("T"),
Class("T"));
}
[Theory, CombinatorialData]
public async Task AmbiguityTypeAsGenericMethodArgumentVsLocal(TestHost testHost)
{
await TestAsync(
"""
class T
{
void M<T>()
{
T T;
M<T>();
}
}
""",
testHost,
TypeParameter("T"),
Method("M"),
TypeParameter("T"));
}
[Theory, CombinatorialData]
public async Task AmbiguityTypeAsGenericArgumentVsLocal(TestHost testHost)
{
await TestAsync(
"""
class T
{
class G<T>
{
}
void M()
{
T T;
G<T> g = new G<T>();
}
}
""",
testHost,
Class("T"),
Class("G"),
Class("T"),
Class("G"),
Class("T"));
}
[Theory, CombinatorialData]
public async Task AmbiguityTypeAsGenericArgumentVsField(TestHost testHost)
{
await TestAsync(
"""
class T
{
class H<T>
{
public static int f;
}
void M()
{
T T;
int i = H<T>.f;
}
}
""",
testHost,
Class("T"),
Class("H"),
Class("T"),
Field("f"),
Static("f"));
}
/// <summary>
/// §7.5.4.2
/// </summary>
[Theory, CombinatorialData]
public async Task GrammarAmbiguity_7_5_4_2(TestHost testHost)
{
await TestAsync(
"""
class M
{
void m()
{
int A = 2;
int B = 3;
F(G<A, B>(7));
}
void F(bool b)
{
}
bool G<t, f>(int a)
{
return true;
}
class A
{
}
class B
{
}
}
""",
testHost,
Method("F"),
Method("G"),
Class("A"),
Class("B"));
}
[Theory, CombinatorialData]
public async Task AnonymousTypePropertyName(TestHost testHost)
{
await TestAsync(
"""
using System;
class C
{
void M()
{
var x = new { String = " }; } }
""",
testHost,
Namespace("System"),
Keyword("var"),
Property("String"));
}
[Theory, CombinatorialData]
public async Task YieldAsATypeName(TestHost testHost)
{
await TestAsync(
"""
using System.Collections.Generic;
class yield
{
IEnumerable<yield> M()
{
yield yield = new yield();
yield return yield;
}
}
""",
testHost,
Namespace("System"),
Namespace("Collections"),
Namespace("Generic"),
Interface("IEnumerable"),
Class("yield"),
Class("yield"),
Class("yield"),
Local("yield"));
}
[Theory, CombinatorialData]
public async Task TypeNameDottedNames(TestHost testHost)
{
await TestAsync(
"""
class C
{
class Nested
{
}
C.Nested f;
}
""",
testHost,
Class("C"),
Class("Nested"));
}
[Theory, CombinatorialData]
public async Task BindingTypeNameFromBCLViaGlobalAlias(TestHost testHost)
{
await TestAsync(
"""
using System;
class C
{
global::System.String f;
}
""",
testHost,
Namespace("System"),
Namespace("System"),
Class("String"));
}
[Theory, CombinatorialData]
public async Task BindingTypeNames(TestHost testHost)
{
var code = """
using System;
using Str = System.String;
class C
{
class Nested { }
Str UsingAlias;
Nested NestedClass;
String BCL;
C ClassDeclaration;
C.Nested FCNested;
global::C FCN;
global::System.String FCNBCL;
global::Str GlobalUsingAlias;
}
""";
await TestAsync(code,
code,
testHost,
Options.Regular,
Namespace("System"),
Class("Str"),
Namespace("System"),
Class("String"),
Class("Str"),
Class("Nested"),
Class("String"),
Class("C"),
Class("C"),
Class("Nested"),
Class("C"),
Namespace("System"),
Class("String"));
}
[Theory, CombinatorialData]
public async Task Constructors(TestHost testHost)
{
await TestAsync(
"""
struct S
{
public int i;
public S(int i)
{
this.i = i;
}
}
class C
{
public C()
{
var s = new S(1);
var c = new C();
}
}
""",
testHost,
Field("i"),
Parameter("i"),
Keyword("var"),
Struct("S"),
Keyword("var"),
Class("C"));
}
[Theory, CombinatorialData]
public async Task TypesOfClassMembers(TestHost testHost)
{
await TestAsync(
"""
class Type
{
public Type()
{
}
static Type()
{
}
~Type()
{
}
Type Property { get; set; }
Type Method()
{
}
event Type Event;
Type this[Type index] { get; set; }
Type field;
const Type constant = null;
static operator Type(Type other)
{
}
static operator +(Type other)
{
}
static operator int(Type other)
{
}
static operator Type(int other)
{
}
}
""",
testHost,
Class("Type"),
Class("Type"),
Class("Type"),
Class("Type"),
Class("Type"),
Class("Type"),
Class("Type"),
Class("Type"),
Class("Type"),
Class("Type"),
Class("Type"),
Class("Type"));
}
/// <summary>
/// NAQ = Namespace Alias Qualifier (?)
/// </summary>
[Theory, CombinatorialData]
public async Task NAQTypeNameCtor(TestHost testHost)
{
await TestInMethodAsync(
@"System.IO.BufferedStream b = new global::System.IO.BufferedStream();",
testHost,
Namespace("System"),
Namespace("IO"),
Class("BufferedStream"),
Namespace("System"),
Namespace("IO"),
Class("BufferedStream"));
}
[Theory, CombinatorialData]
public async Task NAQEnum(TestHost testHost)
{
await TestAsync(
"""
class C
{
void M()
{
global::System.IO.DriveType d;
}
}
""",
testHost,
Namespace("System"),
Namespace("IO"),
Enum("DriveType"));
}
[Theory, CombinatorialData]
public async Task NAQDelegate(TestHost testHost)
{
await TestAsync(
"""
class C
{
void M()
{
global::System.AssemblyLoadEventHandler d;
}
}
""",
testHost,
Namespace("System"),
Delegate("AssemblyLoadEventHandler"));
}
[Theory, CombinatorialData]
public async Task NAQTypeNameMethodCall(TestHost testHost)
{
await TestInMethodAsync(@"global::System.String.Clone("");",
testHost,
Namespace("System"),
Class("String"),
Method("Clone"));
}
[Theory, CombinatorialData]
public async Task NAQEventSubscription(TestHost testHost)
{
await TestInMethodAsync(
"""
global::System.AppDomain.CurrentDomain.AssemblyLoad +=
delegate (object sender, System.AssemblyLoadEventArgs args) {};
""",
testHost,
Namespace("System"),
Class("AppDomain"),
Property("CurrentDomain"),
Static("CurrentDomain"),
Event("AssemblyLoad"),
Namespace("System"),
Class("AssemblyLoadEventArgs"));
}
[Theory, CombinatorialData]
public async Task AnonymousDelegateParameterType(TestHost testHost)
{
await TestAsync(
"""
class C
{
void M()
{
System.Action<System.EventArgs> a = delegate (System.EventArgs e) {
};
}
}
""",
testHost,
Namespace("System"),
Delegate("Action"),
Namespace("System"),
Class("EventArgs"),
Namespace("System"),
Class("EventArgs"));
}
[Theory, CombinatorialData]
public async Task NAQCtor(TestHost testHost)
{
await TestInMethodAsync(
@"global::System.Collections.DictionaryEntry de = new global::System.Collections.DictionaryEntry();",
testHost,
Namespace("System"),
Namespace("Collections"),
Struct("DictionaryEntry"),
Namespace("System"),
Namespace("Collections"),
Struct("DictionaryEntry"));
}
[Theory, CombinatorialData]
public async Task NAQSameFileClass(TestHost testHost)
{
var code = @"class C { static void M() { global::C.M(); } }";
await TestAsync(code,
testHost,
ParseOptions(Options.Regular),
Class("C"),
Method("M"),
Static("M"));
}
[Theory, CombinatorialData]
public async Task InteractiveNAQSameFileClass(TestHost testHost)
{
var code = @"class C { static void M() { global::Script.C.M(); } }";
await TestAsync(code,
testHost,
ParseOptions(Options.Script),
Class("Script"),
Class("C"),
Method("M"),
Static("M"));
}
[Theory, CombinatorialData]
public async Task NAQSameFileClassWithNamespace(TestHost testHost)
{
await TestAsync(
"""
using @global = N;
namespace N
{
class C
{
static void M()
{
global::N.C.M();
}
}
}
""",
testHost,
Namespace("@global"),
Namespace("N"),
Namespace("N"),
Namespace("N"),
Class("C"),
Method("M"),
Static("M"));
}
[Theory, CombinatorialData]
public async Task NAQSameFileClassWithNamespaceAndEscapedKeyword(TestHost testHost)
{
await TestAsync(
"""
using @global = N;
namespace N
{
class C
{
static void M()
{
@global.C.M();
}
}
}
""",
testHost,
Namespace("@global"),
Namespace("N"),
Namespace("N"),
Namespace("@global"),
Class("C"),
Method("M"),
Static("M"));
}
[Theory, CombinatorialData]
public async Task NAQGlobalWarning(TestHost testHost)
{
await TestAsync(
"""
using global = N;
namespace N
{
class C
{
static void M()
{
global.C.M();
}
}
}
""",
testHost,
Namespace("global"),
Namespace("N"),
Namespace("N"),
Namespace("global"),
Class("C"),
Method("M"),
Static("M"));
}
[Theory, CombinatorialData]
public async Task NAQUserDefinedNAQNamespace(TestHost testHost)
{
await TestAsync(
"""
using goo = N;
namespace N
{
class C
{
static void M()
{
goo.C.M();
}
}
}
""",
testHost,
Namespace("goo"),
Namespace("N"),
Namespace("N"),
Namespace("goo"),
Class("C"),
Method("M"),
Static("M"));
}
[Theory, CombinatorialData]
public async Task NAQUserDefinedNAQNamespaceDoubleColon(TestHost testHost)
{
await TestAsync(
"""
using goo = N;
namespace N
{
class C
{
static void M()
{
goo::C.M();
}
}
}
""",
testHost,
Namespace("goo"),
Namespace("N"),
Namespace("N"),
Namespace("goo"),
Class("C"),
Method("M"),
Static("M"));
}
[Theory, CombinatorialData]
public async Task NAQUserDefinedNamespace1(TestHost testHost)
{
await TestAsync(
"""
class C
{
void M()
{
A.B.D d;
}
}
namespace A
{
namespace B
{
class D
{
}
}
}
""",
testHost,
Namespace("A"),
Namespace("B"),
Class("D"),
Namespace("A"),
Namespace("B"));
}
[Theory, CombinatorialData]
public async Task NAQUserDefinedNamespaceWithGlobal(TestHost testHost)
{
await TestAsync(
"""
class C
{
void M()
{
global::A.B.D d;
}
}
namespace A
{
namespace B
{
class D
{
}
}
}
""",
testHost,
Namespace("A"),
Namespace("B"),
Class("D"),
Namespace("A"),
Namespace("B"));
}
[Theory, CombinatorialData]
public async Task NAQUserDefinedNAQForClass(TestHost testHost)
{
await TestAsync(
"""
using IO = global::System.IO;
class C
{
void M()
{
IO::BinaryReader b;
}
}
""",
testHost,
Namespace("IO"),
Namespace("System"),
Namespace("IO"),
Namespace("IO"),
Class("BinaryReader"));
}
[Theory, CombinatorialData]
public async Task NAQUserDefinedTypes(TestHost testHost)
{
await TestAsync(
"""
using rabbit = MyNameSpace;
class C
{
void M()
{
rabbit::MyClass2.method();
new rabbit::MyClass2().myEvent += null;
rabbit::MyEnum Enum;
rabbit::MyStruct strUct;
object o2 = rabbit::MyClass2.MyProp;
object o3 = rabbit::MyClass2.myField;
rabbit::MyClass2.MyDelegate del = null;
}
}
namespace MyNameSpace
{
namespace OtherNamespace
{
class A
{
}
}
public class MyClass2
{
public static int myField;
public delegate void MyDelegate();
public event MyDelegate myEvent;
public static void method()
{
}
public static int MyProp
{
get
{
return 0;
}
}
}
struct MyStruct
{
}
enum MyEnum
{
}
}
""",
testHost,
Namespace("rabbit"),
Namespace("MyNameSpace"),
Namespace("rabbit"),
Class("MyClass2"),
Method("method"),
Static("method"),
Namespace("rabbit"),
Class("MyClass2"),
Event("myEvent"),
Namespace("rabbit"),
Enum("MyEnum"),
Namespace("rabbit"),
Struct("MyStruct"),
Namespace("rabbit"),
Class("MyClass2"),
Property("MyProp"),
Static("MyProp"),
Namespace("rabbit"),
Class("MyClass2"),
Field("myField"),
Static("myField"),
Namespace("rabbit"),
Class("MyClass2"),
Delegate("MyDelegate"),
Namespace("MyNameSpace"),
Namespace("OtherNamespace"),
Delegate("MyDelegate"));
}
[Theory, CombinatorialData]
public async Task PreferPropertyOverNestedClass(TestHost testHost)
{
await TestAsync(
"""
class Outer
{
class A
{
public int B;
}
class B
{
void M()
{
A a = new A();
a.B = 10;
}
}
}
""",
testHost,
Class("A"),
Class("A"),
Local("a"),
Field("B"));
}
[Theory, CombinatorialData]
public async Task TypeNameInsideNestedClass(TestHost testHost)
{
await TestAsync(
"""
using System;
class Outer
{
class C
{
void M()
{
Console.WriteLine();
Console.WriteLine();
}
}
}
""",
testHost,
Namespace("System"),
Class("Console"),
Static("Console"),
Method("WriteLine"),
Static("WriteLine"),
Class("Console"),
Static("Console"),
Method("WriteLine"),
Static("WriteLine"));
}
[Theory, CombinatorialData]
public async Task StructEnumTypeNames(TestHost testHost)
{
await TestAsync(
"""
using System;
class C
{
enum MyEnum
{
}
struct MyStruct
{
}
static void Main()
{
ConsoleColor c;
Int32 i;
}
}
""",
testHost,
Namespace("System"),
Enum("ConsoleColor"),
Struct("Int32"));
}
[Theory, CombinatorialData]
public async Task PreferFieldOverClassWithSameName(TestHost testHost)
{
await TestAsync(
"""
class C
{
public int C;
void M()
{
C = 0;
}
}
""", testHost,
Field("C"));
}
[Theory, CombinatorialData]
public async Task AttributeBinding(TestHost testHost)
{
await TestAsync(
"""
using System;
[Serializable] // Binds to System.SerializableAttribute; colorized
class Serializable
{
}
[SerializableAttribute] // Binds to System.SerializableAttribute; colorized
class Serializable
{
}
[NonSerialized] // Binds to global::NonSerializedAttribute; colorized
class NonSerializedAttribute
{
}
[NonSerializedAttribute] // Binds to global::NonSerializedAttribute; colorized
class NonSerializedAttribute
{
}
[Obsolete] // Binds to global::Obsolete; colorized
class Obsolete : Attribute
{
}
[ObsoleteAttribute] // Binds to global::Obsolete; colorized
class ObsoleteAttribute : Attribute
{
}
""",
testHost,
Namespace("System"),
Class("Serializable"),
Class("SerializableAttribute"),
Class("NonSerialized"),
Class("NonSerializedAttribute"),
Class("Obsolete"),
Class("Attribute"),
Class("ObsoleteAttribute"),
Class("Attribute"));
}
[Theory, CombinatorialData]
public async Task ShouldNotClassifyNamespacesAsTypes(TestHost testHost)
{
await TestAsync(
"""
using System;
namespace Roslyn.Compilers.Internal
{
}
""",
testHost,
Namespace("System"),
Namespace("Roslyn"),
Namespace("Compilers"),
Namespace("Internal"));
}
[Theory, CombinatorialData]
public async Task NestedTypeCantHaveSameNameAsParentType(TestHost testHost)
{
await TestAsync(
"""
class Program
{
class Program
{
}
static void Main(Program p)
{
}
Program.Program p2;
}
""",
testHost,
Class("Program"),
Class("Program"));
}
[Theory, CombinatorialData]
public async Task NestedTypeCantHaveSameNameAsParentTypeWithGlobalNamespaceAlias(TestHost testHost)
{
var code = """
class Program
{
class Program { }
static void Main(Program p) { }
global::Program.Program p;
}
""";
await TestAsync(code,
testHost,
ParseOptions(Options.Regular),
Class("Program"),
Class("Program"),
Class("Program"));
}
[Theory, CombinatorialData]
public async Task InteractiveNestedTypeCantHaveSameNameAsParentTypeWithGlobalNamespaceAlias(TestHost testHost)
{
var code = """
class Program
{
class Program { }
static void Main(Program p) { }
global::Script.Program.Program p;
}
""";
await TestAsync(code,
testHost,
ParseOptions(Options.Script),
Class("Program"),
Class("Script"),
Class("Program"),
Class("Program"));
}
[Theory, CombinatorialData]
public async Task EnumFieldWithSameNameShouldBePreferredToType(TestHost testHost)
{
await TestAsync(
"""
enum E
{
E,
F = E
}
""", testHost,
EnumMember("E"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541150")]
[CombinatorialData]
public async Task TestGenericVarClassification(TestHost testHost)
{
await TestAsync(
"""
using System;
static class Program
{
static void Main()
{
var x = 1;
}
}
class var<T>
{
}
""",
testHost,
Namespace("System"),
Keyword("var"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541154")]
[CombinatorialData]
public async Task TestInaccessibleVarClassification(TestHost testHost)
{
await TestAsync(
"""
using System;
class A
{
private class var
{
}
}
class B : A
{
static void Main()
{
var x = 1;
}
}
""",
testHost,
Namespace("System"),
Class("A"),
Keyword("var"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541154")]
[CombinatorialData]
public async Task TestVarNamedTypeClassification(TestHost testHost)
{
await TestAsync(
"""
class var
{
static void Main()
{
var x;
}
}
""",
testHost,
Keyword("var"));
}
[Theory, WorkItem(9513, "DevDiv_Projects/Roslyn")]
[CombinatorialData]
public async Task RegressionFor9513(TestHost testHost)
{
await TestAsync(
"""
enum E
{
A,
B
}
class C
{
void M()
{
switch (new E())
{
case E.A:
goto case E.B;
case E.B:
goto default;
default:
goto case E.A;
}
}
}
""",
testHost,
Enum("E"),
Enum("E"),
EnumMember("A"),
Enum("E"),
EnumMember("B"),
Enum("E"),
EnumMember("B"),
Enum("E"),
EnumMember("A"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542368")]
[CombinatorialData]
public async Task RegressionFor9572(TestHost testHost)
{
await TestAsync(
"""
class A<T, S> where T : A<T, S>.I, A<T, T>.I
{
public interface I
{
}
}
""",
testHost,
TypeParameter("T"),
Class("A"),
TypeParameter("T"),
TypeParameter("S"),
Interface("I"),
Class("A"),
TypeParameter("T"),
TypeParameter("T"),
Interface("I"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542368")]
[CombinatorialData]
public async Task RegressionFor9831(TestHost testHost)
{
await TestAsync(@"F : A",
"""
public class B<T>
{
public class A
{
}
}
public class X : B<X>
{
public class F : A
{
}
}
""",
testHost,
Class("A"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542432")]
[CombinatorialData]
public async Task TestVar(TestHost testHost)
{
await TestAsync(
"""
class Program
{
class var<T>
{
}
static var<int> GetVarT()
{
return null;
}
static void Main()
{
var x = GetVarT();
var y = new var<int>();
}
}
""",
testHost,
Class("var"),
Keyword("var"),
Method("GetVarT"),
Static("GetVarT"),
Keyword("var"),
Class("var"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543123")]
[CombinatorialData]
public async Task TestVar2(TestHost testHost)
{
await TestAsync(
"""
class Program
{
void Main(string[] args)
{
foreach (var v in args)
{
}
}
}
""",
testHost,
Keyword("var"),
Parameter("args"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542778")]
[CombinatorialData]
public async Task TestDuplicateTypeParamWithConstraint(TestHost testHost)
{
await TestAsync(@"where U : IEnumerable<S>",
"""
using System.Collections.Generic;
class C<T>
{
public void Goo<U, U>(U arg)
where S : T
where U : IEnumerable<S>
{
}
}
""",
testHost,
TypeParameter("U"),
Interface("IEnumerable"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542685")]
[CombinatorialData]
public async Task OptimisticallyColorFromInDeclaration(TestHost testHost)
{
await TestInExpressionAsync("from ",
testHost,
Keyword("from"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542685")]
[CombinatorialData]
public async Task OptimisticallyColorFromInAssignment(TestHost testHost)
{
await TestInMethodAsync(
"""
var q = 3;
q = from
""",
testHost,
Keyword("var"),
Local("q"),
Keyword("from"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542685")]
[CombinatorialData]
public async Task DoNotColorThingsOtherThanFromInDeclaration(TestHost testHost)
=> await TestInExpressionAsync("fro ", testHost);
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542685")]
[CombinatorialData]
public async Task DoNotColorThingsOtherThanFromInAssignment(TestHost testHost)
{
await TestInMethodAsync(
"""
var q = 3;
q = fro
""",
testHost,
Keyword("var"),
Local("q"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542685")]
[CombinatorialData]
public async Task DoNotColorFromWhenBoundInDeclaration(TestHost testHost)
{
await TestInMethodAsync(
"""
var from = 3;
var q = from
""",
testHost,
Keyword("var"),
Keyword("var"),
Local("from"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542685")]
[CombinatorialData]
public async Task DoNotColorFromWhenBoundInAssignment(TestHost testHost)
{
await TestInMethodAsync(
"""
var q = 3;
var from = 3;
q = from
""",
testHost,
Keyword("var"),
Keyword("var"),
Local("q"),
Local("from"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543404")]
[CombinatorialData]
public async Task NewOfClassWithOnlyPrivateConstructor(TestHost testHost)
{
await TestAsync(
"""
class X
{
private X()
{
}
}
class Program
{
static void Main(string[] args)
{
new X();
}
}
""",
testHost,
Class("X"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544179")]
[CombinatorialData]
public async Task TestNullableVersusConditionalAmbiguity1(TestHost testHost)
{
await TestAsync(
"""
class Program
{
static void Main(string[] args)
{
C1 ?
}
}
public class C1
{
}
""",
testHost,
Class("C1"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544179")]
[CombinatorialData]
public async Task TestPointerVersusMultiplyAmbiguity1(TestHost testHost)
{
await TestAsync(
"""
class Program
{
static void Main(string[] args)
{
C1 *
}
}
public class C1
{
}
""",
testHost,
Class("C1"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544302")]
[CombinatorialData]
public async Task EnumTypeAssignedToNamedPropertyOfSameNameInAttributeCtor(TestHost testHost)
{
await TestAsync(
"""
using System;
using System.Runtime.InteropServices;
class C
{
[DllImport("abc", CallingConvention = CallingConvention)]
static extern void M();
}
""",
testHost,
Namespace("System"),
Namespace("System"),
Namespace("Runtime"),
Namespace("InteropServices"),
Class("DllImport"),
Field("CallingConvention"),
Enum("CallingConvention"));
}
[Theory, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531119")]
[CombinatorialData]
public async Task OnlyClassifyGenericNameOnce(TestHost testHost)
{
await TestAsync(
"""
enum Type
{
}
struct Type<T>
{
Type<int> f;
}
""",
testHost,
Struct("Type"));
}
[Theory, CombinatorialData]
public async Task NameOf1(TestHost testHost)
{
await TestAsync(
"""
class C
{
void goo()
{
var x = nameof
}
}
""",
testHost,
Keyword("var"),
Keyword("nameof"));
}
[Theory, CombinatorialData]
public async Task NameOf2(TestHost testHost)
{
await TestAsync(
"""
class C
{
void goo()
{
var x = nameof(C);
}
}
""",
testHost,
Keyword("var"),
Keyword("nameof"),
Class("C"));
}
[Theory, CombinatorialData]
public async Task NameOfLocalMethod(TestHost testHost)
{
await TestAsync(
"""
class C
{
void goo()
{
var x = nameof(M);
void M()
{
}
void M(int a)
{
}
void M(string s)
{
}
}
}
""",
testHost,
Keyword("var"),
Keyword("nameof"),
Method("M"));
}
[Theory, CombinatorialData]
public async Task MethodCalledNameOfInScope(TestHost testHost)
{
await TestAsync(
"""
class C
{
void nameof(int i)
{
}
void goo()
{
int y = 3;
var x = nameof();
}
}
""",
testHost,
Keyword("var"),
Method("nameof"));
}
[Theory, CombinatorialData]
public async Task Tuples(TestHost testHost)
{
await TestAsync(
"""
class C
{
(int a, int b) x;
}
""",
testHost,
ParseOptions(TestOptions.Regular, Options.Script));
}
[Theory, CombinatorialData]
[WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/261049")]
public async Task DevDiv261049RegressionTest(TestHost testHost)
{
var source = """
var (a,b) = Get(out int x, out int y);
Console.WriteLine($"({a.first}, {a.second})");
""";
await TestInMethodAsync(
source,
testHost,
Keyword("var"), Local("a"), Local("a"));
}
[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/633")]
public async Task InXmlDocCref_WhenTypeOnlyIsSpecified_ItIsClassified(TestHost testHost)
{
await TestAsync(
"""
/// <summary>
/// <see cref="MyClass"/>
/// </summary>
class MyClass
{
public MyClass(int x)
{
}
}
""",
testHost,
Class("MyClass"));
}
[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/633")]
public async Task InXmlDocCref_WhenConstructorOnlyIsSpecified_NothingIsClassified(TestHost testHost)
{
await TestAsync(
"""
/// <summary>
/// <see cref="MyClass(int)"/>
/// </summary>
class MyClass
{
public MyClass(int x)
{
}
}
""", testHost,
Class("MyClass"));
}
[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/633")]
public async Task InXmlDocCref_WhenTypeAndConstructorSpecified_OnlyTypeIsClassified(TestHost testHost)
{
await TestAsync(
"""
/// <summary>
/// <see cref="MyClass.MyClass(int)"/>
/// </summary>
class MyClass
{
public MyClass(int x)
{
}
}
""",
testHost,
Class("MyClass"),
Class("MyClass"));
}
[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/13174")]
public async Task TestMemberBindingThatLooksGeneric(TestHost testHost)
{
await TestAsync(
"""
using System.Diagnostics;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Debug.Assert(args?.Length < 2);
}
}
}
""",
testHost,
Namespace("System"),
Namespace("Diagnostics"),
Namespace("System"),
Namespace("Threading"),
Namespace("Tasks"),
Namespace("ConsoleApplication1"),
Class("Debug"),
Static("Debug"),
Method("Assert"),
Static("Assert"),
Parameter("args"),
Property("Length"));
}
[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/23940")]
public async Task TestAliasQualifiedClass(TestHost testHost)
{
await TestAsync(
"""
using System;
using Col = System.Collections.Generic;
namespace AliasTest
{
class Program
{
static void Main(string[] args)
{
var list1 = new Col::List
}
}
}
""",
testHost,
Namespace("System"),
Namespace("Col"),
Namespace("System"),
Namespace("Collections"),
Namespace("Generic"),
Namespace("AliasTest"),
Keyword("var"),
Namespace("Col"),
Class("List"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_InsideMethod(TestHost testHost)
{
// Asserts no Keyword("unmanaged") because it is an identifier.
await TestInMethodAsync("""
var unmanaged = 0;
unmanaged++;
""",
testHost,
Keyword("var"),
Local("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_Type_Keyword(TestHost testHost)
{
await TestAsync(
"class X<T> where T : unmanaged { }",
testHost,
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_Type_ExistingInterface(TestHost testHost)
{
await TestAsync("""
interface unmanaged {}
class X<T> where T : unmanaged { }
""",
testHost,
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_Type_ExistingInterfaceButOutOfScope(TestHost testHost)
{
await TestAsync("""
namespace OtherScope
{
interface unmanaged {}
}
class X<T> where T : unmanaged { }
""",
testHost,
Namespace("OtherScope"),
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_Method_Keyword(TestHost testHost)
{
await TestAsync("""
class X
{
void M<T>() where T : unmanaged { }
}
""",
testHost,
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_Method_ExistingInterface(TestHost testHost)
{
await TestAsync("""
interface unmanaged {}
class X
{
void M<T>() where T : unmanaged { }
}
""",
testHost,
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_Method_ExistingInterfaceButOutOfScope(TestHost testHost)
{
await TestAsync("""
namespace OtherScope
{
interface unmanaged {}
}
class X
{
void M<T>() where T : unmanaged { }
}
""",
testHost,
Namespace("OtherScope"),
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_Delegate_Keyword(TestHost testHost)
{
await TestAsync(
"delegate void D<T>() where T : unmanaged;",
testHost,
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_Delegate_ExistingInterface(TestHost testHost)
{
await TestAsync("""
interface unmanaged {}
delegate void D<T>() where T : unmanaged;
""",
testHost,
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_Delegate_ExistingInterfaceButOutOfScope(TestHost testHost)
{
await TestAsync("""
namespace OtherScope
{
interface unmanaged {}
}
delegate void D<T>() where T : unmanaged;
""",
testHost,
Namespace("OtherScope"),
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_LocalFunction_Keyword(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
void M<T>() where T : unmanaged { }
}
}
""",
testHost,
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_LocalFunction_ExistingInterface(TestHost testHost)
{
await TestAsync("""
interface unmanaged {}
class X
{
void N()
{
void M<T>() where T : unmanaged { }
}
}
""",
testHost,
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, CombinatorialData]
public async Task TestUnmanagedConstraint_LocalFunction_ExistingInterfaceButOutOfScope(TestHost testHost)
{
await TestAsync("""
namespace OtherScope
{
interface unmanaged {}
}
class X
{
void N()
{
void M<T>() where T : unmanaged { }
}
}
""",
testHost,
Namespace("OtherScope"),
TypeParameter("T"),
Keyword("unmanaged"));
}
[Theory, WorkItem("https://github.com/dotnet/roslyn/issues/29451")]
[CombinatorialData]
public async Task TestDirectiveStringLiteral(TestHost testHost)
=> await TestInMethodAsync("""
#line 1 "a\b"
""", testHost);
[Theory, WorkItem("https://github.com/dotnet/roslyn/issues/30378")]
[CombinatorialData]
public async Task TestFormatSpecifierInInterpolation(TestHost testHost)
{
await TestInMethodAsync(@"var goo = $""goo{{1:0000}}bar"";",
testHost,
Keyword("var"),
Escape(@"{{"),
Escape(@"}}"));
}
[Theory, WorkItem("https://github.com/dotnet/roslyn/issues/29492")]
[CombinatorialData]
public async Task TestOverloadedOperator_BinaryExpression(TestHost testHost)
{
await TestAsync("""
class C
{
void M()
{
var a = 1 + 1;
var b = new True() + new True();
}
}
class True
{
public static True operator +(True a, True b)
{
return new True();
}
}
""",
testHost,
Keyword("var"),
Keyword("var"),
Class("True"),
OverloadedOperators.Plus,
Class("True"),
Class("True"),
Class("True"),
Class("True"),
Class("True"));
}
[Theory, WorkItem("https://github.com/dotnet/roslyn/issues/29492")]
[CombinatorialData]
public async Task TestOverloadedOperator_PrefixUnaryExpression(TestHost testHost)
{
await TestAsync("""
class C
{
void M()
{
var a = !false;
var b = !new True();
}
}
class True
{
public static bool operator !(True a)
{
return false;
}
}
""",
testHost,
Keyword("var"),
Keyword("var"),
OverloadedOperators.Exclamation,
Class("True"),
Class("True"));
}
[Theory, WorkItem("https://github.com/dotnet/roslyn/issues/29492")]
[CombinatorialData]
public async Task TestOverloadedOperator_PostfixUnaryExpression(TestHost testHost)
{
await TestAsync("""
class C
{
void M()
{
var a = 1;
a++;
var b = new True();
b++;
}
}
class True
{
public static True operator ++(True a)
{
return new True();
}
}
""",
testHost,
Keyword("var"),
Local("a"),
Keyword("var"),
Class("True"),
Local("b"),
OverloadedOperators.PlusPlus,
Class("True"),
Class("True"),
Class("True"));
}
[Theory, WorkItem("https://github.com/dotnet/roslyn/issues/29492")]
[CombinatorialData]
public async Task TestOverloadedOperator_ConditionalExpression(TestHost testHost)
{
await TestAsync("""
class C
{
void M()
{
var a = 1 == 1;
var b = new True() == new True();
}
}
class True
{
public static bool operator ==(True a, True b)
{
return true;
}
}
""",
testHost,
Keyword("var"),
Keyword("var"),
Class("True"),
OverloadedOperators.EqualsEquals,
Class("True"),
Class("True"),
Class("True"));
}
[Theory, CombinatorialData]
public async Task TestCatchDeclarationVariable(TestHost testHost)
{
await TestInMethodAsync("""
try
{
}
catch (Exception ex)
{
throw ex;
}
""",
testHost,
Local("ex"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_InsideMethod(TestHost testHost)
{
// Asserts no Keyword("notnull") because it is an identifier.
await TestInMethodAsync("""
var notnull = 0;
notnull++;
""",
testHost,
Keyword("var"),
Local("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_Type_Keyword(TestHost testHost)
{
await TestAsync(
"class X<T> where T : notnull { }",
testHost,
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_Type_ExistingInterface(TestHost testHost)
{
await TestAsync("""
interface notnull {}
class X<T> where T : notnull { }
""",
testHost,
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_Type_ExistingInterfaceButOutOfScope(TestHost testHost)
{
await TestAsync("""
namespace OtherScope
{
interface notnull {}
}
class X<T> where T : notnull { }
""",
testHost,
Namespace("OtherScope"),
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_Method_Keyword(TestHost testHost)
{
await TestAsync("""
class X
{
void M<T>() where T : notnull { }
}
""",
testHost,
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_Method_ExistingInterface(TestHost testHost)
{
await TestAsync("""
interface notnull {}
class X
{
void M<T>() where T : notnull { }
}
""",
testHost,
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_Method_ExistingInterfaceButOutOfScope(TestHost testHost)
{
await TestAsync("""
namespace OtherScope
{
interface notnull {}
}
class X
{
void M<T>() where T : notnull { }
}
""",
testHost,
Namespace("OtherScope"),
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_Delegate_Keyword(TestHost testHost)
{
await TestAsync(
"delegate void D<T>() where T : notnull;",
testHost,
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_Delegate_ExistingInterface(TestHost testHost)
{
await TestAsync("""
interface notnull {}
delegate void D<T>() where T : notnull;
""",
testHost,
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_Delegate_ExistingInterfaceButOutOfScope(TestHost testHost)
{
await TestAsync("""
namespace OtherScope
{
interface notnull {}
}
delegate void D<T>() where T : notnull;
""",
testHost,
Namespace("OtherScope"),
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_LocalFunction_Keyword(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
void M<T>() where T : notnull { }
}
}
""",
testHost,
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_LocalFunction_ExistingInterface(TestHost testHost)
{
await TestAsync("""
interface notnull {}
class X
{
void N()
{
void M<T>() where T : notnull { }
}
}
""",
testHost,
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task TestNotNullConstraint_LocalFunction_ExistingInterfaceButOutOfScope(TestHost testHost)
{
await TestAsync("""
namespace OtherScope
{
interface notnull {}
}
class X
{
void N()
{
void M<T>() where T : notnull { }
}
}
""",
testHost,
Namespace("OtherScope"),
TypeParameter("T"),
Keyword("notnull"));
}
[Theory, CombinatorialData]
public async Task NonDiscardVariableDeclaration(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
var _ = int.Parse("");
}
}
""",
testHost,
Keyword("var"),
Method("Parse"),
Static("Parse"));
}
[Theory, CombinatorialData]
public async Task NonDiscardVariableDeclarationMultipleDeclarators(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
int i = 1, _ = 1;
int _ = 2, j = 1;
}
}
""", testHost);
}
[Theory, CombinatorialData]
public async Task DiscardAssignment(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
_ = int.Parse("");
}
}
""",
testHost,
Keyword("_"),
Method("Parse"),
Static("Parse"));
}
[Theory, CombinatorialData]
public async Task DiscardInOutDeclaration(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
int.TryParse("", out var _);
}
}
""",
testHost,
Method("TryParse"),
Static("TryParse"),
Keyword("var"),
Keyword("_"));
}
[Theory, CombinatorialData]
public async Task DiscardInOutAssignment(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
int.TryParse("", out _);
}
}
""",
testHost,
Method("TryParse"),
Static("TryParse"),
Keyword("_"));
}
[Theory, CombinatorialData]
public async Task DiscardInDeconstructionAssignment(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
(x, _) = (0, 0);
}
}
""",
testHost,
Keyword("_"));
}
[Theory, CombinatorialData]
public async Task DiscardInDeconstructionDeclaration(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
(int x, int _) = (0, 0);
}
}
""",
testHost,
Keyword("_"));
}
[Theory, CombinatorialData]
public async Task DiscardInPatternMatch(TestHost testHost)
{
await TestAsync("""
class X
{
bool N(object x)
{
return x is int _;
}
}
""",
testHost,
Parameter("x"),
Keyword("_"));
}
[Theory, CombinatorialData]
public async Task DiscardInSwitch(TestHost testHost)
{
await TestAsync("""
class X
{
bool N(object x)
{
switch(x)
{
case int _:
return true;
default:
return false;
}
}
}
""",
testHost,
Parameter("x"),
Keyword("_"));
}
[Theory, CombinatorialData]
public async Task DiscardInSwitchPatternMatch(TestHost testHost)
{
await TestAsync("""
class X
{
bool N(object x)
{
return x switch
{
_ => return true;
};
}
}
""",
testHost,
Parameter("x"),
Keyword("_"));
}
[Theory, CombinatorialData]
public async Task UnusedUnderscoreParameterInLambda(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
System.Func<int, int> a = (int _) => 0;
}
}
""",
testHost,
Namespace("System"),
Delegate("Func"));
}
[Theory, CombinatorialData]
public async Task UsedUnderscoreParameterInLambda(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
System.Func<int, int> a = (int _) => _;
}
}
""",
testHost,
Namespace("System"),
Delegate("Func"),
Parameter("_"));
}
[Theory, CombinatorialData]
public async Task DiscardsInLambda(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
System.Func<int, int, int> a = (int _, int _) => 0;
}
}
""",
testHost,
Namespace("System"),
Delegate("Func"),
Keyword("_"),
Keyword("_"));
}
[Theory, CombinatorialData]
public async Task DiscardsInLambdaWithInferredType(TestHost testHost)
{
await TestAsync("""
class X
{
void N()
{
System.Func<int, int, int> a = (_, _) => 0;
}
}
""",
testHost,
Namespace("System"),
Delegate("Func"),
Keyword("_"),
Keyword("_"));
}
[Theory, CombinatorialData]
public async Task NativeInteger(TestHost testHost)
{
await TestInMethodAsync(
@"nint i = 0; nuint i2 = 0;",
testHost,
Classifications(Keyword("nint"), Keyword("nuint")));
}
[Theory, CombinatorialData]
public async Task NotNativeInteger(TestHost testHost)
{
await TestInMethodAsync(
"nint",
"M",
"nint i = 0;",
testHost,
Classifications(Class("nint")));
}
[Theory, CombinatorialData]
public async Task NotNativeUnsignedInteger(TestHost testHost)
{
await TestInMethodAsync(
"nuint",
"M",
"nuint i = 0;",
testHost,
Classifications(Class("nuint")));
}
[Theory, CombinatorialData]
public async Task StaticBoldingMethodName(TestHost testHost)
{
await TestAsync(
"""
class C
{
public static void Method()
{
System.Action action = Method;
}
}
""",
testHost,
Namespace("System"),
Delegate("Action"),
Method("Method"),
Static("Method"));
}
[Theory, CombinatorialData]
public async Task StaticBoldingMethodNameNestedInNameof(TestHost testHost)
{
await TestAsync(
"""
class C
{
public static void Method()
{
_ = nameof(Method);
}
}
""",
testHost,
Keyword("_"),
Keyword("nameof"),
Static("Method"),
Method("Method"));
}
[Theory, CombinatorialData]
public async Task BoldingMethodNameStaticAndNot(TestHost testHost)
{
await TestAsync(
"""
class C
{
public static void Method()
{
}
public void Method(int x)
{
}
public void Test() {
_ = nameof(Method);
}
}
""",
testHost,
Keyword("_"),
Keyword("nameof"),
Static("Method"),
Method("Method"));
}
[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/46985")]
public async Task BasicRecordClassification(TestHost testHost)
{
await TestAsync(
"""
record R
{
R r;
R() { }
}
""",
testHost,
RecordClass("R"));
}
[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/46985")]
public async Task ParameterizedRecordClassification(TestHost testHost)
{
await TestAsync(
"""
record R(int X, int Y);
class C
{
R r;
}
""",
testHost,
RecordClass("R"));
}
[Theory, CombinatorialData]
public async Task BasicRecordClassClassification(TestHost testHost)
{
await TestAsync(
"""
record class R
{
R r;
R() { }
}
""",
testHost,
RecordClass("R"));
}
[Theory, CombinatorialData]
public async Task BasicRecordStructClassification(TestHost testHost)
{
await TestAsync(
"""
record struct R
{
R property { get; set; }
}
""",
testHost,
RecordStruct("R"));
}
[Theory, CombinatorialData]
public async Task BasicFileScopedNamespaceClassification(TestHost testHost)
{
await TestAsync(
"""
namespace NS;
class C { }
""",
testHost,
Namespace("NS"));
}
[Theory, CombinatorialData]
public async Task NullCheckedParameterClassification(TestHost testHost)
{
await TestAsync(
"""
class C
{
void M(string s!!) { }
}
""",
testHost);
}
[Theory, CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/57184")]
public async Task MethodGroupClassifications(TestHost testHost)
{
await TestAsync(
"""
var f = m;
Delegate d = m;
MulticastDelegate md = m;
ICloneable c = m;
object obj = m;
m(m);
int m(Delegate d) { }
""",
testHost,
Keyword("var"),
Method("m"),
Method("m"),
Method("m"),
Method("m"),
Method("m"),
Method("m"),
Method("m"));
}
/// <seealso cref="SyntacticClassifierTests.LocalFunctionDeclaration"/>
/// <seealso cref="TotalClassifierTests.LocalFunctionDeclarationAndUse"/>
[Theory, CombinatorialData]
public async Task LocalFunctionUse(TestHost testHost)
{
await TestAsync(
"""
using System;
class C
{
void M(Action action)
{
[|localFunction();
staticLocalFunction();
M(localFunction);
M(staticLocalFunction);
void localFunction() { }
static void staticLocalFunction() { }|]
}
}
""",
testHost,
Method("localFunction"),
Method("staticLocalFunction"),
Static("staticLocalFunction"),
Method("M"),
Method("localFunction"),
Method("M"),
Method("staticLocalFunction"),
Static("staticLocalFunction"));
}
[WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/744813")]
public async Task TestCreateWithBufferNotInWorkspace()
{
// don't crash
using var workspace = EditorTestWorkspace.CreateCSharp("");
var document = workspace.CurrentSolution.GetRequiredDocument(workspace.Documents.First().Id);
var contentTypeService = document.GetRequiredLanguageService<IContentTypeLanguageService>();
var contentType = contentTypeService.GetDefaultContentType();
var extraBuffer = workspace.ExportProvider.GetExportedValue<ITextBufferFactoryService>().CreateTextBuffer("", contentType);
WpfTestRunner.RequireWpfFact($"Creates an {nameof(IWpfTextView)} explicitly with an unrelated buffer");
using var disposableView = workspace.ExportProvider.GetExportedValue<ITextEditorFactoryService>().CreateDisposableTextView(extraBuffer);
var listenerProvider = workspace.ExportProvider.GetExportedValue<IAsynchronousOperationListenerProvider>();
var globalOptions = workspace.ExportProvider.GetExportedValue<IGlobalOptionService>();
var provider = new SemanticClassificationViewTaggerProvider(
workspace.GetService<TaggerHost>(),
workspace.GetService<ClassificationTypeMap>());
using var tagger = provider.CreateTagger(disposableView.TextView, extraBuffer);
using (var edit = extraBuffer.CreateEdit())
{
edit.Insert(0, "class A { }");
edit.Apply();
}
var waiter = listenerProvider.GetWaiter(FeatureAttribute.Classification);
await waiter.ExpeditedWaitAsync();
}
}
|