diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 39595af6107e902cfd34cd42a91bf4672023706d..eb9d2637297a4fbb61da66ed4189ebb273d0fa99 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -77,11 +77,60 @@ public override void VisitField(IFieldSymbol symbol) } } + private static bool ShouldPropertyDisplayReadOnly(IPropertySymbol property) + { + if (property.ContainingType?.IsReadOnly == true) + { + return false; + } + + // If at least one accessor is present and all present accessors are readonly, the property should be marked readonly. + + var getMethod = property.GetMethod; + if (getMethod is object && !ShouldMethodDisplayReadOnly(getMethod, property)) + { + return false; + } + + var setMethod = property.SetMethod; + if (setMethod is object && !ShouldMethodDisplayReadOnly(setMethod, property)) + { + return false; + } + + return getMethod is object || setMethod is object; + } + + private static bool ShouldMethodDisplayReadOnly(IMethodSymbol method, IPropertySymbol propertyOpt = null) + { + if (method.ContainingType?.IsReadOnly == true) + { + return false; + } + + if (method is SourcePropertyAccessorSymbol sourceAccessor && propertyOpt is SourcePropertySymbol sourceProperty) + { + // only display if the accessor is explicitly readonly + return sourceAccessor.LocalDeclaredReadOnly || sourceProperty.HasReadOnlyModifier; + } + else if (method is MethodSymbol m) + { + return m.IsDeclaredReadOnly; + } + + return false; + } + public override void VisitProperty(IPropertySymbol symbol) { AddAccessibilityIfRequired(symbol); AddMemberModifiersIfRequired(symbol); + if (ShouldPropertyDisplayReadOnly(symbol)) + { + AddReadOnlyIfRequired(); + } + if (format.MemberOptions.IncludesOption(SymbolDisplayMemberOptions.IncludeType)) { if (symbol.ReturnsByRef) @@ -168,6 +217,12 @@ public override void VisitEvent(IEventSymbol symbol) AddAccessibilityIfRequired(symbol); AddMemberModifiersIfRequired(symbol); + var accessor = symbol.AddMethod ?? symbol.RemoveMethod; + if (accessor is object && ShouldMethodDisplayReadOnly(accessor)) + { + AddReadOnlyIfRequired(); + } + if (format.KindOptions.IncludesOption(SymbolDisplayKindOptions.IncludeMemberKeyword)) { AddKeyword(SyntaxKind.EventKeyword); @@ -257,6 +312,11 @@ public override void VisitMethod(IMethodSymbol symbol) AddAccessibilityIfRequired(symbol); AddMemberModifiersIfRequired(symbol); + if (ShouldMethodDisplayReadOnly(symbol)) + { + AddReadOnlyIfRequired(); + } + if (format.MemberOptions.IncludesOption(SymbolDisplayMemberOptions.IncludeType)) { switch (symbol.MethodKind) @@ -753,7 +813,7 @@ private void AddParametersIfRequired(bool hasThisParameter, bool isVarargs, Immu } } - private void AddAccessor(ISymbol property, IMethodSymbol method, SyntaxKind keyword) + private void AddAccessor(IPropertySymbol property, IMethodSymbol method, SyntaxKind keyword) { if (method != null) { @@ -763,6 +823,11 @@ private void AddAccessor(ISymbol property, IMethodSymbol method, SyntaxKind keyw AddAccessibility(method); } + if (!ShouldPropertyDisplayReadOnly(property) && ShouldMethodDisplayReadOnly(method, property)) + { + AddReadOnlyIfRequired(); + } + AddKeyword(keyword); AddPunctuation(SyntaxKind.SemicolonToken); } @@ -829,6 +894,17 @@ private void AddRefReadonlyIfRequired() } } + private void AddReadOnlyIfRequired() + { + // 'readonly' in this context is effectively a 'ref' modifier + // because it affects whether the 'this' parameter is 'ref' or 'in'. + if (format.MemberOptions.IncludesOption(SymbolDisplayMemberOptions.IncludeRef)) + { + AddKeyword(SyntaxKind.ReadOnlyKeyword); + AddSpace(); + } + } + private void AddParameterRefKindIfRequired(RefKind refKind) { if (format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeParamsRefOut)) diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs index f6a7dead0898bb6183268d2ae7891b5aed4acfa4..d22424d7e00c6f1d520c168e38a9c8bd4178c71e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs @@ -117,7 +117,8 @@ public sealed override ImmutableArray ExplicitInterfaceImplementat get { return ImmutableArray.Empty; } } - internal sealed override bool IsDeclaredReadOnly => true; + // methods on classes are never 'readonly' + internal sealed override bool IsDeclaredReadOnly => false; public sealed override ImmutableArray RefCustomModifiers { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs index dea274abaf359e1cd3b6205b91915f08df79eea5..8a90006b5b02c8f5d70f4bc0cd6e41a68f6ef645 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedIntrinsicOperatorSymbol.cs @@ -261,7 +261,8 @@ public override ImmutableArray ExplicitInterfaceImplementations } } - internal override bool IsDeclaredReadOnly => true; + // operators are never 'readonly' because there is no 'this' parameter + internal override bool IsDeclaredReadOnly => false; public override ImmutableArray RefCustomModifiers { diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConstructorInitTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConstructorInitTests.cs index 1abc61e566a23bcf8d3e824e830f5e53265ef5c5..26f4333b8974d5ac62c042a852be53dee52e27da 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConstructorInitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConstructorInitTests.cs @@ -367,7 +367,7 @@ .maxstack 2 IL_0003: stfld ""int S.k__BackingField"" IL_0008: ldarg.0 IL_0009: ldarg.0 - IL_000a: call ""int S.X.get"" + IL_000a: call ""readonly int S.X.get"" IL_000f: stfld ""int S.k__BackingField"" IL_0014: ret } @@ -408,11 +408,11 @@ .maxstack 2 IL_0003: stfld ""int C.k__BackingField"" IL_0008: ldarg.0 IL_0009: ldarg.0 - IL_000a: call ""int C.I.get"" + IL_000a: call ""readonly int C.I.get"" IL_000f: call ""void C.J.set"" IL_0014: ldarg.0 IL_0015: ldarg.0 - IL_0016: call ""int C.J.get"" + IL_0016: call ""readonly int C.J.get"" IL_001b: stfld ""int C.k__BackingField"" IL_0020: ldarg.0 IL_0021: ldarg.1 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs index 4349f6b55fd0dab86459cf6fea0232072d142699..a85a3ca2bfa9329a930a6e76e73c427904d0079d 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenReadonlyStructTests.cs @@ -1856,7 +1856,7 @@ .maxstack 1 .locals init (S V_0, //copy S V_1) IL_0000: ldarg.0 - IL_0001: call ""void S.M1()"" + IL_0001: call ""readonly void S.M1()"" IL_0006: ldarg.0 IL_0007: ldfld ""int S.i"" IL_000c: call ""void System.Console.Write(int)"" @@ -1932,7 +1932,7 @@ .maxstack 1 .locals init (S V_0, //copy S V_1) IL_0000: ldarg.0 - IL_0001: call ""int S.P1.get"" + IL_0001: call ""readonly int S.P1.get"" IL_0006: pop IL_0007: ldarg.0 IL_0008: ldfld ""int S.i"" @@ -1976,7 +1976,7 @@ public struct S // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 - IL_0001: call ""int S.M2()"" + IL_0001: call ""readonly int S.M2()"" IL_0006: ldc.i4.1 IL_0007: add IL_0008: ret @@ -2002,7 +2002,7 @@ public struct S // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 - IL_0001: call ""int S.P2.get"" + IL_0001: call ""readonly int S.P2.get"" IL_0006: ldc.i4.1 IL_0007: add IL_0008: ret @@ -2027,7 +2027,7 @@ public struct S // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 - IL_0001: call ""int S.P2.get"" + IL_0001: call ""readonly int S.P2.get"" IL_0006: ldc.i4.1 IL_0007: add IL_0008: ret @@ -2053,7 +2053,7 @@ public struct S // Code size 9 (0x9) .maxstack 2 IL_0000: ldarg.0 - IL_0001: call ""int S.M2()"" + IL_0001: call ""readonly int S.M2()"" IL_0006: ldc.i4.1 IL_0007: add IL_0008: ret @@ -2089,7 +2089,7 @@ public struct S2 .maxstack 1 IL_0000: ldarg.0 IL_0001: ldflda ""S1 S2.s1"" - IL_0006: call ""void S1.M1()"" + IL_0006: call ""readonly void S1.M1()"" IL_000b: ret }"); @@ -2465,17 +2465,17 @@ public struct S // Code size 99 (0x63) .maxstack 2 IL_0000: ldarg.0 - IL_0001: call ""System.Type S.GetType()"" + IL_0001: call ""readonly System.Type S.GetType()"" IL_0006: pop IL_0007: ldarg.0 - IL_0008: call ""string S.ToString()"" + IL_0008: call ""readonly string S.ToString()"" IL_000d: pop IL_000e: ldarg.0 - IL_000f: call ""int S.GetHashCode()"" + IL_000f: call ""readonly int S.GetHashCode()"" IL_0014: pop IL_0015: ldarg.0 IL_0016: ldnull - IL_0017: call ""bool S.Equals(object)"" + IL_0017: call ""readonly bool S.Equals(object)"" IL_001c: pop IL_001d: ldarg.0 IL_001e: ldobj ""S"" diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs index da94d5c26873496067cd17185d224dbe57595f70..aac0bb5c4f6011b385e88001aed9c35d30c7125b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenShortCircuitOperatorTests.cs @@ -3704,7 +3704,7 @@ .locals init (Program.C1 V_0) IL_000e: call ""Program.C1 Program.C1?.GetValueOrDefault()"" IL_0013: stloc.0 IL_0014: ldloca.s V_0 - IL_0016: call ""int Program.C1.x.get"" + IL_0016: call ""readonly int Program.C1.x.get"" IL_001b: ret } ").VerifyIL("Program.Test2(Program.C1?)", @" @@ -3725,7 +3725,7 @@ .maxstack 1 IL_0016: call ""Program.C1 Program.C1?.GetValueOrDefault()"" IL_001b: stloc.2 IL_001c: ldloca.s V_2 - IL_001e: call ""int? Program.C1.y.get"" + IL_001e: call ""readonly int? Program.C1.y.get"" IL_0023: stloc.0 IL_0024: ldloca.s V_0 IL_0026: call ""bool int?.HasValue.get"" @@ -3793,7 +3793,7 @@ .locals init (Program.C1 V_0) IL_000d: call ""Program.C1 Program.C1?.GetValueOrDefault()"" IL_0012: stloc.0 IL_0013: ldloca.s V_0 - IL_0015: call ""int Program.C1.x.get"" + IL_0015: call ""readonly int Program.C1.x.get"" IL_001a: ret } @@ -3816,7 +3816,7 @@ .maxstack 2 IL_0015: call ""Program.C1 Program.C1?.GetValueOrDefault()"" IL_001a: stloc.2 IL_001b: ldloca.s V_2 - IL_001d: call ""int? Program.C1.y.get"" + IL_001d: call ""readonly int? Program.C1.y.get"" IL_0022: stloc.0 IL_0023: ldloca.s V_0 IL_0025: call ""bool int?.HasValue.get"" diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index c848ee4c96a575aaefee55fd2efd6e6fc404a1ee..9d35a939adcf60e56a19c2bd2e9b2296a4a1c4d2 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -14456,8 +14456,8 @@ public override string ToString() "(System.Int32, System.Int32)..ctor(System.Int32 item1, System.Int32 item2)", "System.String (System.Int32, System.Int32).ToString()", "System.Int32 (System.Int32, System.Int32).k__BackingField", - "System.Int32 (System.Int32, System.Int32).P1 { get; set; }", - "System.Int32 (System.Int32, System.Int32).P1.get", + "System.Int32 (System.Int32, System.Int32).P1 { readonly get; set; }", + "readonly System.Int32 (System.Int32, System.Int32).P1.get", "void (System.Int32, System.Int32).P1.set", "System.Int32 (System.Int32, System.Int32).this[System.Int32 a, System.Int32 b] { get; }", "System.Int32 (System.Int32, System.Int32).this[System.Int32 a, System.Int32 b].get", @@ -14470,7 +14470,7 @@ public override string ToString() m1Tuple.MemberNames.ToArray()); Assert.Equal(m1Tuple.TupleUnderlyingType.GetEarlyAttributeDecodingMembers().Select(m => m.Name).ToArray(), m1Tuple.GetEarlyAttributeDecodingMembers().Select(m => m.Name).ToArray()); - Assert.Equal("System.Int32 (System.Int32, System.Int32).P1 { get; set; }", m1Tuple.GetEarlyAttributeDecodingMembers("P1").Single().ToTestDisplayString()); + Assert.Equal("System.Int32 (System.Int32, System.Int32).P1 { readonly get; set; }", m1Tuple.GetEarlyAttributeDecodingMembers("P1").Single().ToTestDisplayString()); var m2Tuple = (NamedTypeSymbol)c.GetMember("M2").ReturnType; var m3Tuple = (NamedTypeSymbol)c.GetMember("M3").ReturnType; @@ -14486,7 +14486,7 @@ public override string ToString() Assert.Equal(SymbolKind.Property, m1P1.Kind); Assert.Same(m1P1, m1P1.OriginalDefinition); Assert.True(m1P1.Equals(m1P1)); - Assert.Equal("System.Int32 System.ValueTuple.P1 { get; set; }", m1P1.TupleUnderlyingProperty.ToTestDisplayString()); + Assert.Equal("System.Int32 System.ValueTuple.P1 { readonly get; set; }", m1P1.TupleUnderlyingProperty.ToTestDisplayString()); Assert.Same(m1Tuple, m1P1.ContainingSymbol); Assert.Same(m1Tuple.TupleUnderlyingType, m1P1.TupleUnderlyingProperty.ContainingSymbol); Assert.True(m1P1.TypeWithAnnotations.CustomModifiers.IsEmpty); diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/ObjectAndCollectionInitializerTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/ObjectAndCollectionInitializerTests.cs index 72d3cdeec203c6b36bd12598c652905614118026..e1a1c6ee26f7cdedd9eb662afde987061131e91d 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/ObjectAndCollectionInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/ObjectAndCollectionInitializerTests.cs @@ -81,7 +81,7 @@ public static void Main() // Code size 50 (0x32) .maxstack 2 .locals init (MemberInitializerTest V_0, //i - MemberInitializerTest V_1) + MemberInitializerTest V_1) IL_0000: ldloca.s V_1 IL_0002: initobj ""MemberInitializerTest"" IL_0008: ldloca.s V_1 @@ -96,7 +96,7 @@ .maxstack 2 IL_001b: ldfld ""int MemberInitializerTest.x"" IL_0020: call ""void System.Console.WriteLine(int)"" IL_0025: ldloca.s V_0 - IL_0027: call ""int MemberInitializerTest.y.get"" + IL_0027: call ""readonly int MemberInitializerTest.y.get"" IL_002c: call ""void System.Console.WriteLine(int)"" IL_0031: ret }"); diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/UnsafeTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/UnsafeTests.cs index fda7915cec14ed3aa40b7d6ec35b1f23f2a10588..39a975b6c777db35308bfe0fd09243df7c4530cf 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/UnsafeTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/UnsafeTests.cs @@ -7583,13 +7583,13 @@ static int GetIndex() // Code size 74 (0x4a) .maxstack 3 .locals init (S V_0, //s - S* V_1, - int V_2) + S* V_1, + int V_2) IL_0000: ldloca.s V_0 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: dup - IL_000b: call ""S* S.P.get"" + IL_000b: call ""readonly S* S.P.get"" IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: sizeof ""S"" @@ -7608,7 +7608,7 @@ .maxstack 3 IL_0036: ldloc.1 IL_0037: call ""void S.this[int].set"" IL_003c: ldloca.s V_0 - IL_003e: call ""S* S.P.get"" + IL_003e: call ""readonly S* S.P.get"" IL_0043: conv.i4 IL_0044: call ""void System.Console.Write(int)"" IL_0049: ret @@ -7764,7 +7764,7 @@ .maxstack 5 IL_0002: initobj ""S"" IL_0008: ldloca.s V_0 IL_000a: dup - IL_000b: call ""S* S.P.get"" + IL_000b: call ""readonly S* S.P.get"" IL_0010: ldc.i4.3 IL_0011: conv.i IL_0012: sizeof ""S"" @@ -7787,7 +7787,7 @@ .maxstack 5 IL_003a: sub IL_003b: call ""void S.this[int].set"" IL_0040: ldloca.s V_0 - IL_0042: call ""S* S.P.get"" + IL_0042: call ""readonly S* S.P.get"" IL_0047: conv.i4 IL_0048: call ""void System.Console.Write(int)"" IL_004d: ret diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index fb7fde41eda2db536b4a2fd1f3852461040e1775..967bd251b68a8923b9a4e7104748f6c5b8ecebd5 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -6819,6 +6819,542 @@ namespace Nested SymbolDisplayPartKind.StructName); } + [Fact] + public void TestReadOnlyMembers_Malformed() + { + var source = @" +struct X +{ + int P1 { } + readonly int P2 { } + readonly event System.Action E1 { } + readonly event System.Action E2 { remove { } } +} +"; + var format = SymbolDisplayFormat.TestFormat + .AddMemberOptions(SymbolDisplayMemberOptions.IncludeModifiers) + .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp = CreateCompilation(source).VerifyDiagnostics( + // (4,9): error CS0548: 'X.P1': property or indexer must have at least one accessor + // int P1 { } + Diagnostic(ErrorCode.ERR_PropertyWithNoAccessors, "P1").WithArguments("X.P1").WithLocation(4, 9), + // (5,18): error CS0548: 'X.P2': property or indexer must have at least one accessor + // readonly int P2 { } + Diagnostic(ErrorCode.ERR_PropertyWithNoAccessors, "P2").WithArguments("X.P2").WithLocation(5, 18), + // (6,34): error CS0065: 'X.E1': event property must have both add and remove accessors + // readonly event System.Action E1 { } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "E1").WithArguments("X.E1").WithLocation(6, 34), + // (7,34): error CS0065: 'X.E2': event property must have both add and remove accessors + // readonly event System.Action E2 { remove { } } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "E2").WithArguments("X.E2").WithLocation(7, 34)); + + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + + var declaration = (BaseTypeDeclarationSyntax)semanticModel.SyntaxTree.GetRoot().DescendantNodes().Single(n => n.Kind() == SyntaxKind.StructDeclaration); + var members = semanticModel.GetDeclaredSymbol(declaration).GetMembers(); + + Verify(members[0].ToDisplayParts(format), "int X.P1 { }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[1].ToDisplayParts(format), "int X.P2 { }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[2].ToDisplayParts(format), "event System.Action X.E1", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.DelegateName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.EventName); + + Verify(members[3].ToDisplayParts(format), "readonly event System.Action X.E2", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.DelegateName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.EventName); + } + + [Fact] + public void TestReadOnlyMembers() + { + var source = @" +struct X +{ + readonly void M() { } + readonly int P1 { get => 123; } + readonly int P2 { set {} } + readonly int P3 { get => 123; set {} } + int P4 { readonly get => 123; set {} } + int P5 { get => 123; readonly set {} } + readonly event System.Action E { add {} remove {} } +} +"; + var format = SymbolDisplayFormat.TestFormat + .AddMemberOptions(SymbolDisplayMemberOptions.IncludeModifiers) + .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp = CreateCompilation(source).VerifyDiagnostics(); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + + var declaration = (BaseTypeDeclarationSyntax)semanticModel.SyntaxTree.GetRoot().DescendantNodes().Single(n => n.Kind() == SyntaxKind.StructDeclaration); + var members = semanticModel.GetDeclaredSymbol(declaration).GetMembers(); + + Verify(members[0].ToDisplayParts(format), + "readonly void X.M()", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.MethodName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Punctuation); + + Verify(members[1].ToDisplayParts(format), + "readonly int X.P1 { get; }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[2].ToDisplayParts(format), + "readonly int X.P1.get", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[3].ToDisplayParts(format), + "readonly int X.P2 { set; }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[4].ToDisplayParts(format), + "readonly void X.P2.set", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[5].ToDisplayParts(format), + "readonly int X.P3 { get; set; }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[6].ToDisplayParts(format), + "readonly int X.P3.get", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[7].ToDisplayParts(format), + "readonly void X.P3.set", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[8].ToDisplayParts(format), + "int X.P4 { readonly get; set; }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[9].ToDisplayParts(format), + "readonly int X.P4.get", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[10].ToDisplayParts(format), + "void X.P4.set", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[11].ToDisplayParts(format), + "int X.P5 { get; readonly set; }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[12].ToDisplayParts(format), + "int X.P5.get", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[13].ToDisplayParts(format), + "readonly void X.P5.set", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[14].ToDisplayParts(format), + "readonly event System.Action X.E", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.DelegateName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.EventName); + + Verify(members[15].ToDisplayParts(format), + "readonly void X.E.add", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.EventName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[16].ToDisplayParts(format), + "readonly void X.E.remove", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.EventName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + } + + [Fact] + public void TestReadOnlyStruct_Members() + { + var source = @" +readonly struct X +{ + void M() { } + int P1 { get => 123; } + int P2 { set {} } + int P3 { get => 123; readonly set {} } + event System.Action E { add {} remove {} } +} +"; + var format = SymbolDisplayFormat.TestFormat + .AddMemberOptions(SymbolDisplayMemberOptions.IncludeModifiers) + .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp = CreateCompilation(source).VerifyDiagnostics(); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + + var declaration = (BaseTypeDeclarationSyntax)semanticModel.SyntaxTree.GetRoot().DescendantNodes().Single(n => n.Kind() == SyntaxKind.StructDeclaration); + var members = semanticModel.GetDeclaredSymbol(declaration).GetMembers(); + + Verify(members[0].ToDisplayParts(format), + "void X.M()", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.MethodName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Punctuation); + + Verify(members[1].ToDisplayParts(format), + "int X.P1 { get; }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[2].ToDisplayParts(format), + "int X.P1.get", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[3].ToDisplayParts(format), + "int X.P2 { set; }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[4].ToDisplayParts(format), + "void X.P2.set", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[5].ToDisplayParts(format), + "int X.P3 { get; set; }", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Punctuation); + + Verify(members[6].ToDisplayParts(format), + "int X.P3.get", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[7].ToDisplayParts(format), + "void X.P3.set", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.PropertyName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[8].ToDisplayParts(format), + "event System.Action X.E", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.DelegateName, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.EventName); + + Verify(members[9].ToDisplayParts(format), + "void X.E.add", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.EventName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + + Verify(members[10].ToDisplayParts(format), + "void X.E.remove", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.EventName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Keyword); + } + + [Fact] + public void TestReadOnlyStruct_Nested() + { + var source = @" +namespace Nested +{ + struct X + { + readonly void M() { } + } +} +"; + var format = SymbolDisplayFormat.TestFormat + .AddMemberOptions(SymbolDisplayMemberOptions.IncludeModifiers) + .AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + var comp = CreateCompilation(source).VerifyDiagnostics(); + var semanticModel = comp.GetSemanticModel(comp.SyntaxTrees.Single()); + + var declaration = (BaseTypeDeclarationSyntax)semanticModel.SyntaxTree.GetRoot().DescendantNodes().Single(n => n.Kind() == SyntaxKind.StructDeclaration); + var members = semanticModel.GetDeclaredSymbol(declaration).GetMembers(); + + Verify(members[0].ToDisplayParts(format), + "readonly void Nested.X.M()", + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.Keyword, + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.NamespaceName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.StructName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.MethodName, + SymbolDisplayPartKind.Punctuation, + SymbolDisplayPartKind.Punctuation); + } + + [Fact] public void TestPassingVBSymbolsToStructSymbolDisplay() { diff --git a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayMemberOptions.cs b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayMemberOptions.cs index 666f67e385313bcb54feae104d835af0523a5a02..255e302c08013b508726a558cb70f4e3b042b0de 100644 --- a/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayMemberOptions.cs +++ b/src/Compilers/Core/Portable/SymbolDisplay/SymbolDisplayMemberOptions.cs @@ -68,6 +68,8 @@ public enum SymbolDisplayMemberOptions /// /// Includes the ref, ref readonly, ByRef keywords for ref-returning methods and properties/indexers. + /// Also includes the readonly keyword on methods, properties/indexers, and events due to the keyword + /// changing the this parameter's ref kind from ref to ref readonly. /// IncludeRef = 1 << 7, } diff --git a/src/EditorFeatures/Test2/IntelliSense/IntellisenseQuickInfoBuilderTests.vb b/src/EditorFeatures/Test2/IntelliSense/IntellisenseQuickInfoBuilderTests.vb index 30aed69908eca3e84293f367cc39bac1bb83aaa5..2e218c5f5e11567cb0b8dbfcc6d134e3aea9fd2d 100644 --- a/src/EditorFeatures/Test2/IntelliSense/IntellisenseQuickInfoBuilderTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/IntellisenseQuickInfoBuilderTests.vb @@ -537,6 +537,145 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.IntelliSense AssertEqualAdornments(expected, container) End Sub + + + Public Async Sub QuickInfoForReadOnlyMethodReference() + Dim workspace = + + + + struct MyStruct { + readonly void MyMethod() { + MyM$$ethod(); + } + } + + + + + Dim codeAnalysisQuickInfoItem = Await GetQuickInfoItemAsync(workspace, LanguageNames.CSharp) + + Dim trackingSpan = New Mock(Of ITrackingSpan) With { + .DefaultValue = DefaultValue.Mock + } + + Dim intellisenseQuickInfo = Await IntellisenseQuickInfoBuilder.BuildItemAsync(trackingSpan.Object, codeAnalysisQuickInfoItem, Nothing, Nothing, CancellationToken.None) + Assert.NotNull(intellisenseQuickInfo) + + Dim container = Assert.IsType(Of ContainerElement)(intellisenseQuickInfo.Item) + + Dim expected = New ContainerElement( + ContainerElementStyle.Stacked Or ContainerElementStyle.VerticalPadding, + New ContainerElement( + ContainerElementStyle.Wrapped, + New ImageElement(New ImageId(KnownImageIds.ImageCatalogGuid, KnownImageIds.MethodPrivate)), + New ClassifiedTextElement( + New ClassifiedTextRun(ClassificationTypeNames.Keyword, "readonly"), + New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + New ClassifiedTextRun(ClassificationTypeNames.Keyword, "void"), + New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + New ClassifiedTextRun(ClassificationTypeNames.StructName, "MyStruct"), + New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "."), + New ClassifiedTextRun(ClassificationTypeNames.MethodName, "MyMethod"), + New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "("), + New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ")")))) + + AssertEqualAdornments(expected, container) + End Sub + + + + Public Async Sub QuickInfoForReadOnlyPropertyReference() + Dim workspace = + + + + struct MyStruct { + readonly int MyProperty => My$$Property; + } + + + + + Dim codeAnalysisQuickInfoItem = Await GetQuickInfoItemAsync(workspace, LanguageNames.CSharp) + + Dim trackingSpan = New Mock(Of ITrackingSpan) With { + .DefaultValue = DefaultValue.Mock + } + + Dim intellisenseQuickInfo = Await IntellisenseQuickInfoBuilder.BuildItemAsync(trackingSpan.Object, codeAnalysisQuickInfoItem, Nothing, Nothing, CancellationToken.None) + Assert.NotNull(intellisenseQuickInfo) + + Dim container = Assert.IsType(Of ContainerElement)(intellisenseQuickInfo.Item) + + Dim expected = New ContainerElement( + ContainerElementStyle.Stacked Or ContainerElementStyle.VerticalPadding, + New ContainerElement( + ContainerElementStyle.Wrapped, + New ImageElement(New ImageId(KnownImageIds.ImageCatalogGuid, KnownImageIds.PropertyPrivate)), + New ClassifiedTextElement( + New ClassifiedTextRun(ClassificationTypeNames.Keyword, "readonly"), + New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + New ClassifiedTextRun(ClassificationTypeNames.Keyword, "int"), + New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + New ClassifiedTextRun(ClassificationTypeNames.StructName, "MyStruct"), + New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "."), + New ClassifiedTextRun(ClassificationTypeNames.PropertyName, "MyProperty"), + New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "{"), + New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + New ClassifiedTextRun(ClassificationTypeNames.Keyword, "get"), + New ClassifiedTextRun(ClassificationTypeNames.Punctuation, ";"), + New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "}")))) + + AssertEqualAdornments(expected, container) + End Sub + + + + Public Async Sub QuickInfoForReadOnlyEventReference() + Dim workspace = + + + + struct MyStruct { + readonly event System.Action MyEvent { add { My$$Event += value; } remove { } } + } + + + + + Dim codeAnalysisQuickInfoItem = Await GetQuickInfoItemAsync(workspace, LanguageNames.CSharp) + + Dim trackingSpan = New Mock(Of ITrackingSpan) With { + .DefaultValue = DefaultValue.Mock + } + + Dim intellisenseQuickInfo = Await IntellisenseQuickInfoBuilder.BuildItemAsync(trackingSpan.Object, codeAnalysisQuickInfoItem, Nothing, Nothing, CancellationToken.None) + Assert.NotNull(intellisenseQuickInfo) + + Dim container = Assert.IsType(Of ContainerElement)(intellisenseQuickInfo.Item) + + Dim expected = New ContainerElement( + ContainerElementStyle.Stacked Or ContainerElementStyle.VerticalPadding, + New ContainerElement( + ContainerElementStyle.Wrapped, + New ImageElement(New ImageId(KnownImageIds.ImageCatalogGuid, KnownImageIds.EventPrivate)), + New ClassifiedTextElement( + New ClassifiedTextRun(ClassificationTypeNames.Keyword, "readonly"), + New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + New ClassifiedTextRun(ClassificationTypeNames.NamespaceName, "System"), + New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "."), + New ClassifiedTextRun(ClassificationTypeNames.DelegateName, "Action"), + New ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, " "), + New ClassifiedTextRun(ClassificationTypeNames.StructName, "MyStruct"), + New ClassifiedTextRun(ClassificationTypeNames.Punctuation, "."), + New ClassifiedTextRun(ClassificationTypeNames.EventName, "MyEvent")))) + + AssertEqualAdornments(expected, container) + End Sub + Public Async Sub QuickInfoForTypeParameterReference()