From d9ad24a6d5719a1556f3ee2c16a9103cf36ab192 Mon Sep 17 00:00:00 2001 From: Fred Silberberg Date: Wed, 29 Dec 2021 22:15:44 -0800 Subject: [PATCH 1/2] Update language feature status (#58525) --- docs/Language Feature Status.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 003bb89aa1434..15b16f46a7454 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -18,7 +18,7 @@ efforts behind them. | [Relax ordering of `ref` and `partial` modifiers](https://github.com/dotnet/csharplang/issues/946) | [ref-partial](https://github.com/dotnet/roslyn/tree/features/ref-partial) | In Progress | [alrz](https://github.com/alrz) | [gafter](https://github.com/gafter) | [jcouv](https://github.com/jcouv) | | [Generic attributes](https://github.com/dotnet/csharplang/issues/124) | [generic-attributes](https://github.com/dotnet/roslyn/tree/features/generic-attributes) | [Merged into 17.0p4 (preview langver)](https://github.com/dotnet/roslyn/issues/36285) | [AviAvni](https://github.com/AviAvni) | [RikkiGibson](https://github.com/RikkiGibson), [jcouv](https://github.com/jcouv) | [mattwar](https://github.com/mattwar) | | [Default in deconstruction](https://github.com/dotnet/roslyn/pull/25562) | [decon-default](https://github.com/dotnet/roslyn/tree/features/decon-default) | [Implemented](https://github.com/dotnet/roslyn/issues/25559) | [jcouv](https://github.com/jcouv) | [gafter](https://github.com/gafter) | [jcouv](https://github.com/jcouv) | -| [Semi-auto-properties](https://github.com/dotnet/csharplang/issues/140) | [semi-auto-properties](https://github.com/dotnet/roslyn/tree/features/features/semi-auto-properties) | [In Progress](https://github.com/dotnet/roslyn/issues/57012) | [Youssef1313](https://github.com/Youssef1313) | TBD | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | +| [Semi-auto-properties](https://github.com/dotnet/csharplang/issues/140) | [semi-auto-props](https://github.com/dotnet/roslyn/tree/features/semi-auto-props) | [In Progress](https://github.com/dotnet/roslyn/issues/57012) | [Youssef1313](https://github.com/Youssef1313) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [Required members](https://github.com/dotnet/csharplang/issues/3630) | [required-members](https://github.com/dotnet/roslyn/tree/features/required-members) | [In Progress](https://github.com/dotnet/roslyn/issues/57046) | [333fred](https://github.com/333fred) | [jcouv](https://github.com/jcouv), [RikkiGibson](https://github.com/RikkiGibson) | [333fred](https://github.com/333fred) | | [Top Level statement attribute specifiers](https://github.com/dotnet/csharplang/issues/5045) | [main-attributes](https://github.com/dotnet/roslyn/tree/features/features/main-attributes) | [In Progress](https://github.com/dotnet/roslyn/issues/57047) | [chsienki](https://github.com/chsienki) | TBD | [jaredpar](https://github.com/jaredpar) | | [Primary Constructors](https://github.com/dotnet/csharplang/issues/2691) | [primary-constructors](https://github.com/dotnet/roslyn/tree/features/features/primary-constructors) | [In Progress](https://github.com/dotnet/roslyn/issues/57048) | TBD | TBD | [MadsTorgersen](https://github.com/MadsTorgersen) | From cb400a925dc235b505ee2163dc1501d3433d2a12 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 2 Jan 2022 02:10:12 +0800 Subject: [PATCH 2/2] Visit anonymous type and optionally custom modifiers in VisitType. (#58541) --- .../Portable/Symbols/TypeSymbolExtensions.cs | 115 ++++++++--- .../Symbol/Symbols/SymbolExtensionTests.cs | 193 ++++++++++++++++++ 2 files changed, 281 insertions(+), 27 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 6b0f2459f47c4..a83da25243d79 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -637,7 +637,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo this TypeSymbol type, Func predicate, T arg, - bool canDigThroughNullable = false) + bool canDigThroughNullable = false, + bool visitCustomModifiers = false) { return VisitType( typeWithAnnotationsOpt: default, @@ -645,7 +646,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo typeWithAnnotationsPredicate: null, typePredicate: predicate, arg, - canDigThroughNullable); + canDigThroughNullable, + visitCustomModifiers: visitCustomModifiers); } /// @@ -666,10 +668,13 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo Func? typePredicate, T arg, bool canDigThroughNullable = false, - bool useDefaultType = false) + bool useDefaultType = false, + bool visitCustomModifiers = false) { RoslynDebug.Assert(typeWithAnnotationsOpt.HasType == (type is null)); RoslynDebug.Assert(canDigThroughNullable == false || useDefaultType == false, "digging through nullable will cause early resolution of nullable types"); + RoslynDebug.Assert(canDigThroughNullable == false || visitCustomModifiers == false); + RoslynDebug.Assert(visitCustomModifiers == false || typePredicate is { }); // In order to handle extremely "deep" types like "int[][][][][][][][][]...[]" // or int*****************...* we implement manual tail recursion rather than @@ -693,7 +698,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo if ((object)containingType != null) { isNestedNamedType = true; - var result = VisitType(default, containingType, typeWithAnnotationsPredicate, typePredicate, arg, canDigThroughNullable, useDefaultType); + var result = VisitType(default, containingType, typeWithAnnotationsPredicate, typePredicate, arg, canDigThroughNullable, useDefaultType, visitCustomModifiers); if (result is object) { return result; @@ -722,6 +727,21 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo } } + if (visitCustomModifiers && typeWithAnnotationsOpt.HasType) + { + foreach (var customModifier in typeWithAnnotationsOpt.CustomModifiers) + { + var result = VisitType( + typeWithAnnotationsOpt: default, type: ((CSharpCustomModifier)customModifier).ModifierSymbol, + typeWithAnnotationsPredicate, typePredicate, arg, + canDigThroughNullable, useDefaultType, visitCustomModifiers); + if (result is object) + { + return result; + } + } + } + TypeWithAnnotations next; switch (current.TypeKind) @@ -737,32 +757,71 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo case TypeKind.Struct: case TypeKind.Interface: case TypeKind.Delegate: - var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; - if (typeArguments.IsEmpty) + + if (current.IsAnonymousType) { - return null; - } + var anonymous = (AnonymousTypeManager.AnonymousTypeOrDelegatePublicSymbol)current; + var fields = anonymous.TypeDescriptor.Fields; + + if (fields.IsEmpty) + { + return null; + } + + int i; + for (i = 0; i < fields.Length - 1; i++) + { + // Let's try to avoid early resolution of nullable types + (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(fields[i].TypeWithAnnotations, canDigThroughNullable); + var result = VisitType( + typeWithAnnotationsOpt: nextTypeWithAnnotations, + type: nextType, + typeWithAnnotationsPredicate, + typePredicate, + arg, + canDigThroughNullable, + useDefaultType, + visitCustomModifiers); + if (result is object) + { + return result; + } + } - int i; - for (i = 0; i < typeArguments.Length - 1; i++) + next = fields[i].TypeWithAnnotations; + } + else { - // Let's try to avoid early resolution of nullable types - (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typeArguments[i], canDigThroughNullable); - var result = VisitType( - typeWithAnnotationsOpt: nextTypeWithAnnotations, - type: nextType, - typeWithAnnotationsPredicate, - typePredicate, - arg, - canDigThroughNullable, - useDefaultType); - if (result is object) + var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + + if (typeArguments.IsEmpty) { - return result; + return null; } + + int i; + for (i = 0; i < typeArguments.Length - 1; i++) + { + // Let's try to avoid early resolution of nullable types + (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typeArguments[i], canDigThroughNullable); + var result = VisitType( + typeWithAnnotationsOpt: nextTypeWithAnnotations, + type: nextType, + typeWithAnnotationsPredicate, + typePredicate, + arg, + canDigThroughNullable, + useDefaultType, + visitCustomModifiers); + if (result is object) + { + return result; + } + } + + next = typeArguments[i]; } - next = typeArguments[i]; break; case TypeKind.Array: @@ -775,7 +834,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo case TypeKind.FunctionPointer: { - var result = visitFunctionPointerType((FunctionPointerTypeSymbol)current, typeWithAnnotationsPredicate, typePredicate, arg, useDefaultType, canDigThroughNullable, out next); + var result = visitFunctionPointerType((FunctionPointerTypeSymbol)current, typeWithAnnotationsPredicate, typePredicate, arg, useDefaultType, canDigThroughNullable, visitCustomModifiers, out next); if (result is object) { return result; @@ -796,7 +855,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo static (TypeWithAnnotations, TypeSymbol?) getNextIterationElements(TypeWithAnnotations type, bool canDigThroughNullable) => canDigThroughNullable ? (default(TypeWithAnnotations), type.NullableUnderlyingTypeOrSelf) : (type, null); - static TypeSymbol? visitFunctionPointerType(FunctionPointerTypeSymbol type, Func? typeWithAnnotationsPredicate, Func? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable, out TypeWithAnnotations next) + static TypeSymbol? visitFunctionPointerType(FunctionPointerTypeSymbol type, Func? typeWithAnnotationsPredicate, Func? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable, bool visitCustomModifiers, out TypeWithAnnotations next) { MethodSymbol currentPointer = type.Signature; if (currentPointer.ParameterCount == 0) @@ -812,7 +871,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo typePredicate, arg, canDigThroughNullable, - useDefaultType); + useDefaultType, + visitCustomModifiers); if (result is object) { next = default; @@ -830,7 +890,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo typePredicate, arg, canDigThroughNullable, - useDefaultType); + useDefaultType, + visitCustomModifiers); if (result is object) { next = default; diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs index 6c032e4930553..8cd545b5dd241 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs @@ -5,9 +5,12 @@ #nullable disable using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Test.Utilities; using System; +using System.Linq; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols @@ -84,6 +87,196 @@ class C { } HasNameQualifierCore(namespaceNames, compilation.GetMember("NB.C"), "NB"); } + [Fact] + public void VisitType_AnonymousDelegate() + { + var source = @" +void F(T t) +{ + var f = (ref T x) => 0; +} +"; + var compilation = CreateCompilation(source, options: TestOptions.DebugDll); + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes().OfType().First(id => id.Identifier.Text == "var"); + var model = compilation.GetSemanticModel(tree); + var anonymousType = model.GetSymbolInfo(identifier).Symbol.GetSymbol(); + + Assert.True(anonymousType.ContainsTypeParameter()); + } + + [Fact] + public void VisitType_AnonymousDelegateWithAnonymousClass() + { + var source = @" +void F(T t) +{ + var f = (ref int x) => new { t }; +} +"; + var compilation = CreateCompilation(source, options: TestOptions.DebugDll); + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes().OfType().First(id => id.Identifier.Text == "var"); + var model = compilation.GetSemanticModel(tree); + var anonymousType = model.GetSymbolInfo(identifier).Symbol.GetSymbol(); + + Assert.True(anonymousType.ContainsTypeParameter()); + } + + [Fact] + public void VisitType_AnonymousClass() + { + var source = @" +void F(T t) +{ + var f = new { t }; +} +"; + var compilation = CreateCompilation(source, options: TestOptions.DebugDll); + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes().OfType().First(id => id.Identifier.Text == "var"); + var model = compilation.GetSemanticModel(tree); + var anonymousType = model.GetSymbolInfo(identifier).Symbol.GetSymbol(); + + Assert.True(anonymousType.ContainsTypeParameter()); + } + + [Fact] + public void VisitType_AnonymousClassWithAnonymousDelegate() + { + var source = @" +void F(T t) +{ + var f = (ref int x) => t; + var g = new { f }; +} +"; + var compilation = CreateCompilation(source, options: TestOptions.DebugDll); + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes().OfType().Last(id => id.Identifier.Text == "var"); + var model = compilation.GetSemanticModel(tree); + var anonymousType = model.GetSymbolInfo(identifier).Symbol.GetSymbol(); + + Assert.True(anonymousType.ContainsTypeParameter()); + } + + [Fact] + public void VisitType_CustomModifiers() + { + var ilSource = @" +.class public auto ansi beforefieldinit C1`1 + extends System.Object +{ + // Methods + .method public hidebysig static + string Method () cil managed + { + // Method begins at RVA 0x2050 + // Code size 6 (0x6) + .maxstack 8 + + IL_0000: ldstr ""Method"" + IL_0005: ret + } // end of method C1`1::Method + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2057 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } // end of method C1`1::.ctor + +} // end of class C1`1 + +.class public auto ansi beforefieldinit C2`1 + extends System.Object +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2057 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } // end of method C2`1::.ctor + +} // end of class C2`1 + +.class public auto ansi beforefieldinit C3`1 + extends class C1`1)> +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x205f + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void class C1`1::.ctor() + IL_0006: ret + } // end of method C3`1::.ctor + +} // end of class C3`1 +"; + + var source = @" +class Test +{ + static void Main() + { + M(); + } + + static void M() + { + System.Func x = C3.Method; + System.Console.WriteLine(x()); + } +} +"; + var compilation = CreateCompilationWithIL(source, ilSource, options: TestOptions.ReleaseExe); + + var tree = compilation.SyntaxTrees.Single(); + var model = compilation.GetSemanticModel(tree); + var method = model.GetSymbolInfo(tree.GetRoot().DescendantNodes().OfType().Where(id => id.Identifier.ValueText == "Method").Single()).Symbol.GetSymbol(); + + AssertEx.Equal("System.String C1)>.Method()", method.ToTestDisplayString()); + + var typeParameters = PooledHashSet.GetInstance(); + try + { + method.ContainingType.VisitType(static (typeSymbol, typeParameters, _) => + { + if (typeSymbol is TypeParameterSymbol typeParameter) + { + typeParameters.Add(typeParameter); + } + + return false; + }, + typeParameters, visitCustomModifiers: true); + + var typeParameter = typeParameters.Single(); + Assert.Equal("G", typeParameter.Name); + Assert.Equal("M", typeParameter.ContainingSymbol.Name); + } + finally + { + typeParameters.Free(); + } + } + private void HasNameQualifierCore(string[] namespaceNames, NamedTypeSymbol type, string expectedName) { Assert.True(Array.IndexOf(namespaceNames, expectedName) >= 0);